<?php
if ( ! defined( 'ABSPATH' ) ) { exit; }

class Vulnity_PHP_Upload_Alert extends Vulnity_Alert_Base {
    
    private $processed_files = array(); // Cache para evitar duplicados
    private $deduplication_window = 300;
    
    public function __construct() {
        $this->alert_type = 'php_file_uploaded';
        parent::__construct();
    }
    
    protected function register_hooks() {
        add_action('add_attachment', array($this, 'check_uploaded_file'), 10, 1);
        add_filter('wp_handle_upload', array($this, 'check_file_upload'), 10, 2);
        add_filter('wp_handle_sideload', array($this, 'check_file_upload'), 10, 2);
    }
    
    public function check_uploaded_file($attachment_id) {
        $file = get_attached_file($attachment_id);
        
        if (!$file) {
            return;
        }
        
        $this->evaluate(array(
            'file_path' => $file,
            'attachment_id' => $attachment_id,
            'source' => 'media_library'
        ));
    }
    
    public function check_file_upload($file, $type) {
        if (!empty($file['file'])) {
            $this->evaluate(array(
                'file_path' => $file['file'],
                'source' => 'direct_upload',
                'type' => $file['type'] ?? 'unknown'
            ));
        }
        return $file;
    }
    
    protected function evaluate($data) {
        $file_path = $data['file_path'];
        $filename = basename($file_path);
        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
        
        $upload_dir = wp_upload_dir();
        $is_in_uploads = strpos($file_path, $upload_dir['basedir']) === 0;
        
        if (!$is_in_uploads) {
            return;
        }

        $file_content = @file_get_contents($file_path);
        $is_php_code = false;
        
        if ($file_content !== false) {
            $php_indicators = array('<?php', '<?=', '<script language="php"', '<?', '<%');
            foreach ($php_indicators as $indicator) {
                if (stripos($file_content, $indicator) !== false) {
                    $is_php_code = true;
                    break;
                }
            }
        }

        $risk_profile = $this->classify_uploaded_file($extension, $is_php_code);
        if ($risk_profile === null) {
            return;
        }

        if ($this->is_duplicate_upload_event($file_path, $filename)) {
            return;
        }
        
        $user_info = $this->get_current_user_info();
        
        $alert_data = array(
            'severity' => $risk_profile['severity'],
            'title' => $risk_profile['title'],
            'message' => sprintf(
                '%s "%s" was uploaded to the uploads directory by user "%s" (ID: %d)',
                $risk_profile['label'],
                $filename,
                $user_info['user_login'],
                $user_info['user_id']
            ),
            'details' => array(
                'filename' => $filename,
                'file_path' => $file_path,
                'file_extension' => $extension,
                'file_category' => $risk_profile['category'],
                'file_size' => filesize($file_path),
                'upload_time' => current_time('mysql'),
                'user_id' => $user_info['user_id'],
                'user_login' => $user_info['user_login'],
                'user_email' => $user_info['user_email'],
                'user_ip' => $user_info['user_ip'],
                'contains_php_code' => $is_php_code,
                'risk_assessment' => $risk_profile['risk'],
                'attachment_id' => isset($data['attachment_id']) ? $data['attachment_id'] : null,
                'source' => $data['source'],
                'deduplication_applied' => true
            )
        );
        
        $this->create_alert($alert_data);
        
        vulnity_log('[Vulnity] File upload alert created: ' . $filename . ' [' . $risk_profile['category'] . ']');
    }

    private function classify_uploaded_file($extension, $is_php_code) {
        $critical_extensions = array(
            'php','php3','php4','php5','php6','php7','php8','phtml','phar','pht','phps',
            'asp','aspx','jsp','jspx','do','action','cgi','pl',
            'sh','bash','py','rb','ps1',
            'exe','dll','com','bat','msi','scr','vbs','jse','wsf',
            'jar','war'
        );

        $high_risk_extensions = array('js', 'inc', 'class', 'shtml', 'shtm');
        $medium_risk_extensions = array('bak', 'old', 'backup', 'tmp', 'swp', 'test', 'pem', 'key', 'crt', 'csr', 'bin');
        $low_risk_extensions = array(
            'zip', 'tar', 'gz', 'tgz', 'rar', '7z', 'bz2', 'tbz', 'tbz2', 'xz', 'lzma', 'lz', 'lz4', 'zst', 'zstd',
            'iso', 'img', 'dmg', 'cab', 'apk', 'aar', 'ar', 'cpio', 'ace', 'lha', 'rz', 'pak', 'rpm', 'deb'
        );

        if ($is_php_code || in_array($extension, $critical_extensions, true)) {
            return array(
                'category' => 'executable',
                'severity' => 'critical',
                'title' => 'Executable File Uploaded to Uploads Directory',
                'label' => 'Executable file',
                'risk' => 'Possible server-side code execution from uploads directory'
            );
        }

        if (in_array($extension, $high_risk_extensions, true)) {
            return array(
                'category' => 'potentially_executable',
                'severity' => 'high',
                'title' => 'Potentially Executable File Uploaded to Uploads Directory',
                'label' => 'Potentially executable file',
                'risk' => 'May execute in browser or be interpreted unexpectedly'
            );
        }

        if (in_array($extension, $medium_risk_extensions, true)) {
            return array(
                'category' => 'sensitive_artifact',
                'severity' => 'medium',
                'title' => 'Sensitive File Uploaded to Uploads Directory',
                'label' => 'Sensitive file',
                'risk' => 'Sensitive artifact or backup stored in a public uploads path'
            );
        }

        if (in_array($extension, $low_risk_extensions, true)) {
            return array(
                'category' => 'archive',
                'severity' => 'low',
                'title' => 'Archive Uploaded to Uploads Directory',
                'label' => 'Archive file',
                'risk' => 'Archive in uploads; usually benign but worth visibility'
            );
        }

        return null;
    }

    private function is_duplicate_upload_event($file_path, $filename) {
        $file_fingerprint = $file_path;
        if (file_exists($file_path)) {
            $file_fingerprint .= '|' . filesize($file_path) . '|' . filemtime($file_path);
        }

        $file_hash = md5($file_fingerprint);
        $transient_key = 'vulnity_php_upload_' . $file_hash;
        $current_time = time();

        if (isset($this->processed_files[$file_hash])) {
            $time_diff = $current_time - $this->processed_files[$file_hash];
            if ($time_diff < $this->deduplication_window) {
                vulnity_log('[Vulnity] Skipping duplicate upload detection: ' . $filename . ' (memory cache)');
                return true;
            }
        }

        if (get_transient($transient_key)) {
            vulnity_log('[Vulnity] Skipping duplicate upload detection: ' . $filename . ' (transient cache)');
            return true;
        }

        $this->processed_files[$file_hash] = $current_time;
        set_transient($transient_key, true, $this->deduplication_window);
        $this->clean_processed_files_cache();

        return false;
    }
    
    /**
     * Limpiar caché de archivos procesados
     */
    private function clean_processed_files_cache() {
        $current_time = time();
        $cache_expiry = $this->deduplication_window;
        
        foreach ($this->processed_files as $hash => $timestamp) {
            if (($current_time - $timestamp) > $cache_expiry) {
                unset($this->processed_files[$hash]);
            }
        }
    }
}
