<?php
defined('ABSPATH') || exit;

/**
 * WPFort Advanced .htaccess Scanner
 * 
 * This engine scans .htaccess files for malicious patterns from the definitions.
 */

/**
 * Scan a .htaccess file for malicious content
 * 
 * @param string $file_path Path to the .htaccess file
 * @param array $htaccess_definitions Htaccess malicious pattern definitions
 * @return array|null Detection result or null if clean
 */
function wpsec_scan_htaccess_file($file_path, $htaccess_definitions) {
    // Skip files that don't exist
    if (!file_exists($file_path)) {
        wpsec_debug_log("❌ WPFort: .htaccess file doesn't exist: $file_path", 'error');
        return null;
    }
    
    // Check if this is a test file for enhanced logging
    $is_test_file = (stripos($file_path, 'test-advanced-htaccess') !== false || 
                    stripos($file_path, 'test-') !== false);
    $is_deep_scan = get_option('wpsec_deep_scan_mode', false);
    
    if ($is_test_file || $is_deep_scan) {
        wpsec_debug_log("🔍 ADVANCED HTACCESS ENGINE: Starting scan for $file_path", 'info');
    }
    
    // Skip non-.htaccess files
    $filename = basename($file_path);
    $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
    
    // Allow .htaccess files and files with .htaccess extension (for test files)
    if ($filename !== '.htaccess' && $ext !== 'htaccess') {
        if ($is_test_file || $is_deep_scan) {
            wpsec_debug_log("❌ ADVANCED HTACCESS ENGINE: Skipping non-.htaccess file: $file_path (filename: $filename, ext: $ext)", 'error');
        }
        return null;
    }
    
    // For test files, always proceed with scanning
    if ($is_test_file) {
        wpsec_debug_log("🔍 ADVANCED HTACCESS ENGINE: Processing test file: $file_path", 'info');
    }
    
    // Load file content
    $content = file_get_contents($file_path);
    if (!$content) {
        if ($is_test_file || $is_deep_scan) {
            wpsec_debug_log("❌ ADVANCED HTACCESS ENGINE: Empty .htaccess file or couldn't read content: $file_path", 'error');
        }
        return null;
    }
    
    // Calculate file hash
    $file_hash = md5($content);
    
    if ($is_test_file || $is_deep_scan) {
        wpsec_debug_log("🔍 ADVANCED HTACCESS ENGINE: Checking .htaccess file: $file_path (size: " . strlen($content) . " bytes)", 'info');
        wpsec_debug_log("🔍 ADVANCED HTACCESS ENGINE: Checking against " . count($htaccess_definitions) . " htaccess patterns", 'info');
    }
    
    // Check each .htaccess pattern
    foreach ($htaccess_definitions as $pattern_id => $pattern_data) {
        $pattern = $pattern_data[1]; // The regex pattern is typically in this position
        
        if (!$pattern || !is_string($pattern)) {
            if ($is_test_file || $is_deep_scan) {
                wpsec_debug_log("❌ ADVANCED HTACCESS ENGINE: Invalid pattern format for ID: $pattern_id", 'error');
            }
            continue; // Skip invalid patterns
        }
        
        if ($is_test_file || $is_deep_scan) {
            wpsec_debug_log("🔍 ADVANCED HTACCESS ENGINE: Testing pattern '$pattern_id' on .htaccess file", 'info');
        }
        
        // Check if the pattern matches
        $match_result = @preg_match($pattern, $content, $matches);
        if ($match_result === false) {
            if ($is_test_file || $is_deep_scan) {
                wpsec_debug_log("❌ ADVANCED HTACCESS ENGINE: preg_match error for pattern '$pattern_id': " . preg_last_error_msg(), 'error');
            }
            continue;
        }
        
        if ($match_result && !empty($matches[0])) {
            if ($is_test_file || $is_deep_scan) {
                wpsec_debug_log("⚠️ ADVANCED HTACCESS ENGINE: MATCHED pattern '$pattern_id' in .htaccess file: $file_path", 'warning');
                wpsec_debug_log("⚠️ ADVANCED HTACCESS ENGINE: Match content (first 200 chars): " . substr($matches[0], 0, 200), 'warning');
            }
            
            // Determine threat level - .htaccess manipulations are usually high risk
            $threat_score = 4;
            
            // Map to standard detection format
            $detection = wpsec_map_advanced_detection(
                $file_path, 
                'htaccess', 
                $pattern_id, 
                $pattern_data, 
                $matches[0], 
                $threat_score, 
                85, // Higher confidence for .htaccess patterns
                $file_hash // Pass the file hash
            );
            
            if ($is_test_file || $is_deep_scan) {
                wpsec_debug_log("🔍 ADVANCED HTACCESS ENGINE: Detection result for $file_path: " . json_encode($detection), 'info');
            }
            
            return $detection;
        }
    }
    
    if ($is_test_file || $is_deep_scan) {
        wpsec_debug_log("🔍 ADVANCED HTACCESS ENGINE: No matches found for $file_path", 'info');
    }
    
    return null;
}

