<?php
/**
 * VMPFence Content Pattern Analysis Engine
 * 
 * 
 * 
 * - Advanced regex pattern matching for malicious content
 * - Behavioral analysis of suspicious code patterns
 * - Obfuscated malware detection
 * - Performance-optimized content scanning
 * - Integration with existing threat detection systems
 * 
 * @package VMPFence
 * @version 1.0.0
 */

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

class VMPFence_Content_Pattern_Analysis {
    
    /**
     * Analysis statistics
     */
    private $analysis_stats = [
        'files_analyzed' => 0,
        'content_patterns_found' => 0,
        'high_risk_content' => 0,
        'obfuscated_content' => 0,
        'suspicious_functions' => 0,
        'encoding_patterns' => 0,
        'total_analysis_time' => 0
    ];
    
    /**
     * Debug log
     */
    private $debug_log = [];
    
    /**
     * Maximum file size to analyze (2MB)
     */
    private $max_file_size = 2097152;
    
    /**
     * High-risk PHP functions that should be flagged
     */
    private $high_risk_functions = [];
    
    /**
     * Suspicious encoding patterns
     */
    private $encoding_patterns = [];
    
    /**
     * Obfuscation patterns
     */
    private $obfuscation_patterns = [];
    
    /**
     * Backdoor patterns
     */
    private $backdoor_patterns = [];
    
    /**
     * Malicious iframe/redirect patterns
     */
    private $redirect_patterns = [];
    
    /**
     * Signature loader instance (stored for lazy loading)
     * @since 2.2.4
     */
    private $signature_loader = null;
    
    /**
     * Whether patterns have been loaded from signatures
     * @since 2.2.4
     */
    private $patterns_loaded = false;
    
    /**
     * Constructor
     * 
     * @since 1.0.0
     * @since 2.2.4 Lazy-init: constructor no longer calls get() on signature loader.
     *              Patterns load on first scan method call via ensure_patterns_loaded().
     */
    public function __construct() {
        // Store loader reference only — do NOT call get() here.
        // Patterns will load lazily on first analysis via ensure_patterns_loaded().
        require_once plugin_dir_path(__FILE__) . 'class-signature-loader.php';
        $this->signature_loader = VMPFence_Signature_Loader::get_instance();
        
        $this->debug_log[] = '[Content Pattern Analysis] Engine initialized (patterns deferred)';
    }
    
    /**
     * Ensure patterns are loaded from signature loader (lazy init guard)
     * 
     * Called by scan methods on first use. Dashboard/results AJAX paths that
     * create a Content_Pattern_Analysis instance but never call scan methods
     * will never trigger the full 130 MB signature load.
     *
     * @since 2.2.4
     * @return void
     */
    private function ensure_patterns_loaded() {
        if ($this->patterns_loaded) {
            return;
        }
        $this->patterns_loaded = true;
        
        // Load all pattern arrays from JSON (triggers lazy load in signature loader)
        $this->high_risk_functions = $this->signature_loader->get('high_risk_functions');
        
        // Load regex patterns and add delimiters/flags
        $this->encoding_patterns = $this->add_regex_delimiters($this->signature_loader->get('encoding_patterns'), 'i');
        $this->obfuscation_patterns = $this->add_regex_delimiters($this->signature_loader->get('obfuscation_patterns'), 'i');
        $this->backdoor_patterns = $this->add_regex_delimiters($this->signature_loader->get('backdoor_patterns'));
        $this->redirect_patterns = $this->add_regex_delimiters($this->signature_loader->get('redirect_patterns'), 'i');
        
        $this->debug_log[] = '[Content Pattern Analysis] Patterns loaded from signatures';
    }
    
    /**
     * Add regex delimiters to patterns from JSON
     * 
     * @param array $patterns Array of regex patterns without delimiters
     * @param string $flags Regex flags (i, s, m, etc.)
     * @return array Array of patterns with proper delimiters
     */
    private function add_regex_delimiters($patterns, $flags = '') {
        $result = [];
        foreach ($patterns as $pattern) {
            // Add delimiters if not already present
            if (strpos($pattern, '/') !== 0) {
                $result[] = '/' . $pattern . '/' . $flags;
            } else {
                $result[] = $pattern;
            }
        }
        return $result;
    }
    
