Plugin Directory

Changeset 3764759


Ignore:
Timestamp:
10/25/2026 02:34:20 PM (73 minutes ago)
Author:
gataurus
Message:

Release 5.9.1.3: performance, UX, Force HTTPS

Location:
cyberpulse/trunk
Files:
16 edited

Legend:

Unmodified
Added
Removed
  • cyberpulse/trunk/assets/js/cyberpulse-403.js

    r3757045 r3764759  
    22 * CyberPulse — 403 block page script.
    33 *
    4  * Handles the human-verification form on the block page: submits the
    5  * math answer together with the signed token, and reacts to success,
    6  * server-side failure, and network failure.
     4 * Handles both:
     5 * 1. Static Apache page (localization only).
     6 * 2. Dynamic PHP page (human-verification form).
    77 *
    88 * @package CyberPulse
    … …  
    1111    'use strict';
    1212
    13     // ==================== HUMAN VERIFICATION ====================
     13    // ==================== STATIC PAGE LOCALIZATION ====================
     14    // This runs ONLY on the static cyberpulse-403.html page.
     15    (function initStaticLocalization() {
     16        var titleEl = document.getElementById("cp-403-title");
     17        if (!titleEl) return; // Not on the static page, exit.
    1418
    15     /**
    16      * Validates the answer locally (non-empty only) and submits it to
    17      * the cybersec_human_verify AJAX endpoint.
    18      *
    19      * The actual math check is intentionally NOT performed client-side —
    20      * the server compares the submitted value against the expected
    21      * answer stored in a transient. This prevents an attacker from
    22      * reading the correct value out of the page source.
    23      *
    24      * UI states:
    25      *   - success:   shows a checkmark, redirects after 1.5s
    26      *   - bad answer / server error: shows the returned message and
    27      *     a refresh link, re-enables the form, clears the input
    28      *   - network error: shows the connection error and a refresh link,
    29      *     re-enables the form
    30      */
     19        var lang = (navigator.language || navigator.userLanguage || 'en').slice(0, 2).toLowerCase();
     20       
     21        var translations = {
     22            "ru": { title: "Доступ запрещен", desc: "Ваш запрос был заблокирован системой безопасности сервера.", btn: "Обновить страницу" },
     23            "de": { title: "Zugriff verweigert", desc: "Ihre Anfrage wurde vom Sicherheitssystem des Servers blockiert.", btn: "Seite neu laden" },
     24            "fr": { title: "Accès refusé", desc: "Votre demande a été bloquée par le système de sécurité du serveur.", btn: "Actualiser la page" },
     25            "es": { title: "Acceso denegado", desc: "Su solicitud fue bloqueada por el sistema de seguridad del servidor.", btn: "Actualizar página" },
     26            "it": { title: "Accesso negato", desc: "La tua richiesta è stata bloccata dal sistema di sicurezza del server.", btn: "Aggiorna pagina" },
     27            "pt": { title: "Acesso negado", desc: "Sua solicitação foi bloqueada pelo sistema de segurança do servidor.", btn: "Atualizar página" },
     28            "zh": { title: "拒绝访问", desc: "您的请求已被服务器安全系统拦截。", btn: "刷新页面" },
     29            "ja": { title: "アクセス拒否", desc: "リクエストはサーバーのセキュリティシステムによってブロックされました。", btn: "ページを更新" },
     30            "ko": { title: "액세스 거부됨", desc: "귀하의 요청이 서버 보안 시스템에 의해 차단되었습니다.", btn: "페이지 새로고침" }
     31        };
     32       
     33        if (translations[lang]) {
     34            var descEl = document.getElementById("cp-403-desc");
     35            var btnEl = document.getElementById("cp-403-btn");
     36           
     37            titleEl.innerText = "403 " + translations[lang].title;
     38            if (descEl) descEl.innerText = translations[lang].desc;
     39            if (btnEl) btnEl.innerText = translations[lang].btn;
     40        }
     41    })();
     42
     43    // ==================== DYNAMIC PAGE HUMAN VERIFICATION ====================
     44    // This runs ONLY on the PHP-generated page where cybersec403Data exists.
    3145    function verifyHuman() {
     46        // Safety check: if PHP data is missing, do nothing.
     47        if (typeof cybersec403Data === 'undefined') return;
     48
    3249        var answerInput = document.getElementById('sw-human-answer');
    3350        var errorDiv = document.getElementById('sw-test-error');
    … …  
    3855        }
    3956
    40                 var answer = answerInput.value;
     57        var answer = answerInput.value;
    4158
    4259        if (!answer) {
    … …  
    4663        }
    4764
    48         // Client-side math check removed — server validates the answer
    49         // against the transient-stored expected value.
    5065        errorDiv.style.display = 'none';
    5166        verifyBtn.disabled = true;
    … …  
    92107    }
    93108
    94     // ==================== INITIALIZATION ====================
    95 
    96     /**
    97      * Wires up the verification form once the DOM is ready:
    98      * Enter-to-submit, click-to-submit, and auto-focus on the input.
    99      */
     109    // ==================== DYNAMIC PAGE INITIALIZATION ====================
    100110    document.addEventListener('DOMContentLoaded', function() {
    101111        var answerInput = document.getElementById('sw-human-answer');
    102112        var verifyBtn = document.getElementById('sw-verify-btn');
    103113
    104         if (answerInput) {
    105             answerInput.addEventListener('keypress', function(e) {
    106                 if (e.key === 'Enter') {
    107                     verifyHuman();
    108                 }
    109             });
    110         }
     114        // If these elements don't exist, we are on the static page. Skip.
     115        if (!answerInput || !verifyBtn) return;
    111116
    112         if (verifyBtn) {
    113             verifyBtn.addEventListener('click', verifyHuman);
    114         }
     117        answerInput.addEventListener('keypress', function(e) {
     118            if (e.key === 'Enter') {
     119                verifyHuman();
     120            }
     121        });
    115122
    116         // Auto-focus the input field
    117         if (answerInput) {
    118             setTimeout(function() {
    119                 answerInput.focus();
    120             }, 100);
    121         }
     123        verifyBtn.addEventListener('click', verifyHuman);
     124
     125        setTimeout(function() {
     126            answerInput.focus();
     127        }, 100);
    122128    });
    123129
  • cyberpulse/trunk/cyberpulse-core.php

    r3763154 r3764759  
    4545define('CYBERSEC_PERMANENT_BLOCKED_SUBNETS', 'secwall_permanent_blocked_subnets');
    4646define('CYBERSEC_UA_BLOCKS_OPTION', 'secwall_blocked_user_agents');
    47 define('CYBERSEC_VERSION', '5.9.1.2');
     47define('CYBERSEC_VERSION', '5.9.1.3');
    4848
    4949if (!file_exists(CYBERSEC_LOG_DIR)) {
    … …  
    9999        'force_secure_cookies' => 1,
    100100        'rate_limiting_enabled' => 1,
    101         'rate_limit_requests' => 60,
     101        'rate_limit_requests' => 40,
    102102        'rate_limit_window' => 60,
    103103        'rest_api_protection' => 1,
    … …  
    116116        'ai_training_block_enabled' => 1,
    117117        'ai_search_block_enabled'   => 0,
     118        'force_https' => 0,
    118119    );
    119120   
    … …  
    27232724    }
    27242725
     2726    // Chrome < 90.
     2727    // Exceptions: real browsers (Yandex, Opera, Edge, etc.) that also carry Chrome/<ver> token.
     2728    if ($chrome_ver > 0 && $chrome_ver < 90) {
     2729        $real_old_browsers = array('YaBrowser', 'OPR/', 'Edg/', 'SamsungBrowser', 'Brave', 'Vivaldi', 'CriOS', 'FxiOS', 'OPiOS');
     2730        $is_real = false;
     2731        foreach ($real_old_browsers as $b) {
     2732            if (stripos($ua, $b) !== false) { $is_real = true; break; }
     2733        }
     2734        // Do not block if it's a real browser or actual old mobile device
     2735        if (!$is_real && stripos($ua, 'Mobile') === false && stripos($ua, 'Android') === false) {
     2736            return true;
     2737        }
     2738    }
     2739
    27252740    if ($chrome_ver > 0 && stripos($ua, 'Macintosh') !== false) {
    2726     if ($chrome_ver >= 76
    2727         && (empty($_SERVER['HTTP_SEC_FETCH_SITE']) || empty($_SERVER['HTTP_SEC_FETCH_MODE']))) {
    2728         return true;
    2729     }
    2730     if ($chrome_ver >= 76 && !cybersec_looks_like_real_chrome()) {
    2731         return true;
    2732     }
    2733 }
     2741        if ($chrome_ver >= 76
     2742            && (empty($_SERVER['HTTP_SEC_FETCH_SITE']) || empty($_SERVER['HTTP_SEC_FETCH_MODE']))) {
     2743            return true;
     2744        }
     2745        if ($chrome_ver >= 76 && !cybersec_looks_like_real_chrome()) {
     2746            return true;
     2747        }
     2748    }
    27342749   
    27352750    if ($chrome_ver >= 76
    … …  
    27372752    && stripos($ua, 'Android') === false
    27382753    && !cybersec_looks_like_real_chrome()) {
    2739     return true;
    2740 }
     2754        return true;
     2755    }
    27412756
    27422757    // Internal consistency
    … …  
    27552770            if (stripos($ua, 'Edg/') !== false) return true;
    27562771        }
     2772
     2773        // Firefox < 90.
     2774        if ($ff_ver < 90) {
     2775            $real_ff = array('Waterfox', 'PaleMoon', 'SeaMonkey', 'Iceweasel');
     2776            $is_real_ff = false;
     2777            foreach ($real_ff as $r) {
     2778                if (stripos($ua, $r) !== false) { $is_real_ff = true; break; }
     2779            }
     2780            // Exclude actual old mobile devices
     2781            if (!$is_real_ff && !$is_fxios && stripos($ua, 'Mobile') === false && stripos($ua, 'Android') === false) {
     2782                return true;
     2783            }
     2784        }
    27572785    }
    27582786
    … …  
    27652793            $ver = (int)$vm[1];
    27662794            if ($ver > 100) {
     2795                return true;
     2796            }
     2797           
     2798            // Safari Version < 14 = bot.
     2799            // Exclude mobile devices (old iPhones/iPads cannot update).
     2800            if ($ver < 14 && stripos($ua, 'Mobile') === false && stripos($ua, 'iPad') === false) {
    27672801                return true;
    27682802            }
    … …  
    27992833    }
    28002834   
    2801         // Known botnet fingerprints — exact old Chrome builds
     2835    // Known botnet fingerprints — exact old Chrome builds
    28022836    // (identical UA from multiple countries within minutes = scraper farm)
    28032837    $botnet_fingerprints = array(
    … …  
    28222856    }
    28232857
    2824     // v5.9.0.10: Opera Presto (defunct since 2013) — any "Presto/" marker is a bot
     2858    // Opera Presto
    28252859    if (stripos($ua, 'Presto/') !== false) {
    28262860        return true;
    28272861    }
    28282862
    2829     // v5.9.0.10: Windows CE — EOL since 2013, no real users exist
     2863    // Windows CE
    28302864    if (stripos($ua, 'Windows CE') !== false) {
    28312865        return true;
    … …  
    28432877   
    28442878    if (preg_match('/Windows NT (6\.1|6\.3)/', $ua)
    2845     && $chrome_ver > 0
    2846     && $chrome_ver < 110
    2847     && !cybersec_looks_like_real_chrome()) {
    2848     return true;
    2849 }
     2879        && $chrome_ver > 0
     2880        && $chrome_ver < 110
     2881        && !cybersec_looks_like_real_chrome()) {
     2882        return true;
     2883    }
    28502884
    28512885    // AI bots — block before any whitelist
    … …  
    29072941    // Tool & script detection
    29082942    $tool_patterns = array(
    2909     'libwww-perl','curl/','wget/','Go-http-client','Java/',
    2910     'Apache-HttpClient','zgrab','masscan','nmap','sqlmap','nikto','nessus',
    2911     'HeadlessChrome','PhantomJS','Selenium','puppeteer',
    2912     'keys-so-bot','SERankingBacklinksBot','scrapy',
    2913     'BacklinksExtendedBot','AffsignalCrawler','SeopultContentAnalyzer','Photon/',
    2914     'python-requests','python-urllib','python-httpx',
    2915     'node-fetch','axios/','okhttp/',
    2916     'Faraday/','Rest-Client','Typhoeus','Excon/',
    2917 );
     2943        'libwww-perl','curl/','wget/','Go-http-client','Java/',
     2944        'Apache-HttpClient','zgrab','masscan','nmap','sqlmap','nikto','nessus',
     2945        'HeadlessChrome','PhantomJS','Selenium','puppeteer',
     2946        'keys-so-bot','SERankingBacklinksBot','scrapy',
     2947        'BacklinksExtendedBot','AffsignalCrawler','SeopultContentAnalyzer','Photon/',
     2948        'python-requests','python-urllib','python-httpx',
     2949        'node-fetch','axios/','okhttp/',
     2950        'Faraday/','Rest-Client','Typhoeus','Excon/',
     2951    );
    29182952    foreach ($tool_patterns as $tool) {
    29192953        if (stripos($ua, $tool) !== false) return true;
    … …  
    29522986            if (preg_match('/(Windows NT|Macintosh|X11; Linux|x86_64|Intel Mac OS X)/', $ua)) return false;
    29532987            return true;
    2954         }
    2955     }
    2956 
    2957     // Old Firefox (except known forks)
    2958     if (preg_match('/Firefox\/(\d+)\./', $ua, $m)) {
    2959         $fv = (int)$m[1];
    2960         if ($fv < 60) {
    2961             $real_ff = array('Waterfox', 'PaleMoon', 'SeaMonkey', 'Iceweasel');
    2962             $is_real = false;
    2963             foreach ($real_ff as $r) {
    2964                 if (stripos($ua, $r) !== false) { $is_real = true; break; }
    2965             }
    2966             if (!$is_real) return true;
    29672988        }
    29682989    }
    … …  
    46604681    if(is_admin()||wp_doing_ajax()||wp_doing_cron()) return;
    46614682    if(defined('REST_REQUEST')&&REST_REQUEST) return;
     4683   
     4684    $early_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
     4685   
     4686    if (preg_match('#^/(ads\.txt|app-ads\.txt|robots\.txt|favicon\.ico|sellers\.json|security\.txt)(\?|$)#i', $early_uri)) {
     4687        return;
     4688    }
     4689
     4690    if (strpos($early_uri, '/.well-known/') === 0) {
     4691        return;
     4692    }
    46624693   
    46634694    $early_ip = cybersec_get_visitor_ip();
    … …  
    48784909                if (preg_match($bad_re, $ua_check)) {
    48794910                    cybersec_increment_stat('blocked');
    4880                     cybersec_ban_and_log($ip_early, 'Static file via bot UA: ' . $bad_re, true);
     4911                    cybersec_ban_and_log($ip_early, cybersec_translate('Static file via bot UA') . ': ' . $bad_re, true);
    48814912                    cybersec_show_block_page($ip_early, cybersec_translate('Access denied.'));
    48824913                    exit;
    … …  
    66856716add_action('init', 'cybersec_force_secure_cookies', 1);
    66866717
     6718// ==================== FORCE HTTPS REDIRECT ====================
     6719
     6720/**
     6721 * Redirects non-HTTPS requests to HTTPS with a 301.
     6722 *
     6723 * Trusts X-Forwarded-Proto: https so the redirect does not loop when
     6724 * the site sits behind a TLS-terminating reverse proxy — is_ssl()
     6725 * returns false in that case even though the client connected over
     6726 * HTTPS. Fires on init priority 1, after brute-force check (0) and
     6727 * alongside other early security hooks.
     6728 *
     6729 * Disabled by default. Enable only after confirming the SSL cert
     6730 * covers the site's canonical domain (and www. alias, if used).
     6731 */
     6732function cybersec_force_https() {
     6733    $s = cybersec_get_settings();
     6734    if (empty($s['force_https'])) return;
     6735    if (is_ssl()) return;
     6736    if (wp_doing_ajax() || wp_doing_cron() || (defined('WP_CLI') && WP_CLI)) return;
     6737    if (defined('REST_REQUEST') && REST_REQUEST) return;
     6738
     6739    $fwd_proto = isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
     6740        ? strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_PROTO'])))
     6741        : '';
     6742    if ($fwd_proto === 'https') return;
     6743
     6744    $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
     6745    $uri  = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
     6746    if (empty($host)) return;
     6747
     6748    wp_safe_redirect('https://' . $host . $uri, 301);
     6749    exit;
     6750}
     6751add_action('init', 'cybersec_force_https', 1);
     6752
    66876753/**
    66886754 * Global rate limiter. Tracks requests per IP in the configured window
    … …  
    71527218    if(get_option(CYBERSEC_PAGES_OPTION)===false) update_option(CYBERSEC_PAGES_OPTION,array(home_url()));
    71537219    if(get_option(CYBERSEC_BOT_OPTIONS)===false) update_option(CYBERSEC_BOT_OPTIONS,cybersec_get_default_bot_options());
    7154     if(get_option(CYBERSEC_SETTINGS_OPTION)===false) { $default_settings = cybersec_get_settings(); $recommended = array('auto_unblock_minutes'=>30,'alert_threshold'=>20,'alert_minutes'=>5,'brute_max_attempts'=>5,'brute_window_minutes'=>15,'rate_limit_requests'=>60,'rate_limit_window'=>60,'max_revisions'=>5,'auto_clean_logs_days'=>30,'referer_history_threshold'=>1,'referer_rate_1min'=>5,'referer_rate_5min'=>10,'referer_same_page'=>3,'referer_no_proxy_rate'=>3); $default_settings = array_merge($default_settings, $recommended); update_option(CYBERSEC_SETTINGS_OPTION, $default_settings); }
     7220    if(get_option(CYBERSEC_SETTINGS_OPTION)===false) { $default_settings = cybersec_get_settings(); $recommended = array('auto_unblock_minutes'=>30,'alert_threshold'=>20,'alert_minutes'=>5,'brute_max_attempts'=>5,'brute_window_minutes'=>15,'rate_limit_requests'=>40,'rate_limit_window'=>60,'max_revisions'=>5,'auto_clean_logs_days'=>30,'referer_history_threshold'=>1,'referer_rate_1min'=>5,'referer_rate_5min'=>10,'referer_same_page'=>3,'referer_no_proxy_rate'=>3); $default_settings = array_merge($default_settings, $recommended); update_option(CYBERSEC_SETTINGS_OPTION, $default_settings); }
    71557221    if(get_option(CYBERSEC_WHITELIST_OPTION)===false) update_option(CYBERSEC_WHITELIST_OPTION,array());
    71567222    if(get_option(CYBERSEC_TEMP_BLOCKS_OPTION)===false) update_option(CYBERSEC_TEMP_BLOCKS_OPTION,array());
  • cyberpulse/trunk/cyberpulse-htaccess.php

    r3763154 r3764759  
    11<?php
    2 /**
    3  * CyberPulse — .htaccess management for Apache / LiteSpeed.
    4  *
    5  * Requests to existing files (.git/objects/*, wp-config.php.bak,
    6  * /.aws/credentials, /backup.sql) never reach PHP: Apache serves them
    7  * as static content because WordPress's own rewrite rule uses
    8  * `RewriteCond !-f` ("if file exists, don't pass to index.php").
    9  * Only the web server can block these requests, so the rules must
    10  * live in .htaccess BEFORE the WordPress block.
    11  *
    12  * The plugin owns only its own marker-delimited section:
    13  *
    14  *   # BEGIN CyberPulse Protection
    15  *   ...
    16  *   # END CyberPulse Protection
    17  *
    18  * On deactivation the block is removed and the rest of .htaccess
    19  * (WP Fastest Cache, Wordfence, etc.) is left untouched.
    20  *
    21  * OPT-IN: this protection is disabled by default. Rules are written
    22  * only when the admin explicitly enables "htaccess_protection_enabled"
    23  * in plugin settings. This prevents accidental breakage of SEO tools,
    24  * nginx setups, or custom server configs on fresh installs.
    25  *
    26  * @package CyberPulse
    27  */
    28 
    292if (!defined('ABSPATH')) exit;
    303
    … …  
    3710
    3811/**
    39  * Returns true if the current web server reads .htaccess files.
    40  * Apache and LiteSpeed do; nginx does not.
    41  *
    42  * @return bool
     12 * Checks whether the current web server honors .htaccess files.
     13 *
     14 * Apache and LiteSpeed read .htaccess natively; nginx does not.
     15 * When SERVER_SOFTWARE is unavailable (CLI, some FPM setups), the
     16 * function assumes .htaccess works — the caller is expected to
     17 * handle any resulting error gracefully via the verification step
     18 * in cybersec_htaccess_sync().
     19 *
     20 * @return bool True when .htaccess rules will actually take effect.
    4321 */
    4422function cybersec_htaccess_server_supports() {
    … …  
    4826
    4927    if ($server === '') {
    50         // CLI / cron — assume Apache
    5128        return !defined('WP_CLI') || !WP_CLI;
    5229    }
    … …  
    5734
    5835/**
    59  * Converts a CIDR to Apache-escaped IP prefix regexes.
    60  *
    61  *   /32     → "x\.y\.z\.w$"         (exact IP)
    62  *   /24     → "x\.y\.z\."           (octet prefix)
    63  *   /16     → "x\.y\."              (two-octet prefix)
    64  *   /8      → "x\."                 (one-octet prefix)
    65  *   /15-/9  → multiple /16 prefixes (single-octet range expanded)
    66  *
    67  * Masks between /24 and /32 exclusive are skipped — expanding them
    68  * would generate dozens of prefixes per subnet and blow up .htaccess.
    69  *
    70  * @param string $cidr
    71  * @return array Apache-escaped prefix strings.
     36 * Converts a CIDR block into .htaccess-compatible regex prefixes.
     37 *
     38 * Only IPv4 is supported. The returned strings are the LHS of
     39 * mod_rewrite REMOTE_ADDR conditions (escaped dots included), so
     40 * they can be used directly as `RewriteCond %{REMOTE_ADDR} ^<prefix>`.
     41 *
     42 * Rationale for the specific prefix lengths:
     43 *   /32  → exact match with trailing `$`
     44 *   /24  → first three octets + trailing dot (covers 256 IPs)
     45 *   /16  → first two octets + trailing dot
     46 *   /8   → single octet + trailing dot
     47 *   /17–/23 and non-/8-/16/-/24 masks cannot be expressed as a
     48 *   single prefix and are expanded into up to 64 host-prefixes.
     49 *   Anything outside /8–/32 is refused to keep the rule set small.
     50 *
     51 * @param string $cidr CIDR string, e.g. "192.168.1.0/24".
     52 * @return array List of regex prefixes (empty on invalid input).
    7253 */
    7354function cybersec_htaccess_cidr_to_prefixes($cidr) {
    … …  
    9576        return array($o[0] . '\\.');
    9677    }
     78    // /17–/23 cannot be expressed as a single octet-aligned prefix.
    9779    if ($mask > 16 && $mask < 24) {
    9880        return array();
    9981    }
    10082
    101     // /9 to /15 — expand to /16 prefixes
     83    // /9–/15: expand to individual /16 prefixes (capped at 64 to keep
     84    // the block size bounded).
    10285    $range = 1 << (16 - $mask);
    10386    $base = ((int) $o[0]) * 256 + ((int) $o[1]);
    … …  
    11396
    11497/**
    115  * Builds the CyberPulse .htaccess block content.
    116  *
    117  * IP deny list comes from the 'secwall_permanent_blocked_subnets' option
    118  * (30 most recent entries only). Path rules for .git/.env/etc. are static.
    119  *
    120  * Returns an empty string when:
    121  *   - the server does not read .htaccess (nginx, CLI with WP_CLI);
    122  *   - .htaccess is not writable;
    123  *   - the feature is disabled by the admin (opt-in).
    124  *
    125  * @return string Empty string when the block should not be written.
     98 * Builds the .htaccess protection block from current settings.
     99 *
     100 * Returns an empty string when the feature is disabled, the server
     101 * does not support .htaccess, or the file is not writable — callers
     102 * must treat an empty return as "do nothing" rather than "write empty".
     103 *
     104 * The block does three things:
     105 *   1. Blocks the top-30 recently-banned /24+ subnets (deny list),
     106 *      capped at 64 regex conditions to keep .htaccess small.
     107 *   2. Blocks access to sensitive paths (.git, .env, SSH keys,
     108 *      crypto wallets, DB dumps, etc.) — defence-in-depth even if
     109 *      the PHP-level checks fail.
     110 *   3. Blocks known AI / data-scraping bots by User-Agent. This
     111 *      happens before PHP runs, saving server resources.
     112 *
     113 * Server's own IP is always excluded from the deny conditions so
     114 * the site cannot lock itself out.
     115 *
     116 * @return string The complete BEGIN/END-wrapped block, or ''.
    126117 */
    127118function cybersec_htaccess_build_block() {
    128119    if (!cybersec_htaccess_server_supports()) return '';
    129120
    130     // OPT-IN gate: never produce rules unless the admin explicitly
    131     // enabled .htaccess protection in Settings. This keeps fresh
    132     // installs and upgrades side-effect free.
     121    // OPT-IN gate: never produce rules unless the admin explicitly enabled it.
    133122    if (function_exists('cybersec_get_settings')) {
    134123        $settings = cybersec_get_settings();
    … …  
    145134    $server_ip = function_exists('cybersec_get_server_ip') ? cybersec_get_server_ip() : '';
    146135
     136    // Collect CIDR prefixes from the 30 most recent permabans.
     137    // Sorting by blocked_at DESC keeps the freshest threats present
     138    // when the 64-condition cap is hit.
    147139    $deny_prefixes = array();
    148140    $permanent = get_option('secwall_permanent_blocked_subnets', array());
    149141    if (!empty($permanent) && is_array($permanent)) {
    150         // Most recent 30 subnets only — keep .htaccess compact
    151142        uasort($permanent, function($a, $b) {
    152143            $ta = isset($a['blocked_at']) ? strtotime($a['blocked_at']) : 0;
    … …  
    166157    $block  = CYBERPULSE_HTACCESS_BEGIN . "\n";
    167158    $block .= "# Generated: " . gmdate('Y-m-d H:i:s') . " UTC\n";
     159   
     160    // Assign our custom 403 page
     161    $block .= "ErrorDocument 403 /cyberpulse-403.html\n\n";
     162   
    168163    $block .= "<IfModule mod_rewrite.c>\n";
    169164    $block .= "RewriteEngine On\n";
    170165
     166    // ============================================================
     167    // PERMABAN DENY LIST
     168    // Server's own IP is excluded first so a misconfigured ban of
     169    // its own subnet cannot break admin access.
     170    // ============================================================
    171171    if (!empty($deny_prefixes)) {
    172         // Never block the server's own IP (self-requests from cron, monitoring, etc.)
    173172        if ($server_ip && filter_var($server_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
    174173            $block .= "RewriteCond %{REMOTE_ADDR} !^" . str_replace('.', '\\.', $server_ip) . "$\n";
    … …  
    181180    }
    182181
     182    // ============================================================
     183    // SENSITIVE CONFIG & SYSTEM FILES
     184    // ============================================================
     185   
    183186    // Configuration files that must NEVER be exposed to the web.
    184187    // Apache usually blocks .htaccess via <FilesMatch "^\.ht"> in the
    … …  
    193196    $block .= "RewriteRule (^|/)(readme|license|changelog)\\.(html|txt|md)$ - [F,L,NC]\n";
    194197
    195     // Path rules — only files/dirs that are NEVER legitimate to expose.
    196     // /graphql and /userfiles are intentionally excluded — they break
    197     // WPGraphQL, Gatsby, Next.js, and CMS platforms with /userfiles/ dirs.
     198    // VCS directories, env/secret files, SSH keys, crypto wallets,
     199    // container/IDE probes, and DB dumps. Runs before PHP, so it
     200    // catches scanners that would otherwise hit PHP and log.
    198201    $block .= "RewriteRule (^|/)\\.git(/|$) - [F,L]\n";
    199202    $block .= "RewriteRule (^|/)\\.svn(/|$) - [F,L]\n";
    … …  
    211214    $block .= "RewriteRule (^|/)@fs(/|$) - [F,L]\n";
    212215    $block .= "RewriteRule (^|/)proc/self(/|$) - [F,L]\n";
     216   
     217    // wp-config backups — covers wp-config.bak, wp-config.php.bak,
     218    // wp-config.php.zip, wp-config.php.tar.gz, etc.
    213219    $block .= "RewriteRule (^|/)wp-config(\\.php)?\\.(bak|old|txt|save|orig|tmp|swp|zip|tar\\.gz)$ - [F,L]\n";
     220   
    214221    $block .= "RewriteRule (^|/)(database|dump|db|users|backup)\\.sql$ - [F,L]\n";
    215222
    216     // Build the AI-bot regex from the ACTIVE signatures only,
    217     // respecting the ai_training_block_enabled / ai_search_block_enabled
    218     // toggles. When both are off, this whole section is skipped.
     223    // ============================================================
     224    // AI / DATA-SCRAPING BOT BLOCK
     225    // Fires before PHP. Signatures come from the ACTIVE list only,
     226    // respecting ai_training_block_enabled / ai_search_block_enabled.
     227    // When both are off, this section is skipped entirely.
     228    // ============================================================
    219229    $active_ai = function_exists('cybersec_get_active_ai_bot_signatures')
    220230        ? cybersec_get_active_ai_bot_signatures()
    221231        : array();
    222232
    223         if (!empty($active_ai)) {
     233    if (!empty($active_ai)) {
    224234        // preg_quote: protects against regex injection.
    225235        // str_replace: replaces literal spaces (e.g. "IAS Crawler")
    … …  
    232242        }
    233243        $bot_ua = '(' . implode('|', $escaped) . ')';
     244       
     245        // Exclude the custom 403 HTML page, its CSS and JS files
     246        // to prevent infinite loops and broken styling.
     247        $block .= "RewriteCond %{REQUEST_URI} !/cyberpulse-403\\.html [NC]\n";
     248        $block .= "RewriteCond %{REQUEST_URI} !/cyberpulse-403\\.css [NC]\n";
     249        $block .= "RewriteCond %{REQUEST_URI} !/cyberpulse-403\\.js [NC]\n";
    234250        $block .= "RewriteCond %{HTTP_USER_AGENT} " . $bot_ua . " [NC]\n";
    235251        $block .= "RewriteRule .* - [F,L]\n\n";
    … …  
    243259
    244260/**
    245  * Inserts or updates the CyberPulse block in .htaccess.
    246  *
    247  * The block is placed BEFORE "# BEGIN WordPress" so deny rules are
    248  * evaluated before WP's "if file exists" catch-all.
    249  *
    250  * @param bool $verify Perform a test GET after writing and roll back on 5xx.
    251  *                     Pass false during activation — WordPress is in
    252  *                     maintenance mode and the verify-request would 5xx,
    253  *                     causing a false rollback.
    254  * @return bool
     261 * Returns a data-URI SVG favicon for the static 403 page.
     262 *
     263 * Inlined so the favicon renders even when the visitor's IP is
     264 * blocked at the .htaccess level and every external request
     265 * (including /favicon.ico) returns 403. No network round-trip,
     266 * no dependency on WordPress being bootstrapped.
     267 *
     268 * @return string `data:image/svg+xml,...` URI ready for <link rel="icon">.
     269 */
     270function cybersec_htaccess_favicon_data_uri() {
     271    static $uri = null;
     272    if ($uri !== null) return $uri;
     273
     274    $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">'
     275        . '<path d="M 18.0,4.5 C 10.9,4.5 6.8,6.8 6.8,10.4 C 7.0,25.8 16.0,30.9 18.0,32.3 C 20.0,30.9 29.0,25.8 29.2,10.4 C 29.2,6.8 25.1,4.5 18.0,4.5 Z"'
     276        . ' fill="#1a1e18" stroke="#00e048" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
     277        . '<path fill="none" stroke="#00e048" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"'
     278        . ' d="M 9.0,18.0 L 13.2,18.0 L 15.0,14.0 L 18.0,23.6 L 21.0,10.0 L 22.8,18.0 L 27.0,18.0"/>'
     279        . '</svg>';
     280
     281    $uri = 'data:image/svg+xml;charset=utf-8,' . rawurlencode($svg);
     282    return $uri;
     283}
     284
     285/**
     286 * Writes the protection block into .htaccess and optionally verifies it.
     287 *
     288 * Uses WordPress's BEGIN/END markers to replace an existing block
     289 * in place. New blocks are placed BEFORE the `# BEGIN WordPress`
     290 * marker so they can deny access before WP rewrite rules run.
     291 *
     292 * Two safety measures:
     293 *   - Whitespace-in-UA-regex guard: if the built block contains a
     294 *     literal space inside the bot UA condition, .htaccess parsing
     295 *     would fail and break the whole site. Refuses to write.
     296 *   - Optional HTTP verification: sends a request to the front page.
     297 *     On 5xx (500/502/504) the original .htaccess is restored, so a
     298 *     bad rule cannot leave the site offline.
     299 *
     300 * Errors are stored in the `cybersec_htaccess_sync_error` transient
     301 * for 5 minutes and surfaced via the AJAX handler / admin error panel.
     302 *
     303 * @param bool $verify Run the HTTP self-test after writing.
     304 * @return bool True on success, false on any failure.
    255305 */
    256306function cybersec_htaccess_sync($verify = true) {
    … …  
    266316    }
    267317
    268     // Safety: RewriteCond patterns must not contain literal spaces.
    269     // A space inside the regex group is parsed by Apache as an extra
    270     // argument and causes 500 Internal Server Error on every request.
     318    // Whitespace inside the UA condition would break Apache parsing.
     319    // Fail closed rather than write an invalid .htaccess.
    271320    if (strpos($block, 'RewriteCond %{HTTP_USER_AGENT}') !== false) {
    272321        if (!preg_match('/RewriteCond\s+%\{HTTP_USER_AGENT\}\s+\S+\s+\[NC\]/', $block)) {
    … …  
    279328    }
    280329
     330    // ============================================================
     331    // GENERATE STATIC 403 PAGE
     332    // We use a distinct filename (cyberpulse-403.html) so we never
     333    // overwrite a custom 403.html that the user might already have.
     334    //
     335    // This file is served directly by Apache via ErrorDocument — it
     336    // does NOT go through WordPress, so wp_enqueue_style() and
     337    // wp_enqueue_script() are unavailable. To comply with Plugin
     338    // Check (WordPress.WP.EnqueuedResources.*) and keep the page
     339    // fully self-contained, the CSS and JS are embedded inline
     340    // instead of being loaded via <link>/<script src>.
     341    //
     342    // Same pattern as cybersec_show_unified_block_page() in
     343    // cyberpulse-core.php — that path is already Plugin-Check clean.
     344    //
     345    // The favicon is also a data URI (cybersec_htaccess_favicon_data_uri):
     346    // a normal /favicon.ico request would be blocked by the same
     347    // permaban / AI-bot rule that produced this page, so the icon
     348    // must be embedded rather than linked.
     349    // ============================================================
     350    $html_403_path = ABSPATH . 'cyberpulse-403.html';
     351    if (!cybersec_fs_exists($html_403_path)) {
     352        $lang = substr(cybersec_get_locale(), 0, 2);
     353
     354        $css_path = plugin_dir_path(__FILE__) . 'assets/css/cyberpulse-403.css';
     355        $js_path  = plugin_dir_path(__FILE__) . 'assets/js/cyberpulse-403.js';
     356
     357        $inline_css = cybersec_fs_get_contents($css_path);
     358        $inline_js  = cybersec_fs_get_contents($js_path);
     359
     360        // Minify inline CSS — identical to cybersec_show_unified_block_page().
     361        // Safe regex: strips comments, collapses whitespace, tightens around
     362        // structural symbols. calc() operators keep their spaces because
     363        // they're not in the [{};:,>] class.
     364        if (!empty($inline_css)) {
     365            $inline_css = preg_replace('#/\*.*?\*/#s', '', $inline_css);
     366            $inline_css = preg_replace('/\s+/', ' ', $inline_css);
     367            $inline_css = preg_replace('/\s*([{};:,>])\s*/', '$1', $inline_css);
     368            $inline_css = trim($inline_css);
     369        }
     370
     371        $favicon_uri = cybersec_htaccess_favicon_data_uri();
     372
     373        $html_403 = '<!DOCTYPE html>
     374<html lang="' . esc_attr($lang) . '">
     375<head>
     376    <meta charset="UTF-8">
     377    <meta name="robots" content="noindex,nofollow,noarchive">
     378    <meta name="viewport" content="width=device-width,initial-scale=1.0">
     379    <title>403 Forbidden | CyberPulse Security</title>
     380    <link rel="icon" type="image/svg+xml" href="' . esc_attr($favicon_uri) . '">
     381    <style>' . $inline_css . '</style>
     382</head>
     383<body>
     384    <div class="sw-card">
     385        <div class="sw-icon">
     386            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ff4040" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     387                <circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
     388            </svg>
     389        </div>
     390        <h1 class="sw-title" id="cp-403-title">403 ' . esc_html(cybersec_translate('Access Denied')) . '</h1>
     391        <p class="sw-description" id="cp-403-desc">' . esc_html(cybersec_translate('Your request was blocked by the server security system.')) . '</p>
     392        <a href="javascript:location.reload();" class="sw-btn" id="cp-403-btn">' . esc_html(cybersec_translate('Refresh Page')) . '</a>
     393        <div class="sw-footer">
     394            <a href="https://cyberpulse-security.com" target="_blank" rel="noopener" class="sw-brand">
     395                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" fill="none">
     396                    <path d="M 18.0,4.5 C 10.9,4.5 6.8,6.8 6.8,10.4 C 7.0,25.8 16.0,30.9 18.0,32.3 C 20.0,30.9 29.0,25.8 29.2,10.4 C 29.2,6.8 25.1,4.5 18.0,4.5 Z" fill="none" stroke="#00e048" stroke-width="2"/>
     397                    <path fill="none" stroke="#00e048" stroke-width="1.6" d="M 9.0,18.0 L 13.2,18.0 L 15.0,14.0 L 18.0,23.6 L 21.0,10.0 L 22.8,18.0 L 27.0,18.0"/>
     398                </svg>
     399                Protected by CyberPulse
     400            </a>
     401        </div>
     402    </div>
     403    <script>' . $inline_js . '</script>
     404</body>
     405</html>';
     406        cybersec_fs_put_contents($html_403_path, $html_403);
     407    }
     408
    281409    $htaccess_path = ABSPATH . '.htaccess';
    282410
    … …  
    294422    }
    295423
     424    // Strip any existing CyberPulse block first so we do not duplicate.
    296425    $pattern = '/' . preg_quote(CYBERPULSE_HTACCESS_BEGIN, '/') . '.*?' . preg_quote(CYBERPULSE_HTACCESS_END, '/') . "\n?/s";
    297426    $content = preg_replace($pattern, '', $original);
    298427
     428    // Insert before the WordPress block so our F,L rules win.
    299429    $wp_marker = '# BEGIN WordPress';
    300430    $pos = strpos($content, $wp_marker);
    … …  
    319449    }
    320450
     451    // ============================================================
     452    // SELF-TEST
     453    // Fetch the front page. A 5xx response almost certainly means
     454    // our new rules broke the server — roll back to the original.
     455    // 4xx is fine (auth-protected sites, 404 on non-existent query
     456    // string, etc.) and does not trigger rollback.
     457    // ============================================================
    321458    $verify_resp = wp_remote_get(home_url('/?cyberpulse_verify=' . wp_rand(1000, 9999)), array(
    322459        'timeout' => 5,
    … …  
    329466    ));
    330467
    331         if (!is_wp_error($verify_resp)) {
     468    if (!is_wp_error($verify_resp)) {
    332469        $code = wp_remote_retrieve_response_code($verify_resp);
    333470        if (in_array($code, array(500, 502, 504))) {
    … …  
    347484
    348485/**
    349  * Removes the CyberPulse block from .htaccess (used on deactivation
    350  * and when the admin unchecks the option in Settings).
    351  *
    352  * Only the plugin's own markers are touched — other plugins' blocks
    353  * stay intact.
    354  *
    355  * @return bool
     486 * Removes the CyberPulse block from .htaccess.
     487 *
     488 * Uses the BEGIN/END markers so user-added rules above or below the
     489 * block are preserved untouched. Safe to call when no block exists
     490 * (returns true) or when .htaccess is missing (returns true).
     491 *
     492 * @return bool True if the file no longer contains a CyberPulse block.
    356493 */
    357494function cybersec_htaccess_remove() {
     495    // Clean up the static 403 page we generated
     496    $html_403_path = ABSPATH . 'cyberpulse-403.html';
     497    if (cybersec_fs_exists($html_403_path)) {
     498        cybersec_fs_delete($html_403_path);
     499    }
     500
    358501    $htaccess_path = ABSPATH . '.htaccess';
    359502    if (!cybersec_fs_exists($htaccess_path)) return true;
    … …  
    370513}
    371514
    372 // ------------------------------------------------------------------
    373 // AJAX: manual sync button
    374 // ------------------------------------------------------------------
    375 
    376 /**
    377  * AJAX: rewrites the plugin's .htaccess block. Returns a JSON error when
    378  * the server does not read .htaccess (nginx), the feature is disabled,
    379  * or the write fails — the latter rolls back automatically inside
    380  * cybersec_htaccess_sync().
    381  */
    382 add_action('wp_ajax_cybersec_htaccess_sync', function() {
     515// ==================== AJAX: MANUAL SYNC ====================
     516
     517/**
     518 * AJAX: writes .htaccess rules on demand from the admin UI.
     519 *
     520 * Requires the "htaccess_protection_enabled" option to be ON —
     521 * this is a safety check, not just a convenience: the admin must
     522 * have explicitly opted in before we touch .htaccess. On failure
     523 * the transient error reason is surfaced in the JSON response.
     524 */
     525add_action('wp_ajax_cyberpulse_htaccess_sync', function() {
    383526    $nonce_action = defined('CYBERPULSE_AJAX_NONCE_ACTION') ? CYBERPULSE_AJAX_NONCE_ACTION : 'cybersec_ajax_action';
    384527    check_ajax_referer($nonce_action, 'nonce');
    … …  
    387530    }
    388531
    389     // Feature must be explicitly enabled in Settings. If the admin
    390     // clicks the "Sync" button while the option is off, refuse rather
    391     // than silently writing rules they didn't ask for.
    392     $settings = function_exists('cybersec_get_settings') ? cybersec_get_settings() : array();
     532    $settings = cybersec_get_settings();
    393533    if (empty($settings['htaccess_protection_enabled'])) {
    394         wp_send_json_error(array(
    395             'message' => 'Enable .htaccess protection in Settings first.',
    396         ));
     534        wp_send_json_error(array('message' => 'Enable .htaccess protection in Settings first.'));
    397535    }
    398536
    … …  
    416554});
    417555
    418 /**
    419  * Runs on every admin_init: performs the one-time legacy cleanup for
    420  * installations upgraded from a version that wrote the block
    421  * unconditionally, and re-syncs .htaccess when the plugin version
    422  * changed and the feature is enabled.
     556// ==================== AUTO-SYNC ON VERSION CHANGE ====================
     557
     558/**
     559 * Hooked on `admin_init` priority 20.
     560 *
     561 * Responsibilities:
     562 *   1. One-time cleanup of legacy blocks if the feature is now
     563 *      disabled (migration from an older build that shipped rules
     564 *      unconditionally).
     565 *   2. Automatic re-sync when the plugin version changes, so the
     566 *      block stays in sync with the current deny list / bot list.
     567 *      Stores the sync state (or error) for admin visibility.
     568 *
     569 * Runs ONLY when the feature is enabled — otherwise it just cleans
     570 * up and exits.
    423571 */
    424572add_action('admin_init', function() {
    … …  
    426574    $enabled  = !empty($settings['htaccess_protection_enabled']);
    427575
    428     // One-time migration for users upgrading from a version where the
    429     // block was written unconditionally. If the option is now off but a
    430     // legacy block still exists in .htaccess, remove it once so nobody
    431     // keeps stale rules they never asked for.
     576    // One-time migration: remove legacy blocks if the feature was disabled.
    432577    if (!$enabled && !get_option('cybersec_htaccess_legacy_purged')) {
    433578        $htaccess_path = ABSPATH . '.htaccess';
    … …  
    444589    if (!$enabled) return;
    445590
    446     // Version check — rewrite only when the plugin version changed.
    447     $stored  = get_option('cybersec_htaccess_version', '');
    448     $current = defined('CYBERSEC_VERSION') ? CYBERSEC_VERSION : '0';
     591    // Version guard — avoid touching .htaccess on every admin page load.
     592    $stored = get_option('cybersec_htaccess_version', '');
     593    $current = defined('CYBERPULSE_PRO_VERSION') ? CYBERPULSE_PRO_VERSION : (defined('CYBERSEC_VERSION') ? CYBERSEC_VERSION : '0');
     594
    449595    if ($stored === $current) return;
    450596
  • cyberpulse/trunk/cyberpulse-tabs.php

    r3763154 r3764759  
    260260            'ai_training_block_enabled' => isset($_POST['cybersec_ai_training_block_enabled']) ? 1 : 0,
    261261            'ai_search_block_enabled'   => isset($_POST['cybersec_ai_search_block_enabled']) ? 1 : 0,
     262            'force_https' => isset($_POST['cybersec_force_https']) ? 1 : 0,
    262263        );
    263264
    … …  
    10041005            $fsize = filesize($audit_log);
    10051006            if ($fsize > 0) {
    1006                 // v5.9.1.1 — read tail via WP_Filesystem instead of
    1007                 // fopen/fseek/fread/fclose (Plugin Check
    1008                 // WordPress.WP.AlternativeFunctions.file_system_operations_*).
    10091007                $full = cybersec_fs_get_contents($audit_log);
    10101008                if ($full !== '') {
    … …  
    20382036    <p style="margin-top:8px;font-size:11px;color:var(--sw-success);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:baseline;margin-right:3px;position:relative;top:1.5px;"><polyline points="20 6 9 17 4 12"/></svg><?php echo esc_html(cybersec_translate('Always active')); ?></p>
    20392037</div>
     2038            <div class="sw-settings-card" style="grid-column: 1 / -1;">
     2039                <h4><?php echo esc_html(cybersec_translate('Force HTTPS')); ?> <span class="sw-status <?php echo !empty($st['force_https']) ? 'sw-status-active' : 'sw-status-inactive'; ?>"><?php echo !empty($st['force_https']) ? esc_html(cybersec_translate('Enabled')) : esc_html(cybersec_translate('Disabled')); ?></span></h4>
     2040                <p><?php echo esc_html(cybersec_translate('Automatically redirects all HTTP requests to HTTPS (301 redirect).')); ?> <strong><?php echo esc_html(cybersec_translate('Enable only if you have an SSL certificate configured')); ?></strong>, <?php echo esc_html(cybersec_translate('otherwise the site will become inaccessible.')); ?></p>
     2041                <label style="margin-top:8px;display:block;"><input type="checkbox" name="cybersec_force_https" value="1" <?php checked(!empty($st['force_https']), true); ?>> <?php echo esc_html(cybersec_translate('Redirect HTTP → HTTPS')); ?></label>
     2042            </div>
    20402043        </div>
    20412044    </div>
    … …  
    20692072                <strong style="color:var(--sw-accent);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:baseline;margin-right:3px;position:relative;top:2px;"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><polyline points="13 2 9 10 12 10 8 18"/></svg><?php echo esc_html(cybersec_translate('Flood and DDoS Protection')); ?></strong>
    20702073                <p style="margin:6px 0 0 0;font-size:12px;color:var(--sw-text-secondary);line-height:1.5;"><?php echo esc_html(cybersec_translate('Three levels of application-layer attack protection.')); ?> <strong><?php echo esc_html(cybersec_translate('Basic')); ?></strong> — <?php echo esc_html(cybersec_translate('constant protection against scraping and flooding.')); ?> <strong><?php echo esc_html(cybersec_translate('Enhanced')); ?></strong> — <?php echo esc_html(cybersec_translate('for repelling DDoS attacks.')); ?> <strong>Anti-Slowloris</strong> — <?php echo esc_html(cybersec_translate('automatic protection against slow connections.')); ?></p>
    2071                 <div style="background:var(--sw-success-light);border:2px solid var(--sw-success-border);border-radius:var(--sw-radius);padding:14px 16px;margin-top:12px;"><strong style="color:var(--sw-success);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:baseline;margin-right:3px;position:relative;top:1.5px;"><polyline points="20 6 9 17 4 12"/></svg><?php echo esc_html(cybersec_translate('Basic Level (Rate Limiting)')); ?></strong><p style="margin:6px 0 0 0;font-size:12px;color:var(--sw-text-secondary);"><?php echo esc_html(cybersec_translate('Limits the number of requests from one IP to the entire site within a specified time interval. Protects against DDoS attacks and aggressive scraping. After 3 repeated violations, the subnet is automatically permanently banned.')); ?> <strong><?php echo esc_html(cybersec_translate('Recommended: enabled, 60 requests per 60 seconds.')); ?></strong></p><label style="margin-top:10px;display:block;"><input type="checkbox" name="cybersec_rate_limiting_enabled" value="1" <?php checked($st['rate_limiting_enabled'],1);?>> <?php echo esc_html(cybersec_translate('Enable basic protection')); ?></label><div style="margin-top:10px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;"><span><?php echo esc_html(cybersec_translate('Maximum')); ?></span><input type="number" name="cybersec_rate_limit_requests" value="<?php echo (int)$st['rate_limit_requests'];?>" min="1" max="1000" style="width:80px;"><span><?php echo esc_html(cybersec_translate('requests per')); ?></span><input type="number" name="cybersec_rate_limit_window" value="<?php echo (int)$st['rate_limit_window'];?>" min="1" max="3600" style="width:80px;"><span><?php echo esc_html(cybersec_translate('seconds')); ?></span></div></div>
    2072                 <div style="background:var(--sw-info-light);border:2px solid var(--sw-info-border);border-radius:var(--sw-radius);padding:14px 16px;margin-top:12px;"><strong style="color:var(--sw-info);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:baseline;margin-right:3px;position:relative;top:1.5px;"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg><?php echo esc_html(cybersec_translate('Anti-Slowloris Protection')); ?></strong><p style="margin:6px 0 0 0;font-size:12px;color:var(--sw-text-secondary);"><?php echo esc_html(cybersec_translate('Automatically tracks and blocks slow connections (Slowloris attacks). Works automatically when enhanced DDoS protection is enabled. IP is blocked when exceeding')); ?> <strong><?php echo esc_html(cybersec_translate('20 simultaneous connections')); ?></strong>.</p></div>
     2074
     2075                <!-- ============ BASIC LEVEL ============ -->
     2076                <div style="background:var(--sw-success-light);border:2px solid var(--sw-success-border);border-radius:var(--sw-radius);padding:14px 16px;margin-top:12px;">
     2077                    <strong style="color:var(--sw-success);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:baseline;margin-right:3px;position:relative;top:1.5px;"><polyline points="20 6 9 17 4 12"/></svg><?php echo esc_html(cybersec_translate('Basic Level (Rate Limiting)')); ?></strong>
     2078                    <p style="margin:6px 0 0 0;font-size:12px;color:var(--sw-text-secondary);"><?php echo esc_html(cybersec_translate('Limits the number of requests from one IP to the entire site within a specified time interval. Protects against DDoS attacks and aggressive scraping. After 3 repeated violations, the subnet is automatically permanently banned.')); ?> <strong><?php echo esc_html(cybersec_translate('Recommended: enabled, 40 requests per 60 seconds.')); ?></strong></p>
     2079
     2080                    <label style="margin-top:10px;display:block;"><input type="checkbox" name="cybersec_rate_limiting_enabled" value="1" <?php checked($st['rate_limiting_enabled'],1);?>> <?php echo esc_html(cybersec_translate('Enable basic protection')); ?></label>
     2081
     2082                    <div style="margin-top:10px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;"><span><?php echo esc_html(cybersec_translate('Maximum')); ?></span><input type="number" name="cybersec_rate_limit_requests" value="<?php echo (int)$st['rate_limit_requests'];?>" min="1" max="1000" style="width:80px;"><span><?php echo esc_html(cybersec_translate('requests per')); ?></span><input type="number" name="cybersec_rate_limit_window" value="<?php echo (int)$st['rate_limit_window'];?>" min="1" max="3600" style="width:80px;"><span><?php echo esc_html(cybersec_translate('seconds')); ?></span></div>
     2083
     2084                    <!-- Collapsible help: how to choose the right value -->
     2085                    <details style="margin-top:12px;padding:10px 12px;background:var(--sw-card-bg-alt);border:1px solid var(--sw-border);border-radius:var(--sw-radius);font-size:12px;color:var(--sw-text-secondary);line-height:1.6;">
     2086                        <summary style="cursor:pointer;font-weight:700;color:var(--sw-text);outline:none;">
     2087                            <?php echo esc_html(cybersec_translate('How to choose the right value')); ?>
     2088                        </summary>
     2089                        <ul style="margin:10px 0 0 16px;padding:0;">
     2090                            <li style="margin-bottom:6px;"><strong>30–35</strong> — <?php echo esc_html(cybersec_translate('homogeneous audience, mostly desktop users, without mobile carriers.')); ?></li>
     2091                            <li style="margin-bottom:6px;"><strong>40</strong> — <?php echo esc_html(cybersec_translate('universal default, suitable for most news and information sites.')); ?></li>
     2092                            <li style="margin-bottom:6px;"><strong>50–80</strong> — <?php echo esc_html(cybersec_translate('large share of mobile-carrier traffic (CGNAT) or corporate networks.')); ?></li>
     2093                        </ul>
     2094                        <p style="margin:10px 0 0 0;">
     2095                            <strong><?php echo esc_html(cybersec_translate('Important:')); ?></strong>
     2096                            <?php echo esc_html(cybersec_translate('the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.')); ?>
     2097                        </p>
     2098                        <p style="margin:8px 0 0 0;padding:8px 10px;background:var(--sw-info-light);border-left:3px solid var(--sw-info);border-radius:4px;">
     2099                            <strong><?php echo esc_html(cybersec_translate('CGNAT (Carrier-Grade NAT):')); ?></strong>
     2100                            <?php echo esc_html(cybersec_translate('mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.')); ?>
     2101                        </p>
     2102                    </details>
     2103                </div>
     2104
     2105                <!-- ============ ANTI-SLOWLORIS ============ -->
     2106                <div style="background:var(--sw-info-light);border:2px solid var(--sw-info-border);border-radius:var(--sw-radius);padding:14px 16px;margin-top:12px;">
     2107                    <strong style="color:var(--sw-info);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:baseline;margin-right:3px;position:relative;top:1.5px;"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg><?php echo esc_html(cybersec_translate('Anti-Slowloris Protection')); ?></strong>
     2108                    <p style="margin:6px 0 0 0;font-size:12px;color:var(--sw-text-secondary);"><?php echo esc_html(cybersec_translate('Automatically tracks and blocks slow connections (Slowloris attacks). Works automatically when enhanced DDoS protection is enabled. IP is blocked when exceeding')); ?> <strong><?php echo esc_html(cybersec_translate('20 simultaneous connections')); ?></strong>.</p>
     2109                </div>
    20732110            </div>
    20742111        </div>
  • cyberpulse/trunk/cyberpulse.php

    r3763154 r3764759  
    44 * Plugin URI: https://wordpress.org/plugins/cyberpulse
    55 * Description: Real-time security heartbeat for your WordPress. Monitors every request, blocks bots instantly, protects against brute force and AI scraping. Feel the pulse of protection. Free version with PRO upgrade available.
    6  * Version: 5.9.1.2
     6 * Version: 5.9.1.3
    77 * Author: CyberPulse Security
    88 * Author URI: https://cyberpulse-security.com
  • cyberpulse/trunk/languages/cyberpulse-de_DE.php

    r3763154 r3764759  
    830830    'Automatically added administrator IP' => 'Automatisch hinzugefügte Administrator-IP',
    831831    'No manual entries yet.' => 'Noch keine manuellen Einträge.',
     832    'Static file via bot UA' => 'Statische Datei über Bot User-Agent',
    832833
    833834    // ------------------------------------------------------------------
    … …  
    10571058    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => 'Wird beim Speichern automatisch angewendet. Regeln werden entfernt, wenn das Häkchen entfernt wird. Andere .htaccess-Inhalte (WordPress, Caching-Plugins usw.) bleiben erhalten. Ein fehlgeschlagenes Schreiben wird überprüft und automatisch zurückgesetzt.',
    10581059    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => 'Fügt Blockierungsregeln direkt zu .htaccess hinzu (nur Apache / LiteSpeed). Blockiert KI-Bots und sensible Dateien (.git, .env, .sql, Schlüssel, Backups), bevor PHP geladen wird, um Serverressourcen zu sparen. Die KI-Bot-Regeln folgen den beiden Schaltern oben – das Deaktivieren einer Kategorie entfernt sie auch aus .htaccess.',
     1060   
     1061    // Force HTTPS
     1062    'Force HTTPS' => 'HTTPS erzwingen',
     1063    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'Leitet alle HTTP-Anfragen automatisch auf HTTPS um (301-Weiterleitung).',
     1064    'Enable only if you have an SSL certificate configured' => 'Nur aktivieren, wenn ein SSL-Zertifikat konfiguriert ist',
     1065    'otherwise the site will become inaccessible.' => 'andernfalls wird die Website unzugänglich.',
     1066    'Redirect HTTP → HTTPS' => 'HTTP → HTTPS-Weiterleitung',
     1067   
     1068    // Flood and DDoS Protection — Help Block
     1069    'Recommended: enabled, 40 requests per 60 seconds.' => 'Empfohlen: aktiviert, 40 Anfragen pro 60 Sekunden.',
     1070    'How to choose the right value' => 'So wählen Sie den richtigen Wert',
     1071    'homogeneous audience, mostly desktop users, without mobile carriers.' => 'homogene Zielgruppe, überwiegend Desktop-Nutzer, ohne Mobilfunkanbieter.',
     1072    'universal default, suitable for most news and information sites.' => 'universeller Standardwert, geeignet für die meisten Nachrichten- und Informationsseiten.',
     1073    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'großer Anteil an Mobilfunkverkehr (CGNAT) oder Firmennetzwerken.',
     1074    'Important:' => 'Wichtig:',
     1075    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => 'der obige Wert gilt für normale Browser mit einem modernen User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). Für leere oder defekte User-Agents, Rechenzentrums-IPs, alte Browser und bekannte Bots wird das Limit automatisch 3–6× strenger angewendet.',
     1076    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT):',
     1077    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'Mobilfunkanbieter (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) und Firmennetzwerke weisen eine öffentliche IP Hunderten von Teilnehmern zu. Ein zu niedriges Limit blockiert echte Leser, nicht Angreifer.',
    10591078);
  • cyberpulse/trunk/languages/cyberpulse-en_US.php

    r3763154 r3764759  
    802802    'Enable ONLY if' => 'Enable ONLY if',
    803803    'you use Cloudflare (orange cloud enabled in DNS).' => 'you use Cloudflare (orange cloud enabled in DNS).',
     804    'Static file via bot UA' => 'Static file via bot UA',
    804805
    805806    // ------------------------------------------------------------------
    … …  
    10751076    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.'
    10761077        => 'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.',
     1078       
     1079    // Force HTTPS
     1080    'Force HTTPS' => 'Force HTTPS',
     1081    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'Automatically redirects all HTTP requests to HTTPS (301 redirect).',
     1082    'Enable only if you have an SSL certificate configured' => 'Enable only if you have an SSL certificate configured',
     1083    'otherwise the site will become inaccessible.' => 'otherwise the site will become inaccessible.',
     1084    'Redirect HTTP → HTTPS' => 'Redirect HTTP → HTTPS',
     1085   
     1086    // Flood and DDoS Protection — Help Block
     1087    'Recommended: enabled, 40 requests per 60 seconds.' => 'Recommended: enabled, 40 requests per 60 seconds.',
     1088    'How to choose the right value' => 'How to choose the right value',
     1089    'homogeneous audience, mostly desktop users, without mobile carriers.' => 'homogeneous audience, mostly desktop users, without mobile carriers.',
     1090    'universal default, suitable for most news and information sites.' => 'universal default, suitable for most news and information sites.',
     1091    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'large share of mobile-carrier traffic (CGNAT) or corporate networks.',
     1092    'Important:' => 'Important:',
     1093    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => 'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.',
     1094    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT):',
     1095    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.',
    10771096);
  • cyberpulse/trunk/languages/cyberpulse-es_ES.php

    r3763154 r3764759  
    802802    'Enable ONLY if' => 'Activar SOLO si',
    803803    'you use Cloudflare (orange cloud enabled in DNS).' => 'usas Cloudflare (nube naranja activada en DNS).',
     804    'Static file via bot UA' => 'Archivo estático vía User-Agent de bot',
    804805
    805806    // ------------------------------------------------------------------
    … …  
    10551056    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => 'Se aplica automáticamente al guardar. Las reglas se eliminan cuando se desmarca. Se conserva el resto del contenido de .htaccess (WordPress, complementos de caché, etc.). Una escritura fallida se verifica y se revierte automáticamente.',
    10561057    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => 'Agrega reglas de bloqueo directamente a .htaccess (solo Apache / LiteSpeed). Bloquea bots de IA y archivos confidenciales (.git, .env, .sql, claves, copias de seguridad) antes de que se cargue PHP, ahorrando recursos del servidor. Las reglas de los bots de IA siguen los dos interruptores anteriores: deshabilitar una categoría también la elimina de .htaccess.',
     1058   
     1059    // Force HTTPS
     1060    'Force HTTPS' => 'Forzar HTTPS',
     1061    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'Redirige automáticamente todas las solicitudes HTTP a HTTPS (redirección 301).',
     1062    'Enable only if you have an SSL certificate configured' => 'Habilitar solo si tiene un certificado SSL configurado',
     1063    'otherwise the site will become inaccessible.' => 'de lo contrario, el sitio quedará inaccesible.',
     1064    'Redirect HTTP → HTTPS' => 'Redirigir HTTP → HTTPS',
     1065   
     1066    // Flood and DDoS Protection — Help Block
     1067    'Recommended: enabled, 40 requests per 60 seconds.' => 'Recomendado: activado, 40 solicitudes por 60 segundos.',
     1068    'How to choose the right value' => 'Cómo elegir el valor correcto',
     1069    'homogeneous audience, mostly desktop users, without mobile carriers.' => 'audiencia homogénea, mayoritariamente usuarios de escritorio, sin operadores móviles.',
     1070    'universal default, suitable for most news and information sites.' => 'valor predeterminado universal, adecuado para la mayoría de sitios de noticias e información.',
     1071    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'gran proporción de tráfico de operadores móviles (CGNAT) o redes corporativas.',
     1072    'Important:' => 'Importante:',
     1073    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => 'el valor anterior se aplica a navegadores normales con un User-Agent moderno (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). Para User-Agents vacíos o rotos, IP de centros de datos, navegadores antiguos y bots conocidos, el límite se aplica automáticamente 3–6× más estricto.',
     1074    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT):',
     1075    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'los operadores móviles (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) y las redes corporativas asignan una IP pública a cientos de suscriptores. Un límite demasiado bajo bloqueará lectores reales, no atacantes.',
    10571076);
  • cyberpulse/trunk/languages/cyberpulse-fr_FR.php

    r3763154 r3764759  
    802802    'Enable ONLY if' => 'Activez UNIQUEMENT si',
    803803    'you use Cloudflare (orange cloud enabled in DNS).' => 'vous utilisez Cloudflare (nuage orange activé dans DNS).',
     804    'Static file via bot UA' => 'Fichier statique via User-Agent de bot',
    804805
    805806    // ------------------------------------------------------------------
    … …  
    10571058    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => 'Appliqué automatiquement lors de l\'enregistrement. Les règles sont supprimées lorsque la case est décochée. Le reste du contenu de .htaccess (WordPress, plugins de cache, etc.) est préservé. Un échec d\'écriture est vérifié et annulé automatiquement.',
    10581059    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => 'Ajoute des règles de blocage directement dans .htaccess (Apache / LiteSpeed uniquement). Bloque les bots IA et les fichiers sensibles (.git, .env, .sql, clés, sauvegardes) avant même le chargement de PHP, économisant les ressources du serveur. Les règles des bots IA suivent les deux interrupteurs ci-dessus — la désactivation d\'une catégorie la supprime également de .htaccess.',
     1060   
     1061    // Force HTTPS
     1062    'Force HTTPS' => 'Forcer HTTPS',
     1063    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'Redirige automatiquement toutes les requêtes HTTP vers HTTPS (redirection 301).',
     1064    'Enable only if you have an SSL certificate configured' => 'Activer uniquement si un certificat SSL est configuré',
     1065    'otherwise the site will become inaccessible.' => 'sinon le site deviendra inaccessible.',
     1066    'Redirect HTTP → HTTPS' => 'Rediriger HTTP → HTTPS',
     1067   
     1068    // Flood and DDoS Protection — Help Block
     1069    'Recommended: enabled, 40 requests per 60 seconds.' => 'Recommandé : activé, 40 requêtes par 60 secondes.',
     1070    'How to choose the right value' => 'Comment choisir la bonne valeur',
     1071    'homogeneous audience, mostly desktop users, without mobile carriers.' => 'audience homogène, principalement des utilisateurs de bureau, sans opérateurs mobiles.',
     1072    'universal default, suitable for most news and information sites.' => 'valeur par défaut universelle, adaptée à la plupart des sites d\'actualités et d\'information.',
     1073    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'part importante du trafic d\'opérateurs mobiles (CGNAT) ou de réseaux d\'entreprise.',
     1074    'Important:' => 'Important :',
     1075    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => 'la valeur ci-dessus s\'applique aux navigateurs normaux avec un User-Agent moderne (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). Pour les User-Agents vides ou cassés, les IP de centres de données, les anciens navigateurs et les bots connus, la limite est appliquée automatiquement 3–6× plus strictement.',
     1076    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT) :',
     1077    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'les opérateurs mobiles (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) et les réseaux d\'entreprise attribuent une IP publique à des centaines d\'abonnés. Une limite trop basse bloquera de vrais lecteurs, pas les attaquants.',
    10591078);
  • cyberpulse/trunk/languages/cyberpulse-it_IT.php

    r3763154 r3764759  
    800800    'Enable ONLY if' => 'Abilita SOLO se',
    801801    'you use Cloudflare (orange cloud enabled in DNS).' => 'usi Cloudflare (cloud arancione abilitato nel DNS).',
     802    'Static file via bot UA' => 'File statico tramite User-Agent bot',
    802803
    803804    // ------------------------------------------------------------------
    … …  
    10431044    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => 'Applicato automaticamente al salvataggio. Le regole vengono rimosse quando la casella viene deselezionata. Il resto del contenuto di .htaccess (WordPress, plugin di caching, ecc.) viene preservato. Una scrittura fallita viene verificata e ripristinata automaticamente.',
    10441045    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => 'Aggiunge regole di blocco direttamente in .htaccess (solo Apache / LiteSpeed). Blocca bot IA e file sensibili (.git, .env, .sql, chiavi, backup) prima ancora che PHP venga caricato, risparmiando risorse del server. Le regole dei bot IA seguono i due interruttori sopra — la disabilitazione di una categoria la rimuove anche da .htaccess.',
     1046   
     1047    // Force HTTPS
     1048    'Force HTTPS' => 'Forza HTTPS',
     1049    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'Reindirizza automaticamente tutte le richieste HTTP a HTTPS (reindirizzamento 301).',
     1050    'Enable only if you have an SSL certificate configured' => 'Abilita solo se hai configurato un certificato SSL',
     1051    'otherwise the site will become inaccessible.' => 'altrimenti il sito diventerà inaccessibile.',
     1052    'Redirect HTTP → HTTPS' => 'Reindirizza HTTP → HTTPS',
     1053   
     1054    // Flood and DDoS Protection — Help Block
     1055    'Recommended: enabled, 40 requests per 60 seconds.' => 'Consigliato: attivo, 40 richieste per 60 secondi.',
     1056    'How to choose the right value' => 'Come scegliere il valore giusto',
     1057    'homogeneous audience, mostly desktop users, without mobile carriers.' => 'pubblico omogeneo, prevalentemente utenti desktop, senza operatori mobili.',
     1058    'universal default, suitable for most news and information sites.' => 'valore predefinito universale, adatto alla maggior parte dei siti di notizie e informazione.',
     1059    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'grande quota di traffico da operatori mobili (CGNAT) o reti aziendali.',
     1060    'Important:' => 'Importante:',
     1061    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => 'il valore sopra si applica ai browser normali con User-Agent moderno (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). Per User-Agent vuoti o corrotti, IP di datacenter, browser obsoleti e bot noti, il limite viene applicato automaticamente 3–6× più severo.',
     1062    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT):',
     1063    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'gli operatori mobili (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) e le reti aziendali assegnano un IP pubblico a centinaia di abbonati. Un limite troppo basso bloccherà lettori reali, non gli aggressori.',
    10451064);
  • cyberpulse/trunk/languages/cyberpulse-ja.php

    r3763154 r3764759  
    828828    'Enable ONLY if' => '次の場合のみ有効にする',
    829829    'you use Cloudflare (orange cloud enabled in DNS).' => 'Cloudflareを使用している(DNSでオレンジ色の雲が有効)。',
     830    'Static file via bot UA' => 'ボットのUser-Agentによる静的ファイルアクセス',
    830831
    831832    // ------------------------------------------------------------------
    … …  
    10571058    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => '保存時に自動的に適用されます。チェックを外すとルールが削除されます。他の.htaccessの内容(WordPress、キャッシュプラグインなど)は保持されます。書き込みに失敗した場合は検証され、自動的にロールバックされます。',
    10581059    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => 'ブロックルールを.htaccessに直接追加します(Apache / LiteSpeedのみ)。PHPが読み込まれる前にAIボットと機密ファイル(.git、.env、.sql、キー、バックアップ)をブロックし、サーバーリソースを節約します。AIボットのルールは上記の2つのトグルに従います — カテゴリを無効にすると.htaccessからも削除されます。',
     1060   
     1061    // Force HTTPS
     1062    'Force HTTPS' => 'HTTPSを強制する',
     1063    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'すべてのHTTPリクエストをHTTPSに自動リダイレクトします(301リダイレクト)。',
     1064    'Enable only if you have an SSL certificate configured' => 'SSL証明書が設定されている場合のみ有効にしてください',
     1065    'otherwise the site will become inaccessible.' => 'そうでない場合、サイトにアクセスできなくなります。',
     1066    'Redirect HTTP → HTTPS' => 'HTTP → HTTPS リダイレクト',
     1067   
     1068    // Flood and DDoS Protection — Help Block
     1069    'Recommended: enabled, 40 requests per 60 seconds.' => '推奨:有効、60 秒あたり 40 リクエスト。',
     1070    'How to choose the right value' => '適切な値の選び方',
     1071    'homogeneous audience, mostly desktop users, without mobile carriers.' => '均質なオーディエンス、主にデスクトップユーザー、モバイルキャリアなし。',
     1072    'universal default, suitable for most news and information sites.' => '汎用的なデフォルト値、ほとんどのニュース・情報サイトに適しています。',
     1073    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'モバイルキャリア(CGNAT)または企業ネットワークのトラフィックが多い場合。',
     1074    'Important:' => '重要:',
     1075    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => '上記の値は、最新の User-Agent(Chrome 90+、Firefox 90+、Safari 14+、Edge 90+)を使用する通常のブラウザに適用されます。空または壊れた User-Agent、データセンター IP、古いブラウザ、既知のボットには、制限が自動的に 3〜6 倍厳しく適用されます。',
     1076    'CGNAT (Carrier-Grade NAT):' => 'CGNAT(キャリアグレード NAT):',
     1077    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'モバイルキャリア(MTS、MegaFon、Beeline、Airtel、Claro、Jio、Vivo)と企業ネットワークは、1 つのパブリック IP を数百の加入者に割り当てます。制限が低すぎると、攻撃者ではなく実際の読者をブロックします。',
    10591078);
  • cyberpulse/trunk/languages/cyberpulse-ko_KR.php

    r3763154 r3764759  
    824824    'Enable ONLY if' => '다음 경우에만 활성화',
    825825    'you use Cloudflare (orange cloud enabled in DNS).' => 'Cloudflare를 사용 중인 경우 (DNS에서 주황색 구름 활성화).',
     826    'Static file via bot UA' => '봇 User-Agent를 통한 정적 파일 접근',
    826827
    827828    // ------------------------------------------------------------------
    … …  
    10401041    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => '저장 시 자동으로 적용됩니다. 선택을 취소하면 규칙이 제거됩니다. 기타 .htaccess 콘텐츠(WordPress, 캐싱 플러그인 등)는 보존됩니다. 쓰기 실패는 확인 후 자동으로 롤백됩니다.',
    10411042    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => '차단 규칙을 .htaccess에 직접 추가합니다(Apache / LiteSpeed 전용). PHP가 로드되기 전에 AI 봇 및 민감한 파일(.git, .env, .sql, 키, 백업)을 차단하여 서버 리소스를 절약합니다. AI 봇 규칙은 위의 두 토글을 따릅니다 — 범주를 비활성화하면 .htaccess에서도 제거됩니다.',
     1043   
     1044    // Force HTTPS
     1045    'Force HTTPS' => 'HTTPS 강제',
     1046    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => '모든 HTTP 요청을 HTTPS로 자동 리디렉션합니다 (301 리디렉션).',
     1047    'Enable only if you have an SSL certificate configured' => 'SSL 인증서가 구성된 경우에만 활성화하세요',
     1048    'otherwise the site will become inaccessible.' => '그렇지 않으면 사이트에 액세스할 수 없게 됩니다.',
     1049    'Redirect HTTP → HTTPS' => 'HTTP → HTTPS 리디렉션',
     1050   
     1051    // Flood and DDoS Protection — Help Block
     1052    'Recommended: enabled, 40 requests per 60 seconds.' => '권장: 활성화됨, 60초당 40회 요청.',
     1053    'How to choose the right value' => '올바른 값을 선택하는 방법',
     1054    'homogeneous audience, mostly desktop users, without mobile carriers.' => '동질적인 사용자층, 주로 데스크톱 사용자, 모바일 통신사 없음.',
     1055    'universal default, suitable for most news and information sites.' => '범용 기본값, 대부분의 뉴스 및 정보 사이트에 적합합니다.',
     1056    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => '모바일 통신사(CGNAT) 또는 기업 네트워크 트래픽이 많은 경우.',
     1057    'Important:' => '중요:',
     1058    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => '위 값은 최신 User-Agent(Chrome 90+, Firefox 90+, Safari 14+, Edge 90+)를 사용하는 일반 브라우저에 적용됩니다. 비어 있거나 손상된 User-Agent, 데이터센터 IP, 구형 브라우저 및 알려진 봇에는 제한이 자동으로 3–6배 더 엄격하게 적용됩니다.',
     1059    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT):',
     1060    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => '모바일 통신사(MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo)와 기업 네트워크는 하나의 공인 IP를 수백 명의 가입자에게 할당합니다. 제한이 너무 낮으면 공격자가 아닌 실제 독자를 차단합니다.',
    10421061);
  • cyberpulse/trunk/languages/cyberpulse-pt_BR.php

    r3763154 r3764759  
    824824    'Enable ONLY if' => 'Ative SOMENTE se',
    825825    'you use Cloudflare (orange cloud enabled in DNS).' => 'você usa Cloudflare (nuvem laranja ativada no DNS).',
     826    'Static file via bot UA' => 'Arquivo estático via User-Agent de bot',
    826827
    827828    // ------------------------------------------------------------------
    … …  
    10531054    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => 'Aplicado automaticamente ao salvar. As regras são removidas quando desmarcadas. O restante do conteúdo do .htaccess (WordPress, plugins de cache, etc.) é preservado. Uma gravação com falha é verificada e revertida automaticamente.',
    10541055    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => 'Adiciona regras de bloqueio diretamente ao .htaccess (apenas Apache / LiteSpeed). Bloqueia bots de IA e arquivos confidenciais (.git, .env, .sql, chaves, backups) antes mesmo de o PHP carregar, economizando recursos do servidor. As regras de bots de IA seguem os dois botões acima — desativar uma categoria também a remove do .htaccess.',
     1056   
     1057    // Force HTTPS
     1058    'Force HTTPS' => 'Forçar HTTPS',
     1059    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'Redireciona automaticamente todas as solicitações HTTP para HTTPS (redirecionamento 301).',
     1060    'Enable only if you have an SSL certificate configured' => 'Ative apenas se você tiver um certificado SSL configurado',
     1061    'otherwise the site will become inaccessible.' => 'caso contrário, o site ficará inacessível.',
     1062    'Redirect HTTP → HTTPS' => 'Redirecionar HTTP → HTTPS',
     1063   
     1064    // Flood and DDoS Protection — Help Block
     1065    'Recommended: enabled, 40 requests per 60 seconds.' => 'Recomendado: ativado, 40 requisições por 60 segundos.',
     1066    'How to choose the right value' => 'Como escolher o valor correto',
     1067    'homogeneous audience, mostly desktop users, without mobile carriers.' => 'público homogêneo, majoritariamente usuários de desktop, sem operadoras móveis.',
     1068    'universal default, suitable for most news and information sites.' => 'valor padrão universal, adequado para a maioria dos sites de notícias e informação.',
     1069    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'grande parcela de tráfego de operadoras móveis (CGNAT) ou redes corporativas.',
     1070    'Important:' => 'Importante:',
     1071    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => 'o valor acima se aplica a navegadores normais com User-Agent moderno (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). Para User-Agents vazios ou quebrados, IPs de datacenter, navegadores antigos e bots conhecidos, o limite é aplicado automaticamente 3–6× mais rigoroso.',
     1072    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT):',
     1073    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'operadoras móveis (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) e redes corporativas atribuem um IP público a centenas de assinantes. Um limite muito baixo bloqueará leitores reais, não atacantes.',
    10551074);
  • cyberpulse/trunk/languages/cyberpulse-ru_RU.php

    r3763154 r3764759  
    832832    'Automatically added administrator IP' => 'Автоматически добавлен IP администратора',
    833833    'No manual entries yet.' => 'Ручных записей пока нет.',
     834    'Static file via bot UA' => 'Статический файл запрошен ботом (по User-Agent)',
    834835
    835836    // ------------------------------------------------------------------
    … …  
    11071108    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.'
    11081109        => 'Добавляет правила блокировки прямо в .htaccess (только Apache / LiteSpeed). Блокирует ИИ-ботов и чувствительные файлы (.git, .env, .sql, ключи, бэкапы) ещё до загрузки PHP, экономя ресурсы сервера. Правила для ИИ-ботов подчиняются двум переключателям выше — отключение категории убирает её и из .htaccess.',
     1110       
     1111    // Force HTTPS
     1112    'Force HTTPS' => 'Принудительный HTTPS',
     1113    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => 'Автоматически перенаправляет все HTTP-запросы на HTTPS (301 редирект).',
     1114    'Enable only if you have an SSL certificate configured' => 'Включайте только при наличии настроенного SSL-сертификата',
     1115    'otherwise the site will become inaccessible.' => 'иначе сайт станет недоступен.',
     1116    'Redirect HTTP → HTTPS' => 'Редирект HTTP → HTTPS',
     1117   
     1118    // Flood and DDoS Protection — Help Block
     1119    'Recommended: enabled, 40 requests per 60 seconds.' => 'Рекомендуется: включено, 40 запросов за 60 секунд.',
     1120    'How to choose the right value' => 'Как выбрать правильное значение',
     1121    'homogeneous audience, mostly desktop users, without mobile carriers.' => 'однородная аудитория, преимущественно десктопные пользователи, без мобильных операторов.',
     1122    'universal default, suitable for most news and information sites.' => 'универсальное значение по умолчанию, подходит для большинства новостных и информационных сайтов.',
     1123    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => 'большая доля трафика с мобильных операторов (CGNAT) или из корпоративных сетей.',
     1124    'Important:' => 'Важно:',
     1125    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => 'значение из поля выше применяется к обычным браузерам с современным User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). Для пустых или битых User-Agent, IP из дата-центров, старых браузеров и известных ботов лимит автоматически снижается в 3–6 раз.',
     1126    'CGNAT (Carrier-Grade NAT):' => 'CGNAT (Carrier-Grade NAT):',
     1127    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => 'мобильные операторы (МТС, МегаФон, Билайн, Airtel, Claro, Jio, Vivo) и корпоративные сети выдают один публичный IP сотням абонентов. Слишком низкий лимит заблокирует реальных читателей, а не атакующих.',
    11091128);
  • cyberpulse/trunk/languages/cyberpulse-zh_CN.php

    r3763154 r3764759  
    824824    'Enable ONLY if' => '仅在以下情况下启用',
    825825    'you use Cloudflare (orange cloud enabled in DNS).' => '您使用Cloudflare(DNS中启用橙色云)。',
     826    'Static file via bot UA' => '通过机器人 User-Agent 访问静态文件',
    826827
    827828    // ------------------------------------------------------------------
    … …  
    10531054    'Applied automatically on save. Rules removed when unchecked. Other .htaccess content (WordPress, caching plugins, etc.) is preserved. A failed write is verified and rolled back automatically.' => '保存时自动应用。取消勾选时删除规则。保留 .htaccess 的其他内容(如 WordPress、缓存插件等)。写入失败时会自动验证并回滚。',
    10541055    'Adds blocking rules directly to .htaccess (Apache / LiteSpeed only). Blocks AI bots and sensitive files (.git, .env, .sql, keys, backups) before PHP even loads, saving server resources. The AI-bot rules follow the two toggles above — disabling a category also removes it from .htaccess.' => '直接将拦截规则添加到 .htaccess(仅限 Apache / LiteSpeed)。在加载 PHP 之前拦截 AI 机器人和敏感文件(.git、.env、.sql、密钥、备份),节省服务器资源。AI 机器人规则遵循上方的两个开关——禁用某个类别也会将其从 .htaccess 中移除。',
     1056   
     1057    // Force HTTPS
     1058    'Force HTTPS' => '强制 HTTPS',
     1059    'Automatically redirects all HTTP requests to HTTPS (301 redirect).' => '自动将所有 HTTP 请求重定向到 HTTPS(301 重定向)。',
     1060    'Enable only if you have an SSL certificate configured' => '仅当您已配置 SSL 证书时才启用',
     1061    'otherwise the site will become inaccessible.' => '否则网站将无法访问。',
     1062    'Redirect HTTP → HTTPS' => '重定向 HTTP → HTTPS',
     1063   
     1064    // Flood and DDoS Protection — Help Block
     1065    'Recommended: enabled, 40 requests per 60 seconds.' => '推荐:开启,每 60 秒 40 次请求。',
     1066    'How to choose the right value' => '如何选择正确的值',
     1067    'homogeneous audience, mostly desktop users, without mobile carriers.' => '受众单一,以桌面用户为主,无移动运营商。',
     1068    'universal default, suitable for most news and information sites.' => '通用默认值,适合大多数新闻和信息网站。',
     1069    'large share of mobile-carrier traffic (CGNAT) or corporate networks.' => '移动运营商(CGNAT)或企业网络流量占比较大。',
     1070    'Important:' => '重要提示:',
     1071    'the value above applies to normal browsers with a modern User-Agent (Chrome 90+, Firefox 90+, Safari 14+, Edge 90+). For empty or broken User-Agents, datacenter IPs, old browsers, and known bots the limit is applied 3–6× stricter automatically.' => '上述值适用于使用现代 User-Agent(Chrome 90+、Firefox 90+、Safari 14+、Edge 90+)的正常浏览器。对于空或损坏的 User-Agent、数据中心 IP、旧浏览器和已知机器人,限制会自动收紧 3–6 倍。',
     1072    'CGNAT (Carrier-Grade NAT):' => 'CGNAT(运营商级 NAT):',
     1073    'mobile carriers (MTS, MegaFon, Beeline, Airtel, Claro, Jio, Vivo) and corporate networks assign one public IP to hundreds of subscribers. A too-low limit will block real readers, not attackers.' => '移动运营商(MTS、MegaFon、Beeline、Airtel、Claro、Jio、Vivo)和企业网络为一个公网 IP 分配数百个用户。限制过低会阻止真实读者,而非攻击者。',
    10551074);
  • cyberpulse/trunk/readme.txt

    r3763154 r3764759  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 5.9.1.2
     7Stable tag: 5.9.1.3
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    3131* 🤖 AI Scraping Protection — blocks GPTBot, ClaudeBot, PerplexityBot, CCBot, and other AI data collectors
    3232* 🛡️ Server-level `.htaccess` protection — optional (opt-in) blocking of sensitive files and AI bots directly at the Apache/LiteSpeed level
    33 * 🔍 Multi-layer bot detection (11 independent checks: User-Agent, HTTP headers, Referer, Accept-Language, and more)
     33* 🔍 Multi-layer bot detection (20+ independent checks: User-Agent, HTTP headers, Referer, Accept-Language, behavioral analysis, and more)
    3434* 🔐 Brute force protection — automatic permanent /24 subnet ban after exceeding attempt limit
    3535* 🌍 IP Management — block IPs, subnets (/24), whitelist trusted addresses, detect visitor country by IP
    … …  
    3737* ⏱️ Rate Limiting — configurable request limits to prevent DDoS and aggressive scraping
    3838* ✅ IP Whitelist — manual and automatic (server IP, search engines, administrator IPs)
     39* 🔒 Force HTTPS redirect — automatic 301 redirect from HTTP to HTTPS with proxy-loop protection
    3940* 🛡️ XSS attack protection — blocks cross-site scripting in requests
    4041* 🔎 404 scanner detection — blocks vulnerability scanners by excessive 404 errors
    … …  
    6768= 🔍 How does bot detection work? =
    6869
    69 CyberPulse analyzes 11 parameters of every request: User-Agent, HTTP headers, Accept-Language, Referer, browser headers (Sec-Fetch-*), and more. Suspicious requests are blocked automatically with temporary or permanent IP bans.
     70CyberPulse analyzes 20+ parameters of every request: User-Agent, HTTP headers, Accept-Language, Referer, browser headers (Sec-Fetch-*), behavioral patterns, and more. Suspicious requests are blocked automatically with temporary or permanent IP bans.
    7071
    7172= ⚡ Does it slow down my site? =
    … …  
    1081094. 📁 Page tracking and traffic analytics
    1091105. ✅ Whitelist management panel
    110 6. ⚙️ Security settings with 11 bot detection options
     1116. ⚙️ Security settings with multiple bot detection options
    111112
    112113== Changelog ==
     114
     115= 5.9.1.3 =
     116* ⚡ Performance: Lowered the default Rate Limiting threshold from 60 to 40 requests per minute. Added a smart collapsible guide in the UI to help users choose the right value (with CGNAT and mobile carrier warnings).
     117* 🛡️ Custom Static 403 Page: When .htaccess protection blocks a bot, it now displays a beautifully styled cyberpulse-403.html page instead of the hosting provider's default error page.
     118* 🔧 Better UX: Replaced the "Back to Home" button with a "Refresh Page" button on the static block page, which is more useful for temporary blocks.
     119* 🔒 New Free Feature: Force HTTPS 301 redirect with proxy-loop protection, full-width UI, and translations for all 10 supported languages.
    113120
    114121= 5.9.1.2 =
    … …  
    275282== Source Code ==
    276283
    277 The original unminified developer source code is available at:
    278 https://github.com/gataurus/cyberpulse
     284The original unminified developer source code is available inside the plugin package (in the .zip archive) or via the WordPress.org SVN repository.
Note: See TracChangeset for help on using the changeset viewer.