/**
 * Scan all .htaccess files for malicious content
 * 
 * @param array $scan_queue File scan queue
 * @param array $htaccess_definitions Htaccess malicious pattern definitions
 * @return array Array of detection results
 */
/**
 * Detect if the server is running Nginx
 * 
 * @return bool True if Nginx is detected, false otherwise
 */
function wpsec_is_nginx_server() {
    // Store server detection results as a transient to avoid repeated checks
    $cached_result = get_transient('wpsec_is_nginx_server');
    if ($cached_result !== false) {
        return $cached_result === 'yes';
    }
    
    // First, explicitly check for Apache identifiers (most reliable method)
    if (isset($_SERVER['SERVER_SOFTWARE']) && 
        (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false || 
         stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false)) {
        // Definitely Apache - cache result and return false
        set_transient('wpsec_is_nginx_server', 'no', DAY_IN_SECONDS);
        wpsec_debug_log('[INFO] WPFort: Apache server positively identified via SERVER_SOFTWARE: ' . $_SERVER['SERVER_SOFTWARE'], 'info');
        return false;
    }
    
    // Apache-specific environment variables
    if (function_exists('apache_get_version') || 
        isset($_SERVER['DOCUMENT_ROOT']) && stripos($_SERVER['DOCUMENT_ROOT'], 'apache') !== false ||
        isset($_SERVER['mod_fcgid']) || 
        isset($_SERVER['SCRIPT_FILENAME']) && isset($_SERVER['PHP_SELF'])) {
        set_transient('wpsec_is_nginx_server', 'no', DAY_IN_SECONDS);
        wpsec_debug_log('[INFO] WPFort: Apache server identified via Apache-specific variables', 'info');
        return false;
    }
    
    // Check for Apache or mod_rewrite modules 
    if (function_exists('apache_get_modules')) {
        set_transient('wpsec_is_nginx_server', 'no', DAY_IN_SECONDS);
        wpsec_debug_log('[INFO] WPFort: Apache server identified via apache_get_modules function', 'info');
        return false;
    }
    
    // Now check for Nginx identifiers
    // Method 1: Check Server header for Nginx
    if (isset($_SERVER['SERVER_SOFTWARE']) && stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
        set_transient('wpsec_is_nginx_server', 'yes', DAY_IN_SECONDS);
        wpsec_debug_log('[INFO] WPFort: Nginx server identified via SERVER_SOFTWARE: ' . $_SERVER['SERVER_SOFTWARE'], 'info');
        return true;
    }
    
    // Method 2: Look for Nginx-specific environment variables
    if (isset($_SERVER['HTTP_X_NGINX']) || isset($_SERVER['nginx_version'])) {
        set_transient('wpsec_is_nginx_server', 'yes', DAY_IN_SECONDS);
        wpsec_debug_log('[INFO] WPFort: Nginx server identified via Nginx-specific variables', 'info');
        return true;
    }
    
    // Method 3: Try for a more reliable check - does .htaccess actually work?
    // The presence of working Apache rewrite rules is a very good indicator
    global $wp_rewrite;
    if ($wp_rewrite && $wp_rewrite->using_mod_rewrite_permalinks()) {
        set_transient('wpsec_is_nginx_server', 'no', DAY_IN_SECONDS);
        wpsec_debug_log('[INFO] WPFort: Apache server identified via working mod_rewrite permalinks', 'info');
        return false;
    }
    
    // If we reach this point, we couldn't positively identify Apache
    // So we should default to not flagging .htaccess files as problems
    wpsec_debug_log('[INFO] WPFort: Could not positively identify server type - defaulting to Apache-compatible', 'info');
    set_transient('wpsec_is_nginx_server', 'no', DAY_IN_SECONDS);
    return false;
}