    /**
     * Analyze file content for malicious patterns
     * 
     * @since 1.0.0
     * @since 2.2.4 -- Added lazy loading of patterns to improve performance on non-scan paths.
     * @param string $file_path Full file path
     * @param string $relative_path Relative path from WordPress root
     * @return array Analysis result
     */
    public function analyze_file_content($file_path, $relative_path) {
        $start_time = microtime(true);
        $this->analysis_stats['files_analyzed']++;
        
        // Lazy-init: load patterns on first scan call
        $this->ensure_patterns_loaded();
        
        $analysis = [
            'file_path' => $file_path,
            'relative_path' => $relative_path,
            'has_malicious_content' => false,
            'threat_level' => 'none',
            'patterns_found' => [],
            'risk_score' => 0,
            'should_create_issue' => false,
            'analysis_details' => []
        ];
        
        // Check file size
        if (!file_exists($file_path) || !is_readable($file_path)) {
            $analysis['analysis_details'][] = 'File not accessible for content analysis';
            return $analysis;
        }
        
        $file_size = filesize($file_path);
        if ($file_size > $this->max_file_size) {
            $analysis['analysis_details'][] = 'File too large for content analysis (' . size_format($file_size) . ')';
            return $analysis;
        }
        
        if ($file_size === 0) {
            $analysis['analysis_details'][] = 'Empty file - no content to analyze';
            return $analysis;
        }
        
        // Only analyze specific file types
        $file_extension = strtolower(pathinfo($relative_path, PATHINFO_EXTENSION));
        if (!in_array($file_extension, ['php', 'js', 'html', 'htm', 'css', 'txt', 'inc', 'phtml'])) {
            $analysis['analysis_details'][] = 'File type not analyzed (' . $file_extension . ')';
            return $analysis;
        }
        
        // Read file content
        $content = file_get_contents($file_path);
        if ($content === false) {
            $analysis['analysis_details'][] = 'Failed to read file content';
            return $analysis;
        }
        
        // Perform pattern analysis
        $this->analyze_high_risk_functions($content, $analysis);
        $this->analyze_encoding_patterns($content, $analysis);
        $this->analyze_obfuscation_patterns($content, $analysis);
        $this->analyze_backdoor_patterns($content, $analysis);
        $this->analyze_redirect_patterns($content, $analysis);
        
        // Apply combination bonuses
        $this->apply_combination_bonuses($analysis);
        
        // Calculate overall risk score and threat level
        $this->calculate_risk_assessment($analysis);
        
        // Determine if this should create a database issue
        $analysis['should_create_issue'] = $this->should_create_content_issue($analysis);
        
        // Update statistics
        if (count($analysis['patterns_found']) > 0) {
            $this->analysis_stats['content_patterns_found']++;
        }
        
        if ($analysis['threat_level'] === 'high' || $analysis['threat_level'] === 'critical') {
            $this->analysis_stats['high_risk_content']++;
        }
        
        // Add to debug log
        if ($analysis['should_create_issue']) {
            $this->debug_log[] = '[Content Analysis] THREAT: ' . $relative_path . ' (Level: ' . $analysis['threat_level'] . ', Score: ' . $analysis['risk_score'] . ')';
        } elseif (count($analysis['patterns_found']) > 0) {
            $this->debug_log[] = '[Content Analysis] PATTERNS: ' . $relative_path . ' (Level: ' . $analysis['threat_level'] . ', Score: ' . $analysis['risk_score'] . ')';
        }
        
        // Track analysis time
        $analysis_time = microtime(true) - $start_time;
        $this->analysis_stats['total_analysis_time'] += $analysis_time;
        
        return $analysis;
    }
    
    /**
     * Analyze high-risk PHP functions
     */
    private function analyze_high_risk_functions($content, &$analysis) {
        $function_count = 0;
        $suspicious_contexts = [];
        
        foreach ($this->high_risk_functions as $function) {
            // Look for function calls
            $pattern = '/\b' . preg_quote($function) . '\s*\(/i';
            if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
                $function_count += count($matches[0]);
                
                // Check context around each match
                foreach ($matches[0] as $match) {
                    $offset = $match[1];
                    $context = substr($content, max(0, $offset - 50), 100);
                    
                    // Look for suspicious contexts
                    if (preg_match('/\$_(?:POST|GET|REQUEST|COOKIE)\s*\[/', $context)) {
                        $suspicious_contexts[] = "$function with user input";
                        $analysis['risk_score'] += 15; // Higher risk when user input involved
                    } elseif (preg_match('/base64_decode|gzinflate|eval/', $context)) {
                        $suspicious_contexts[] = "$function with obfuscation";
                        $analysis['risk_score'] += 10;
                    } else {
                        $analysis['risk_score'] += 3; // Base risk for high-risk function
                    }
                }
            }
        }
        
