user_status, array( 0 ), true ) ) { return false; } // Verify capability $has_capability = user_can( $user, $capability ); if ( ! $has_capability ) { // Log failed capability attempt self::powerstack_log_security_event( 'failed_capability', array( 'capability' => $capability, 'user_id' => $user_id, 'user_ip' => self::powerstack_get_user_ip(), ) ); } return $has_capability; } /** * Rate limiting for API requests * @param string $identifier * @param int $max_requests * @param int $time_window * @return bool|WP_Error */ public static function powerstack_rate_limit( $identifier, $max_requests = 30, $time_window = 60 ) { $current_time = time(); $window_start = $current_time - $time_window; // Clean old requests if ( ! isset( self::$rate_limits[$identifier] ) ) { self::$rate_limits[$identifier] = array(); } self::$rate_limits[$identifier] = array_filter( self::$rate_limits[$identifier], function( $timestamp ) use ( $window_start ) { return $timestamp > $window_start; } ); // Check if limit exceeded if ( count( self::$rate_limits[$identifier] ) >= $max_requests ) { self::powerstack_log_security_event( 'rate_limit_exceeded', array( 'identifier' => $identifier, 'requests' => count( self::$rate_limits[$identifier] ), 'limit' => $max_requests, 'window' => $time_window, ) ); return new WP_Error( 'rate_limit_exceeded', __( 'Rate limit exceeded. Please try again later.', 'powerstack-admin-search' ), array( 'status' => 429 ) ); } // Add current request self::$rate_limits[$identifier][] = $current_time; return true; } /** * Input sanitization with enhanced validation * @param mixed $input * @param string $type * @return mixed */ public static function powerstack_sanitize_input( $input, $type = 'text' ) { if ( is_array( $input ) ) { return array_map( function( $item ) use ( $type ) { return self::powerstack_sanitize_input( $item, $type ); }, $input ); } switch ( $type ) { case 'text': return sanitize_text_field( $input ); case 'textarea': return sanitize_textarea_field( $input ); case 'url': return esc_url_raw( $input ); case 'email': return sanitize_email( $input ); case 'integer': return absint( $input ); case 'float': return floatval( $input ); case 'alpha': return preg_replace( '/[^a-zA-Z]/', '', $input ); case 'alphanumeric': return preg_replace( '/[^a-zA-Z0-9]/', '', $input ); case 'search_query': // Enhanced search query sanitization $query = sanitize_text_field( $input ); // Remove potentially harmful characters $query = preg_replace( '/[<>"\']/', '', $query ); // Limit length return substr( $query, 0, 100 ); default: return sanitize_text_field( $input ); } } /** * Output escaping with context awareness * @param string $output * @param string $context * @return string */ public static function powerstack_escape_output( $output, $context = 'html' ) { switch ( $context ) { case 'html': return esc_html( $output ); case 'attr': return esc_attr( $output ); case 'url': return esc_url( $output ); case 'js': return esc_js( $output ); case 'sql': global $wpdb; return $wpdb->_real_escape( $output ); default: return esc_html( $output ); } } /** * Get user IP safely * @return string */ private static function powerstack_get_user_ip() { $ip_headers = array( 'HTTP_CF_CONNECTING_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR', ); foreach ( $ip_headers as $header ) { if ( ! empty( $_SERVER[$header] ) ) { $ips = explode( ',', sanitize_text_field( wp_unslash( $_SERVER[$header] ) ) ); $ip = trim( $ips[0] ); if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) { return $ip; } } } return '127.0.0.1'; } /** * Log security events * @param string $event_type * @param array $data */ private static function powerstack_log_security_event( $event_type, $data = array() ) { if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { return; } $log_entry = array( 'timestamp' => current_time( 'mysql' ), 'event' => $event_type, 'data' => $data, ); // Debug logging removed for production } /** * Add security headers */ public function powerstack_add_security_headers() { // Only add on admin pages where our plugin loads if ( ! current_user_can( 'manage_options' ) ) { return; } // Add CSP header for our assets $plugin_url = POWERSTACK_ADMIN_SEARCH_URL; $csp_header = "default-src 'self'; script-src 'self' 'unsafe-inline' {$plugin_url}; style-src 'self' 'unsafe-inline' {$plugin_url}; img-src 'self' data:; font-src 'self' data:;"; header( "Content-Security-Policy: {$csp_header}" ); header( 'X-Content-Type-Options: nosniff' ); header( 'X-Frame-Options: SAMEORIGIN' ); header( 'Referrer-Policy: strict-origin-when-cross-origin' ); } /** * Cleanup old rate limit data */ public function powerstack_cleanup_rate_limits() { $cutoff_time = time() - 3600; // Keep 1 hour of data foreach ( self::$rate_limits as $identifier => $timestamps ) { self::$rate_limits[$identifier] = array_filter( $timestamps, function( $timestamp ) use ( $cutoff_time ) { return $timestamp > $cutoff_time; } ); // Remove empty entries if ( empty( self::$rate_limits[$identifier] ) ) { unset( self::$rate_limits[$identifier] ); } } } }