/**
 * Create a detection result for inappropriate .htaccess file
 * 
 * @param string $file_path Path to the htaccess file
 * @return array Detection result
 */
/**
 * Check for suspicious patterns in htaccess content
 *
 * @param string $content The htaccess file content
 * @return array|null Detection details if suspicious, null if clean
 */
function wpsec_check_suspicious_htaccess_content($content, $file_hash = null) {
    // Define suspicious patterns to look for
    $suspicious_patterns = [
        'deny_php_execution' => [
            'pattern' => '/<FilesMatch.*\.(php|phtml|php5|suspected).*>\s*.*Deny from all/is',
            'description' => 'Suspicious rule denying access to PHP files - may indicate an attempt to hide malware',
            'severity' => 'critical'
        ],
        'deny_wp_admin_access' => [
            'pattern' => '/<FilesMatch.*\.(php).*>\s*.*Allow from.*\s*.*<\/FilesMatch>/is',
            'description' => 'Suspicious rule controlling access to PHP files with explicit allow list',
            'severity' => 'high'
        ],
        'suspicious_redirect' => [
            'pattern' => '/RewriteRule.*\.(php|html|htm).*\[R=301.*\]/i',
            'description' => 'Suspicious redirect rule that may be used for traffic hijacking',
            'severity' => 'high'
        ],
        'base64_content' => [
            'pattern' => '/base64_decode|eval\s*\(/i',
            'description' => 'Potentially malicious code execution in htaccess using base64 or eval',
            'severity' => 'critical'
        ],
        'php_execution' => [
            'pattern' => '/SetHandler\s+application\/x-httpd-php/i',
            'description' => 'Htaccess file attempting to execute PHP in non-PHP files',
            'severity' => 'critical'
        ],
        'environment_variable_tampering' => [
            'pattern' => '/SetEnv\s+HTTP_|SetEnv\s+PHP_/i',
            'description' => 'Suspicious environment variable manipulation',
            'severity' => 'high'
        ]
    ];
    
    // Check each pattern
    foreach ($suspicious_patterns as $id => $pattern_data) {
        if (preg_match($pattern_data['pattern'], $content, $matches)) {
            $line_number = 1;
            // Try to determine line number by counting newlines before the match position
            if (!empty($matches[0])) {
                $pos = strpos($content, $matches[0]);
                if ($pos !== false) {
                    $line_number = substr_count(substr($content, 0, $pos), "\n") + 1;
                }
            }
            
            // Extract a meaningful snippet around the match
            $snippet = isset($matches[0]) ? $matches[0] : substr($content, 0, 100) . (strlen($content) > 100 ? '...' : '');
            if (strlen($snippet) > 200) {
                $snippet = substr($snippet, 0, 200) . '...';
            }
            
            return [
                'pattern_id' => $id,
                'description' => $pattern_data['description'],
                'severity' => $pattern_data['severity'],
                'line' => $line_number,
                'snippet' => $snippet,
                'file_hash' => $file_hash
            ];
        }
    }
    
    return null;
}

/**
 * Enhance an existing detection with additional context and ensure threat score is 5
 * 
 * @param array $existing_detection The existing detection to enhance
 * @param array $additional_info Additional information to include
 * @return array The enhanced detection
 */
