<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;

class CommandInjectionProtection
{
    /**
     * Command injection patterns to detect
     */
    protected $commandPatterns = [
        // Basic command separators
        '/[;&|`$(){}[\]<>]/',
        '/\|\|/',
        '/&&/',
        '/\$\(/',
        '/`[^`]*`/',
        
        // Common Unix commands
        '/\b(cat|ls|pwd|whoami|id|uname|ps|netstat|ifconfig|ping|nslookup|dig|wget|curl|nc|telnet|ssh|ftp|scp|rsync)\b/i',
        '/\b(rm|mv|cp|chmod|chown|kill|killall|pkill|sudo|su|passwd|useradd|userdel|usermod|groupadd|groupdel)\b/i',
        '/\b(find|grep|awk|sed|sort|uniq|head|tail|wc|tr|cut|xargs|which|whereis|locate|updatedb)\b/i',
        '/\b(mount|umount|df|du|fdisk|mkfs|fsck|lsof|strace|ltrace|gdb|objdump|strings|hexdump)\b/i',
        '/\b(crontab|at|batch|nohup|screen|tmux|jobs|bg|fg|disown|history|alias|unalias|export|env|set|unset)\b/i',
        
        // Windows commands
        '/\b(dir|type|copy|move|del|erase|md|mkdir|rd|rmdir|cd|chdir|cls|echo|set|path|prompt|title)\b/i',
        '/\b(net|netsh|ipconfig|ping|nslookup|tracert|route|arp|telnet|ftp|tftp|rcp|rsh|finger|runas)\b/i',
        '/\b(tasklist|taskkill|sc|reg|regedit|msconfig|services|eventvwr|perfmon|resmon|dxdiag|systeminfo)\b/i',
        '/\b(format|chkdsk|defrag|sfc|dism|bcdedit|bootrec|diskpart|wmic|powershell|cmd|command)\b/i',
        '/\b(at|schtasks|shutdown|restart|logoff|lock|gpupdate|gpresult|whoami|hostname|ver|vol|label)\b/i',
        
        // Scripting and programming
        '/\b(python|python3|perl|ruby|php|node|java|javac|gcc|g\+\+|make|cmake|git|svn|hg|bzr)\b/i',
        '/\b(vim|vi|nano|emacs|gedit|notepad|edit|edlin|debug|gdb|lldb|strace|ltrace)\b/i',
        '/\b(bash|sh|zsh|csh|tcsh|ksh|fish|dash|ash|busybox|exec|eval|source|\.)\b/i',
        
        // Network and system tools
        '/\b(nmap|masscan|zmap|unicornscan|hping|scapy|metasploit|msfconsole|msfvenom|sqlmap|nikto|dirb|gobuster)\b/i',
        '/\b(hydra|john|hashcat|aircrack|wireshark|tcpdump|ettercap|dsniff|snort|nessus|openvas|burp)\b/i',
        '/\b(nc|netcat|socat|stunnel|proxychains|tor|privoxy|squid|apache|nginx|mysql|postgresql|mongodb)\b/i',
        
        // File operations
        '/\b(tar|gzip|gunzip|zip|unzip|rar|unrar|7z|bzip2|bunzip2|xz|unxz|compress|uncompress)\b/i',
        '/\b(dd|split|csplit|shred|wipe|srm|base64|uuencode|uudecode|xxd|od|hexdump|strings)\b/i',
        
        // Process and system control
        '/\b(init|systemctl|service|chkconfig|update-rc\.d|insmod|rmmod|modprobe|lsmod|dmesg|sysctl)\b/i',
        '/\b(iptables|ip6tables|ufw|firewall-cmd|semanage|setsebool|getsebool|sestatus|getenforce|setenforce)\b/i',
        
        // Special characters and encodings
        '/\$\{[^}]*\}/',
        '/\$\([^)]*\)/',
        '/\$\[[^\]]*\]/',
        '/\$\<[^>]*\>/',
        '/\%[0-9a-fA-F]{2}/',
        '/\\x[0-9a-fA-F]{2}/',
        '/\\[0-7]{3}/',
        '/\\u[0-9a-fA-F]{4}/',
        '/\\U[0-9a-fA-F]{8}/',
        
        // Command substitution
        '/\$\(.*\)/',
        '/`.*`/',
        '/\${.*}/',
        
        // Redirection operators
        '/[<>]/',
        '/>>/',
        '/<</',
        '/2>&1/',
        '/>&/',
        '/\|&/',
        
        // Environment variables
        '/\$PATH/',
        '/\$HOME/',
        '/\$USER/',
        '/\$SHELL/',
        '/\$PWD/',
        '/\$OLDPWD/',
        '/\$PS1/',
        '/\$IFS/',
        
        // Dangerous functions
        '/\beval\s*\(/',
        '/\bexec\s*\(/',
        '/\bsystem\s*\(/',
        '/\bshell_exec\s*\(/',
        '/\bpassthru\s*\(/',
        '/\bpopen\s*\(/',
        '/\bproc_open\s*\(/',
        '/\bfile_get_contents\s*\(/',
        '/\bfile_put_contents\s*\(/',
        '/\bfopen\s*\(/',
        '/\bfwrite\s*\(/',
        '/\bfputs\s*\(/',
        '/\binclude\s*\(/',
        '/\brequire\s*\(/',
        '/\binclude_once\s*\(/',
        '/\brequire_once\s*\(/',
    ];

    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next): Response
    {
        // Check for command injection in all input data
        $this->checkForCommandInjection($request);

        return $next($request);
    }

    /**
     * Check request data for command injection patterns
     */
    protected function checkForCommandInjection(Request $request): void
    {
        // Check GET parameters
        $this->checkData($request->query->all(), $request, 'GET');

        // Check POST data
        $this->checkData($request->request->all(), $request, 'POST');

        // Check JSON data
        if ($request->isJson()) {
            $this->checkData($request->json()->all(), $request, 'JSON');
        }

        // Check headers
        $this->checkHeaders($request);

        // Check URL path
        $this->checkUrlPath($request);
    }

    /**
     * Check data array for command injection patterns
     */
    protected function checkData(array $data, Request $request, string $type): void
    {
        array_walk_recursive($data, function ($value, $key) use ($request, $type) {
            if (is_string($value) && $this->containsCommandInjection($value)) {
                $this->logCommandInjectionAttempt($request, $type, $key, $value);
                abort(403, 'Command injection attempt detected');
            }
        });
    }

    /**
     * Check headers for command injection patterns
     */
    protected function checkHeaders(Request $request): void
    {
        $suspiciousHeaders = [
            'User-Agent',
            'Referer',
            'X-Forwarded-For',
            'X-Real-IP',
            'X-Originating-IP',
            'X-Remote-IP',
            'X-Remote-Addr',
            'X-Client-IP'
        ];

        foreach ($suspiciousHeaders as $header) {
            if ($request->hasHeader($header)) {
                $value = $request->header($header);
                if ($this->containsCommandInjection($value)) {
                    $this->logCommandInjectionAttempt($request, 'HEADER', $header, $value);
                    abort(403, 'Command injection attempt detected in headers');
                }
            }
        }
    }

    /**
     * Check URL path for command injection patterns
     */
    protected function checkUrlPath(Request $request): void
    {
        $path = $request->getPathInfo();
        if ($this->containsCommandInjection($path)) {
            $this->logCommandInjectionAttempt($request, 'URL', 'path', $path);
            abort(403, 'Command injection attempt detected in URL');
        }
    }

    /**
     * Check if string contains command injection patterns
     */
    protected function containsCommandInjection(string $value): bool
    {
        // Skip check for very short strings
        if (strlen($value) < 2) {
            return false;
        }

        // URL decode the value to catch encoded attacks
        $decodedValue = urldecode($value);
        
        // Check for null bytes
        if (strpos($decodedValue, "\0") !== false) {
            return true;
        }
        
        foreach ($this->commandPatterns as $pattern) {
            if (preg_match($pattern, $decodedValue)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Log command injection attempt
     */
    protected function logCommandInjectionAttempt(Request $request, string $type, string $field, string $value): void
    {
        Log::warning('Command injection attempt detected', [
            'ip' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'url' => $request->fullUrl(),
            'method' => $request->method(),
            'type' => $type,
            'field' => $field,
            'value' => substr($value, 0, 200), // Limit logged value length
            'referer' => $request->header('Referer'),
            'timestamp' => now()->toISOString(),
        ]);
    }
}