        if ($function_count > 0) {
            $analysis['patterns_found'][] = 'High-risk functions: ' . $function_count . ' instances';
            $this->analysis_stats['suspicious_functions'] += $function_count;
            
            if (!empty($suspicious_contexts)) {
                $analysis['analysis_details'][] = 'Suspicious contexts: ' . implode(', ', array_unique($suspicious_contexts));
            }
        }
    }
    
    /**
     * Analyze encoding patterns
     */
    private function analyze_encoding_patterns($content, &$analysis) {
        $encoding_count = 0;
        
        foreach ($this->encoding_patterns as $pattern) {
            if (preg_match_all($pattern, $content, $matches)) {
                $encoding_count += count($matches[0]);
                $analysis['risk_score'] += count($matches[0]) * 1; // Reduced from 1.5 to 1
            }
        }
        
        if ($encoding_count > 0) {
            $analysis['patterns_found'][] = 'Encoding patterns: ' . $encoding_count . ' instances';
            $this->analysis_stats['encoding_patterns'] += $encoding_count;
            
            // High concentration of encoding is suspicious
            if ($encoding_count > 25) { // Increased threshold from 15 to 25
                $analysis['analysis_details'][] = 'High concentration of encoding patterns (' . $encoding_count . ')';
                $analysis['risk_score'] += 10; // Reduced from 15 to 10
            }
        }
    }
    
    /**
     * Analyze obfuscation patterns
     */
    private function analyze_obfuscation_patterns($content, &$analysis) {
        $obfuscation_count = 0;
        $obfuscation_types = [];
        
        foreach ($this->obfuscation_patterns as $pattern) {
            if (preg_match_all($pattern, $content, $matches)) {
                $obfuscation_count += count($matches[0]);
                $analysis['risk_score'] += count($matches[0]) * 35; // Increased from 20 to 35 for critical scoring
                
                // Identify specific obfuscation types
                if (strpos($pattern, 'base64_decode') !== false) {
                    $obfuscation_types[] = 'Base64 obfuscation';
                } elseif (strpos($pattern, 'eval') !== false) {
                    $obfuscation_types[] = 'Dynamic code execution';
                } elseif (strpos($pattern, 'create_function') !== false) {
                    $obfuscation_types[] = 'Dynamic function creation';
                } elseif (strpos($pattern, 'preg_replace') !== false) {
                    $obfuscation_types[] = 'Regex code execution';
                }
            }
        }
        
        if ($obfuscation_count > 0) {
            $analysis['patterns_found'][] = 'Obfuscation patterns: ' . $obfuscation_count . ' instances';
            $this->analysis_stats['obfuscated_content'] += $obfuscation_count;
            
            if (!empty($obfuscation_types)) {
                $analysis['analysis_details'][] = 'Obfuscation types: ' . implode(', ', array_unique($obfuscation_types));
            }
        }
    }
    
    /**
     * Analyze backdoor patterns
     */
    private function analyze_backdoor_patterns($content, &$analysis) {
        $backdoor_count = 0;
        $backdoor_types = [];
        
        foreach ($this->backdoor_patterns as $pattern) {
            if (preg_match_all($pattern, $content, $matches)) {
                $backdoor_count += count($matches[0]);
                $analysis['risk_score'] += count($matches[0]) * 25; // Backdoors are critical
                
                // Identify backdoor types
                if (strpos($pattern, 'cmd|command|exec|system') !== false) {
                    $backdoor_types[] = 'Command execution backdoor';
                } elseif (strpos($pattern, 'pass') !== false) {
                    $backdoor_types[] = 'Password-protected backdoor';
                } elseif (strpos($pattern, 'php://input') !== false) {
                    $backdoor_types[] = 'Raw input backdoor';
                } elseif (strpos($pattern, 'eval') !== false) {
                    $backdoor_types[] = 'Code injection backdoor';
                }
            }
        }
        
        if ($backdoor_count > 0) {
            $analysis['patterns_found'][] = 'Backdoor patterns: ' . $backdoor_count . ' instances';
            
            if (!empty($backdoor_types)) {
                $analysis['analysis_details'][] = 'Backdoor types: ' . implode(', ', array_unique($backdoor_types));
            }
        }
    }
    
    /**
     * Analyze redirect/iframe patterns
     */
    private function analyze_redirect_patterns($content, &$analysis) {
        $redirect_count = 0;
        $redirect_types = [];
        
        foreach ($this->redirect_patterns as $pattern) {
            if (preg_match_all($pattern, $content, $matches)) {
                $redirect_count += count($matches[0]);
                $analysis['risk_score'] += count($matches[0]) * 8; // Moderate risk
                
                // Identify redirect types
                if (strpos($pattern, 'iframe') !== false) {
                    $redirect_types[] = 'Suspicious iframe';
                } elseif (strpos($pattern, 'location.href') !== false) {
                    $redirect_types[] = 'JavaScript redirect';
                } elseif (strpos($pattern, 'window.open') !== false) {
                    $redirect_types[] = 'Popup redirect';
                } elseif (strpos($pattern, 'meta') !== false) {
                    $redirect_types[] = 'Meta refresh redirect';
                }
            }
        }
        
        if ($redirect_count > 0) {
            $analysis['patterns_found'][] = 'Redirect patterns: ' . $redirect_count . ' instances';
            
            if (!empty($redirect_types)) {
                $analysis['analysis_details'][] = 'Redirect types: ' . implode(', ', array_unique($redirect_types));
            }
        }
    }
    
    /**
     * Apply combination bonuses for dangerous pattern combinations
     */
    private function apply_combination_bonuses(&$analysis) {
        $has_high_risk = false;
        $has_obfuscation = false;
        $has_backdoor = false;
        $has_encoding = false;
        
        // Check what patterns were found
        foreach ($analysis['patterns_found'] as $pattern) {
            if (strpos($pattern, 'High-risk functions') !== false) {
                $has_high_risk = true;
            }
            if (strpos($pattern, 'Obfuscation patterns') !== false) {
                $has_obfuscation = true;
            }
            if (strpos($pattern, 'Backdoor patterns') !== false) {
                $has_backdoor = true;
            }
            if (strpos($pattern, 'Encoding patterns') !== false) {
                $has_encoding = true;
            }
        }
        
        // Apply combination bonuses
        if ($has_high_risk && $has_obfuscation) {
            $analysis['risk_score'] += 25; // Critical combination bonus
            $analysis['analysis_details'][] = 'Dangerous combination: obfuscated high-risk functions';
        }
        
        if ($has_backdoor && $has_encoding) {
            $analysis['risk_score'] += 20; // Encoded backdoor bonus
            $analysis['analysis_details'][] = 'Dangerous combination: encoded backdoor patterns';
        }
        
        if ($has_high_risk && $has_backdoor) {
            $analysis['risk_score'] += 15; // High-risk backdoor bonus
            $analysis['analysis_details'][] = 'Dangerous combination: high-risk backdoor functions';
        }
    }

    /**
     * Calculate overall risk assessment
     */
    private function calculate_risk_assessment(&$analysis) {
        $score = $analysis['risk_score'];
        
        // Determine threat level based on risk score
        if ($score >= 100) {
            $analysis['threat_level'] = 'critical';
        } elseif ($score >= 50) {
            $analysis['threat_level'] = 'high';
        } elseif ($score > 20) { // Changed from >= 20 to > 20
            $analysis['threat_level'] = 'medium';
        } elseif ($score > 0) {
            $analysis['threat_level'] = 'low';
        }
        
        // Set malicious content flag
        $analysis['has_malicious_content'] = ($score >= 50);
    }
    
    /**
     * Determine if analysis should create a database issue
     */
    private function should_create_content_issue($analysis) {
        // Only create issues for high-risk content
        return in_array($analysis['threat_level'], ['critical', 'high']) && $analysis['risk_score'] >= 50;
    }
    
    /**
     * Generate issue description for database
     */
    public function generate_content_issue_description($analysis) {
        $base_description = 'Malicious content patterns detected in file';
        
        $details = [];
        if (!empty($analysis['patterns_found'])) {
            $details[] = implode(', ', $analysis['patterns_found']);
        }
        
        if (!empty($analysis['analysis_details'])) {
            $details[] = implode(', ', $analysis['analysis_details']);
        }
        
        $details[] = 'Risk Score: ' . $analysis['risk_score'];
        
        if (!empty($details)) {
            $base_description .= ' | ' . implode(' | ', $details);
        }
        
        return $base_description;
    }
    
    /**
     * Get analysis statistics
     */
    public function get_analysis_stats() {
        // Calculate average analysis time
        if ($this->analysis_stats['files_analyzed'] > 0) {
            $this->analysis_stats['avg_analysis_time'] = round(
                ($this->analysis_stats['total_analysis_time'] / $this->analysis_stats['files_analyzed']) * 1000, 2
            ) . 'ms';
        } else {
            $this->analysis_stats['avg_analysis_time'] = '0ms';
        }
        
        return $this->analysis_stats;
    }
    
    /**
     * Get debug log
     */
    public function get_debug_log() {
        return $this->debug_log;
    }
    
    /**
     * Reset statistics (for new scan)
     */
    public function reset_stats() {
        $this->analysis_stats = [
            'files_analyzed' => 0,
            'content_patterns_found' => 0,
            'high_risk_content' => 0,
            'obfuscated_content' => 0,
            'suspicious_functions' => 0,
            'encoding_patterns' => 0,
            'total_analysis_time' => 0
        ];
        $this->debug_log = [];
    }
}
?>