function wpsec_enhance_htaccess_detection($existing_detection, $additional_info) {
    // Make sure we always have a threat score of 5
    $existing_detection['threat_score'] = 5;
    
    // If we have an existing detection, append the additional info to the description
    if (isset($existing_detection['description'])) {
        $existing_detection['description'] .= '. ' . $additional_info['description'];
    }
    
    // If there's a name from advanced definitions, keep that but append our classification
    if (isset($existing_detection['name']) && is_string($existing_detection['name']) && 
        !empty($additional_info['name']) && strpos($existing_detection['name'], $additional_info['name']) === false) {
        $existing_detection['name'] .= '_' . $additional_info['name'];
    }
    
    // Add additional detection information if we have detections
    if (isset($existing_detection['detections']) && is_array($existing_detection['detections'])) {
        // Add our detection to the existing detections array
        $existing_detection['detections'][] = [
            'line' => $additional_info['line'] ?? 1,
            'pattern' => $additional_info['pattern'] ?? 'additional_context',
            'snippet' => $additional_info['snippet'] ?? '',
            'category' => 'suspicious_file',
            'severity' => $additional_info['severity'] ?? 'high'
        ];
    }
    
    return $existing_detection;
}

function wpsec_create_inappropriate_htaccess_detection($file_path, $is_nginx = false) {
    // Read the file content for reference
    $content = file_get_contents($file_path);
    $snippet = substr($content, 0, 100) . (strlen($content) > 100 ? '...' : '');
    
    // Calculate file hash
    $file_hash = md5($content);
    
    // Check for suspicious content first
    $suspicious_content = wpsec_check_suspicious_htaccess_content($content);
    
    // Name and description based on whether it's Nginx or suspicious content
    $name = $is_nginx ? 'Inappropriate_Htaccess_On_Nginx' : 'Suspicious_Htaccess_Content';
    $description = $is_nginx ? 
        'Found .htaccess file on Nginx server where it has no effect but may indicate hack attempt' : 
        'Suspicious content detected in .htaccess file that may indicate a security issue';
    
    // If suspicious content was found, use that info
    if ($suspicious_content) {
        $name = 'Malicious_Htaccess_Content';
        $description = $suspicious_content['description'];
        $snippet = $suspicious_content['snippet'];
        $severity = $suspicious_content['severity'];
        $line_number = $suspicious_content['line'];
    } else {
        $severity = $is_nginx ? 'high' : 'medium';
        $line_number = 1;
    }
    
    // Create a standardized detection
    return [
        'file_path' => $file_path,
        'name' => $name,
        'description' => $description,
        'detections' => [
            [
                'type' => 'htaccess',
                'line' => $line_number,
                'pattern' => $suspicious_content ? $suspicious_content['pattern_id'] : ($is_nginx ? '.htaccess on Nginx' : 'suspicious_htaccess'),
                'snippet' => $snippet,
                'category' => 'suspicious_file',
                'severity' => $severity,
                'confidence' => $suspicious_content ? 85 : 80,
                'file_hash' => $file_hash
            ]
        ],
        'threat_score' => 5, // Always use threat score 5
        'confidence' => $suspicious_content ? 95 : 90,
        'context' => [
            'type' => 'server_config',
            'is_core' => false,
            'is_plugin' => false,
            'is_theme' => false,
            'is_upload' => false,
            'risk_level' => 'high'
        ]
    ];
}

