<?php
webscan_error();
require_once('webscan_cache.php');

$webscan_referer = empty($_SERVER['HTTP_REFERER']) ? array() : array('HTTP_REFERER'=>$_SERVER['HTTP_REFERER']);

function webscan_error() {
    if (ini_get('display_errors')) {
        ini_set('display_errors', '0');
    }
}

function webscan_slog($logs) {
    return true;
}

function webscan_flatten($arr, &$result = []) {
    if (!is_array($arr)) {
        $result[] = (string)$arr;
        return $result;
    }
    foreach ($arr as $val) {
        webscan_flatten($val, $result);
    }
    return $result;
}

function webscan_is_safe($str) {
    if (preg_match('/^[\p{L}\p{N}\s\-\_\.\,\!\?\&\=\+\*\/\@\#\$\%\^\*\（\）\【\】\、\；\:\"\'\<\> ]+$/u', $str)) {
        return true;
    }
    return false;
}

function webscan_deep_decode($str) {
    $decoded = $str;
    for ($i = 0; $i < 3; $i++) {
        $temp = urldecode($decoded);
        if ($temp === $decoded) break;
        $decoded = $temp;
    }
    return $decoded;
}

function webscan_contains_phpcode($str) {
    $patterns = [
        '/<\?php/i',
        '/\b(eval|assert|system|shell_exec|passthru|popen|proc_open|exec)\s*\(/i',
        '/\b(file_put_contents|fwrite|fopen|unlink|rmdir|mkdir|rename|copy|chmod)\s*\(/i',
        '/`.*?\$.*?`/i',
        '/\$\{.*?\}/i'
    ];
    foreach ($patterns as $p) {
        if (preg_match($p, $str)) return true;
    }
    return false;
}

function webscan_contains_xss($str) {
    $patterns = [
        '/<script[^>]*>[\s\S]*?<\/script>/i',
        '/javascript\s*:/i',
        '/vbscript\s*:/i',
        '/on(load|error|click|mouseover|focus|submit|change)\s*=/i',
        '/<\s*\/?\s*iframe/i',
        '/<\s*\/?\s*object/i',
        '/<\s*\/?\s*embed/i',
        '/expression\s*\(/i',
        '/&#x?[0-9a-f]{2,};/i'
    ];
    foreach ($patterns as $p) {
        if (preg_match($p, $str)) return true;
    }
    return false;
}

function webscan_contains_sqli($str) {
    $patterns = [
        '/union\s+select/i',
        '/\b(or|and)\s+\d+\s*=\s*\d+/i',
        '/\b(sleep|benchmark|concat|substr|user|database|version)\(/i',
        '/\b(load_file|into\s+outfile|into\s+dumpfile)/i',
        '/\b(0x[0-9a-f]+)\b/i'
    ];
    foreach ($patterns as $p) {
        if (preg_match($p, $str)) return true;
    }
    return false;
}

function webscan_should_skip_by_param($key) {
    $safe_keys = ['content', 'body', 'text', 'comment', 'description', 'message', 'detail', 'summary', 'intro', 'answer', 'question', 'reply', 'feedback', 'bio', 'signature'];
    $key_lower = strtolower($key);
    foreach ($safe_keys as $sk) {
        if (strpos($key_lower, $sk) !== false) return true;
    }
    return false;
}

function webscan_check_payload($value, $key, $method) {
    $raw = $value;
    if (webscan_is_safe($raw)) return false;
    $decoded = webscan_deep_decode($raw);
    if ($method === 'GET' || $method === 'COOKIE' || $method === 'REFERRER') {
        if (webscan_contains_phpcode($decoded)) return true;
        if (webscan_contains_xss($decoded)) return true;
        if (webscan_contains_sqli($decoded)) return true;
    } elseif ($method === 'POST') {
        if (webscan_should_skip_by_param($key)) {
            return webscan_contains_phpcode($decoded);
        } else {
            if (webscan_contains_phpcode($decoded)) return true;
            if (webscan_contains_xss($decoded)) return true;
            if (webscan_contains_sqli($decoded)) return true;
        }
    }
    return false;
}