function wpsec_scan_htaccess_files($scan_queue, $htaccess_definitions) {
    $results = [];
    
    if (!$htaccess_definitions || !is_array($htaccess_definitions)) {
        wpsec_debug_log('[ERROR] WPFort: No .htaccess definitions available for scanning', 'error');
        return $results;
    }
    
    wpsec_debug_log('[INFO] WPFort: Scanning .htaccess files with ' . count($htaccess_definitions) . ' definitions', 'info');
    
    $htaccess_files = [];
    
    // First, filter only .htaccess files from the scan queue for efficiency
    foreach ($scan_queue as $file_path => $priority) {
        if (basename($file_path) === '.htaccess') {
            $htaccess_files[$file_path] = $priority;
        }
    }
    
    wpsec_debug_log('[INFO] WPFort: Found ' . count($htaccess_files) . ' .htaccess files to scan', 'info');
    
    // Check if we're on an Nginx server
    $is_nginx = wpsec_is_nginx_server();
    if ($is_nginx) {
        wpsec_debug_log('[INFO] WPFort: Nginx server detected - .htaccess files are not used by Nginx and may indicate a security issue', 'info');
    }
    
    $matched = 0;
    
    // Now scan each .htaccess file
    foreach ($htaccess_files as $file_path => $priority) {
        $has_detection = false;
        $file_result = null;
        
        // First check for malicious patterns from definitions
        $result = wpsec_scan_htaccess_file($file_path, $htaccess_definitions);
        
        if ($result) {
            // Found a detection from advanced definitions
            // Ensure threat score is 5
            $result['threat_score'] = 5;
            $file_result = $result;
            $has_detection = true;
            $matched++;
            wpsec_debug_log('[WARNING] WPFort: Matched advanced definition pattern in .htaccess file: ' . $file_path, 'warning');
        }
        
        // Read the content for checking suspicious patterns
        $content = file_get_contents($file_path);
        if (!$content) {
            if ($has_detection) {
                $results[] = $file_result;
            }
            continue; // Skip empty or unreadable files
        }
        
        // Calculate file hash once
        $file_hash = md5($content);
        
        // Check for suspicious content patterns regardless of server type
        $suspicious_content = wpsec_check_suspicious_htaccess_content($content, $file_hash);
        
        if ($suspicious_content) {
            // Found suspicious content - either enhance existing detection or create new one
            if ($has_detection) {
                // Enhance the existing detection with our suspicious content info
                $additional_info = [
                    'description' => $suspicious_content['description'],
                    'pattern' => $suspicious_content['pattern_id'],
                    'snippet' => $suspicious_content['snippet'],
                    'severity' => $suspicious_content['severity'],
                    'line' => $suspicious_content['line'],
                    'name' => 'Malicious_Htaccess_Content'
                ];
                $file_result = wpsec_enhance_htaccess_detection($file_result, $additional_info);
            } else {
                // Create a new detection
                $file_result = wpsec_create_inappropriate_htaccess_detection($file_path, false);
                $has_detection = true;
                $matched++;
            }
            wpsec_debug_log('[WARNING] WPFort: Detected suspicious content in .htaccess file: ' . $file_path, 'warning');
        }
        
        // If we're on Nginx, flag all .htaccess files as suspicious
        // This will either enhance an existing detection or create a new one
        if ($is_nginx) {
            if ($has_detection) {
                // Enhance the existing detection with Nginx-specific info
                $additional_info = [
                    'description' => 'Found .htaccess file on Nginx server where it has no effect but may indicate hack attempt',
                    'pattern' => '.htaccess on Nginx',
                    'snippet' => substr($content, 0, 100) . (strlen($content) > 100 ? '...' : ''),
                    'severity' => 'high',
                    'line' => 1,
                    'name' => 'Inappropriate_Htaccess_On_Nginx'
                ];
                $file_result = wpsec_enhance_htaccess_detection($file_result, $additional_info);
            } else {
                // Create a new detection
                $file_result = wpsec_create_inappropriate_htaccess_detection($file_path, true);
                $has_detection = true;
                $matched++;
            }
            wpsec_debug_log('[WARNING] WPFort: Flagged inappropriate .htaccess file on Nginx server: ' . $file_path, 'warning');
        }
        
        // Add the result to our collection if we have one
        if ($has_detection) {
            $results[] = $file_result;
        }
    }
    
    wpsec_debug_log(sprintf(
        '[INFO] WPFort: .htaccess scan complete - %d files scanned, %d matches found',
        count($htaccess_files),
        $matched
    ), 'info');
    
    return $results;
}