function webscan_StopAttack($StrFiltKey, $StrFiltValue, $method) {
    $values = webscan_flatten($StrFiltValue);
    foreach ($values as $val) {
        if (strlen($val) < 6 && preg_match('/^[a-zA-Z0-9_\-\.]+$/', $val)) continue;
        if (webscan_check_payload($val, $StrFiltKey, $method)) {
            webscan_slog(array(
                'ip' => $_SERVER["REMOTE_ADDR"],
                'time' => date('Y-m-d H:i:s'),
                'page' => $_SERVER["PHP_SELF"],
                'method' => $method,
                'rkey' => $StrFiltKey,
                'rdata' => $StrFiltValue,
                'user_agent' => $_SERVER['HTTP_USER_AGENT'],
                'request_url' => $_SERVER["REQUEST_URI"]
            ));
            webscan_pape();
        }
    }
}

function webscan_white($webscan_white_name, $webscan_white_url = array()) {
    $url_path = $_SERVER['SCRIPT_NAME'];
    $url_var = $_SERVER['QUERY_STRING'];
    if(empty($url_var) && ($url_path == '/' || $url_path == '/index.php')){
        return false;
    }
    if (!empty($webscan_white_name) && strpos($url_path, $webscan_white_name) !== false) {
        return false;
    }
    foreach ($webscan_white_url as $key => $value) {
        if (!empty($url_var) && !empty($value)) {
            if (strpos($url_path, $key) !== false && strpos($url_var, $value) !== false) {
                return false;
            }
        } elseif (empty($url_var) && empty($value)) {
            if (strpos($url_path, $key) !== false) {
                return false;
            }
        }
    }
    return true;
}

function webscan_pape() {
    $lang = 'en';
    $acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '';
    if (!empty($acceptLang) && strpos(strtolower($acceptLang), 'zh') === 0) {
        $lang = 'zh';
    }

    if ($lang === 'zh') {
        $title = '请求被拦截';
        $message = '您的请求包含危险字符，已被系统安全机制拦截。';
        $backText = '返回上一页';
        $homeText = '返回首页';
    } else {
        $title = 'Request Blocked';
        $message = 'Your request contains dangerous characters and has been blocked for security reasons.';
        $backText = 'Go Back';
        $homeText = 'Go Home';
    }

    $html = <<<HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{$title}</title>
    <style>
        :root { --bg-body: #f5f7fb; --bg-card: #ffffff; --text-primary: #1e293b; --text-secondary: #475569; --border-light: #e2e8f0; --primary: #3b82f6; --primary-dark: #2563eb; --primary-light: #eff6ff; --danger: #ef4444; --danger-light: #fee2e2; --radius-lg: 20px; --shadow-sm: 0 1px 2px 0 rgba(0,0,0,0.03); }
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--bg-body); color: var(--text-primary); display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; }
        .error-box { background: var(--bg-card); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); border: 1px solid var(--border-light); max-width: 500px; width: 100%; padding: 2rem; text-align: center; }
        .error-box h1 { font-size: 4rem; font-weight: 700; color: var(--danger); margin-bottom: 1rem; }
        .error-box h2 { font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; }
        .error-box p { color: var(--text-secondary); margin-bottom: 2rem; }
        .error-box .btn { display: inline-flex; padding: 0.5rem 1.2rem; border-radius: 40px; font-weight: 500; text-decoration: none; margin: 0 0.5rem; }
        .btn-primary { background: var(--primary); color: white; }
        .btn-outline { border: 1px solid var(--border-light); color: var(--text-secondary); }
    </style>
</head>
<body>
    <div class="error-box">
        <h1>!</h1>
        <h2>{$title}</h2>
        <p>{$message}</p>
        <div>
            <a href="javascript:history.go(-1)" class="btn btn-outline">{$backText}</a>
            <a href="/" class="btn btn-primary">{$homeText}</a>
        </div>
    </div>
</body>
</html>
HTML;
    echo $html;
    exit;
}

global $webscan_switch, $webscan_get, $webscan_post, $webscan_cookie, $webscan_referre, $webscan_white_directory, $webscan_white_url;
if ($webscan_switch && webscan_white($webscan_white_directory, $webscan_white_url)) {
    if ($webscan_get) {
        foreach ($_GET as $key => $value) {
            webscan_StopAttack($key, $value, "GET");
        }
    }
    if ($webscan_post) {
        foreach ($_POST as $key => $value) {
            webscan_StopAttack($key, $value, "POST");
        }
    }
    if ($webscan_cookie) {
        foreach ($_COOKIE as $key => $value) {
            webscan_StopAttack($key, $value, "COOKIE");
        }
    }
    if ($webscan_referre) {
        foreach ($webscan_referer as $key => $value) {
            webscan_StopAttack($key, $value, "REFERRER");
        }
    }
}
?>