Plugin Directory

Changeset 3481949


Ignore:
Timestamp:
03/13/2026 11:59:03 AM (2 weeks ago)
Author:
inilerm
Message:

Preparing version 8.9.0

Location:
advanced-ip-blocker/trunk
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • advanced-ip-blocker/trunk/advanced-ip-blocker.php

    r3476940 r3481949  
    44Plugin URI: https://advaipbl.com/
    55Description: Your complete WordPress security firewall. Blocks IPs, bots & countries. Includes an intelligent WAF, Threat Scoring, and Two-Factor Authentication.
    6 Version: 8.8.9
     6Version: 8.9.0
    77Author: IniLerm
    88Author URI: https://advaipbl.com/
     
    1919}
    2020
    21 define( 'ADVAIPBL_VERSION', '8.8.9' );
     21define( 'ADVAIPBL_VERSION', '8.9.0' );
    2222define( 'ADVAIPBL_PLUGIN_FILE', __FILE__ );
    2323
  • advanced-ip-blocker/trunk/css/advaipbl-styles.css

    r3476940 r3481949  
    11/**
    22 * Advanced IP Blocker - Admin Panel Styles
    3  * Version: 8.8.9
     3 * Version: 8.9.0
    44 */
    55
  • advanced-ip-blocker/trunk/includes/class-advaipbl-action-handler.php

    r3476940 r3481949  
    9595
    9696            if ($processed_count > 0) {
     97                $this->plugin->purge_all_page_caches();
    9798                /* translators: %d: Number of entries. */
    9899                $message = sprintf(_n('%d entry has been unblocked.', '%d entries have been unblocked.', $processed_count, 'advanced-ip-blocker'), $processed_count);
     
    141142                case 'unblock_all':
    142143                    $this->plugin->unblock_all_ips('Admin Action');
     144                    $this->plugin->purge_all_page_caches();
    143145                    $message = __('All blocked IPs have been successfully unblocked.', 'advanced-ip-blocker');
    144146                    break;
     
    279281                                } else {
    280282                                    $this->plugin->block_ip_instantly( $ip_or_range, 'manual', __('Manual Block', 'advanced-ip-blocker'), [], 'admin_action' );
     283                                    $this->plugin->purge_all_page_caches();
    281284                                    /* translators: %s: IP. */
    282285                                    $message = sprintf( __( 'Entry %s has been blocked.', 'advanced-ip-blocker' ), $ip_or_range );
     
    285288                            case 'remove_block':
    286289                                $this->plugin->desbloquear_ip($ip_or_range);
     290                                $this->plugin->purge_all_page_caches();
    287291                                /* translators: %s: IP. */
    288292                                $message = sprintf( __( 'IP %s unblocked from all lists.', 'advanced-ip-blocker' ), $ip_or_range );
     
    446450        $options['enable_community_network'] = '1';  // Compartir (Join)
    447451        $options['enable_community_blocking'] = '1'; // Bloquear (Protect)
     452       
     453        // Generar y registrar el API Token V3 automáticamente en nuevas instalaciones
     454        if ( isset( $this->plugin->community_manager ) ) {
     455            // Pasamos las opciones actualizadas a la memoria temporal de la clase Main antes de registrar
     456            $this->plugin->options = $options;
     457            $this->plugin->community_manager->register_site();
     458            // Refrescamos las opciones desde la BD por si register_site las ha modificado directamente.
     459            $options = get_option( ADVAIPBL_Main::OPTION_SETTINGS, [] );
     460        }
    448461    }
    449462   
  • advanced-ip-blocker/trunk/includes/class-advaipbl-admin-pages.php

    r3476940 r3481949  
    2424    }
    2525
     26    nocache_headers(); // Prevent caching on this admin page
     27   
    2628    // 1. DEFINIR LA ESTRUCTURA COMPLETA DEL MENÚ
    2729    $menu_structure = [
  • advanced-ip-blocker/trunk/includes/class-advaipbl-ajax-handler.php

    r3476940 r3481949  
    5252
    5353        if ($success) {
     54            $this->plugin->purge_all_page_caches();
    5455            /* translators: %1$s: IP, %2$s: Username. */
    5556            $this->plugin->log_event(sprintf(__('Threat score for IP %1$s was manually reset by %2$s.', 'advanced-ip-blocker'), $ip, $this->plugin->get_current_admin_username()), 'info');
     
    291292        }
    292293
     294        // --- Verificación del Token API V3 (Servidor Central AIB) ---
     295        if ($provider === 'api_token_v3') {
     296            if (empty($api_key)) {
     297                wp_send_json_error(['message' => __('API Key is missing.', 'advanced-ip-blocker')]);
     298            }
     299
     300            // Llamada al servidor central para verificar el token real
     301            $response = wp_remote_get('https://advaipbl.com/wp-json/aib-api/v3/verify-token', [
     302                'headers' => [
     303                    'Authorization' => 'Bearer ' . $api_key,
     304                    'Accept'        => 'application/json'
     305                ],
     306                'timeout' => 10
     307            ]);
     308
     309            if (is_wp_error($response)) {
     310                wp_send_json_error(['message' => __('Connection failed: ', 'advanced-ip-blocker') . $response->get_error_message()]);
     311            }
     312
     313            $status_code = wp_remote_retrieve_response_code($response);
     314            $body = wp_remote_retrieve_body($response);
     315            $data = json_decode($body, true);
     316
     317            if ($status_code === 200 && isset($data['status']) && in_array($data['status'], ['active', 'connected'], true)) {
     318                wp_send_json_success(['message' => __('API Key is valid and active!', 'advanced-ip-blocker')]);
     319            } else {
     320                $error_msg = $data['message'] ?? __('Invalid or inactive API Key.', 'advanced-ip-blocker');
     321                wp_send_json_error(['message' => $error_msg]);
     322            }
     323            return;
     324        }
     325
    293326        // --- Lógica existente para Geolocalización ---
    294327        $this->plugin->geolocation_manager->set_transient_api_key($provider, $api_key);
     
    303336        }
    304337    }
     338
     339    /**
     340     * AJAX action to get a free API Key from the Central Server automatically.
     341     */
     342    public function ajax_get_free_api_key() {
     343        check_ajax_referer('advaipbl_verify_api_nonce', 'nonce'); // Usamos este nonce existente
     344
     345        if (!current_user_can('manage_options')) {
     346            wp_send_json_error(['message' => __('Unauthorized', 'advanced-ip-blocker')]);
     347        }
     348
     349        $result = $this->plugin->community_manager->register_site();
     350
     351        if (is_wp_error($result)) {
     352            wp_send_json_error(['message' => __('Connection to Central Server failed: ', 'advanced-ip-blocker') . $result->get_error_message()]);
     353        }
     354
     355        if (isset($result['api_token'])) {
     356            wp_send_json_success([
     357                'message' => __('API Key generated and saved successfully!', 'advanced-ip-blocker'),
     358                'api_token_visual' => 'AIB_' . str_repeat('•', 24) . substr($result['api_token'], -4)
     359            ]);
     360        } else {
     361            wp_send_json_error(['message' => __('Failed to generate API Key.', 'advanced-ip-blocker')]);
     362        }
     363    }
     364
    305365    /**
    306366     * Callback de AJAX para gestionar la respuesta al aviso de telemetría.
     
    770830                $skipped_count++; // Duplicate or invalid
    771831            }
     832        }
     833
     834        if ($imported_count > 0) {
     835            $this->plugin->purge_all_page_caches();
    772836        }
    773837
     
    9901054        }
    9911055
     1056        if ($imported_count > 0) {
     1057            $this->plugin->purge_all_page_caches();
     1058        }
     1059
    9921060        // Si Cloudflare está habilitado, desencadenamos una sincronización asíncrona casi inmediata.
    9931061        // Hacemos esto porque sincronizar miles de IPs de forma síncrona aquí podría agotar el tiempo de espera PHP.
  • advanced-ip-blocker/trunk/includes/class-advaipbl-community-manager.php

    r3476940 r3481949  
    66
    77    private $plugin;
    8     private $feed_url = 'https://advaipbl.com/wp-content/uploads/advaipbl-feed/blocklist.json';
     8    private $feed_url_v2 = 'https://advaipbl.com/wp-content/uploads/advaipbl-feed/blocklist.json';
     9    private $feed_url_v3 = 'https://advaipbl.com/wp-json/aib-api/v3/community-blocklist';
    910    private $last_update_option = 'advaipbl_community_last_update';
    1011
     
    1920     */
    2021    public function update_list() {
    21         $response = wp_remote_get($this->feed_url, ['timeout' => 15]);
    22        
    23         if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
     22        $feed_data = false; // Initialize feed_data to store the raw JSON body
     23        $api_token = $this->plugin->options['api_token_v3'] ?? '';
     24
     25        // VÍA V3 (Prioritaria si hay token)
     26        $use_v3 = false;
     27        if (!empty($api_token)) {
     28            $response = wp_remote_get($this->feed_url_v3, [
     29                'headers' => [
     30                    'Authorization' => 'Bearer ' . $api_token,
     31                    'Accept'        => 'application/json'
     32                ],
     33                'timeout' => 30
     34            ]);
     35
     36            $status_code = wp_remote_retrieve_response_code($response);
     37            if (!is_wp_error($response) && $status_code === 200) {
     38                $feed_data = wp_remote_retrieve_body($response);
     39                $use_v3 = true;
     40            } else {
     41                $error_msg = is_wp_error($response) ? $response->get_error_message() : 'HTTP ' . $status_code;
     42                $this->plugin->log_event('AIB Network Sync: V3 failed (' . $error_msg . '), falling back to V2.', 'warning');
     43            }
     44        }
     45
     46        // FALLBACK V2 (Si no hay token o falló V3)
     47        if (!$use_v3) {
     48            $response = wp_remote_get($this->feed_url_v2, [
     49                'timeout' => 30
     50            ]);
     51
     52            if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
     53                $error_msg = is_wp_error($response) ? $response->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code($response);
     54                $this->plugin->log_event('AIB Network list download failed completely. Reason: ' . $error_msg, 'error');
     55               
     56                // Actualizar timestamp para no reintentar de inmediato
     57                update_option(ADVAIPBL_Main::OPTION_COMMUNITY_SYNC_TIME, time());
     58                return false;
     59            }
     60            $feed_data = wp_remote_retrieve_body($response);
     61        }
     62
     63        // Process the downloaded data
     64        if (!$feed_data) {
     65            $this->plugin->log_event('AIB Network list download failed: No data received from V3 or V2.', 'error');
    2466            return false;
    2567        }
    26        
    27         $body = wp_remote_retrieve_body($response);
    28         $data = json_decode($body, true);
     68
     69        $data = json_decode($feed_data, true);
    2970       
    3071        if (!$data || !isset($data['ips']) || !is_array($data['ips'])) {
     72            $this->plugin->log_event('AIB Network list download failed: Invalid data format.', 'error');
    3173            return false;
    3274        }
     
    141183        ];
    142184    }
     185
     186    /**
     187     * Registers the site with the Central Server and gets a V3 API Token.
     188     * Can be called manually via AJAX or automatically during upgrades.
     189     *
     190     * @return array|WP_Error Returns an array with 'api_token' on success, or WP_Error on failure.
     191     */
     192    public function register_site() {
     193        $site_url = home_url();
     194
     195        $response = wp_remote_post('https://advaipbl.com/wp-json/aib-api/v3/register', [
     196            'headers' => [
     197                'Content-Type' => 'application/json',
     198                'Accept'       => 'application/json'
     199            ],
     200            'body' => wp_json_encode(['site_url' => $site_url]),
     201            'timeout' => 15
     202        ]);
     203
     204        if (is_wp_error($response)) {
     205            $this->plugin->log_event('Community Network Registration failed: ' . $response->get_error_message(), 'error');
     206            return new WP_Error('registration_failed', $response->get_error_message());
     207        }
     208
     209        $status_code = wp_remote_retrieve_response_code($response);
     210        $body = json_decode(wp_remote_retrieve_body($response), true);
     211
     212        // Permitir continuar (o al menos loguear info útil) si hay rate-limit o ya registrado
     213        if ($status_code !== 200) {
     214            $error_msg = $body['message'] ?? __('Unknown error during registration.', 'advanced-ip-blocker');
     215            $this->plugin->log_event("Community Network Registration failed (HTTP {$status_code}): {$error_msg}", 'error');
     216           
     217            // If the key was already registered or there are too many requests, we don't abort the entire plugin,
     218            // we just inform. Ideally, for a test environment, the admin should generate
     219            // from their account or wait.
     220            if ($status_code === 429 || (isset($body['code']) && $body['code'] === 'site_already_registered')) {
     221                // For these specific cases, we might not return an error, but rather indicate it's handled.
     222                // However, the function signature expects WP_Error on failure, so we'll return an error.
     223                return new WP_Error('registration_failed_handled', $error_msg, ['status' => $status_code]);
     224            }
     225            return new WP_Error('registration_failed', $error_msg, ['status' => $status_code]);
     226        }
     227
     228        if (isset($body['status']) && $body['status'] === 'success' && !empty($body['api_token'])) {
     229            $options = $this->plugin->options; // Get current options
     230            $options['api_token_v3'] = sanitize_text_field($body['api_token']);
     231            update_option(ADVAIPBL_Main::OPTION_SETTINGS, $options);
     232            $this->plugin->options = $options; // Update memory cache
     233           
     234            return [
     235                'api_token' => $body['api_token']
     236            ];
     237        }
     238
     239        $error_msg = $body['message'] ?? __('Failed to generate API Key.', 'advanced-ip-blocker');
     240        return new WP_Error('registration_failed', $error_msg);
     241    }
    143242}
  • advanced-ip-blocker/trunk/includes/class-advaipbl-main.php

    r3476940 r3481949  
    181181    add_filter('authenticate', [$this, 'check_login_rules'], 20, 3);
    182182
    183     add_action('plugins_loaded', [$this, 'check_database_update']);
     183    add_action('admin_init', [$this, 'check_database_update']);
    184184    $this->add_hooks();
    185185   
     
    260260        add_action('wp_login_failed', [$this, 'registrar_intento_login_fallido']);
    261261        add_action('login_init', [ $this, 'handle_login_page_restriction' ], 1 );
     262        add_action('login_init', [ $this, 'handle_login_geo_restriction' ], 2 );
    262263        add_action('wp_login', [$this, 'auto_whitelist_admin_on_login'], 10, 2);
    263264        add_filter('rest_endpoints', [ $this, 'disable_rest_api_user_endpoints' ] );
     
    321322            add_action('wp_ajax_advaipbl_add_ip_to_whitelist', [$this->ajax_handler, 'ajax_add_ip_to_whitelist']);
    322323            add_action('wp_ajax_advaipbl_verify_api_key', [$this->ajax_handler, 'ajax_verify_api_key']);
     324            add_action('wp_ajax_advaipbl_get_free_api_key', [$this->ajax_handler, 'ajax_get_free_api_key']);
    323325            add_action('wp_ajax_advaipbl_update_geoip_db', [$this->ajax_handler, 'ajax_update_geoip_db']);
    324326            add_action('wp_ajax_advaipbl_get_dashboard_stats', [$this->ajax_handler, 'ajax_get_dashboard_stats']);
     
    377379        }
    378380    }
     381   
     382    /**
     383     * Helper routine to automatically generate V3 API Tokens for users who
     384     * already had the AIB Network activated in older versions.
     385     */
     386    private function auto_migrate_v3_token() {
     387        // Ensure options are freshly loaded since this runs early in init during updates
     388        $this->options = get_option(self::OPTION_SETTINGS, []);
     389
     390        // If the user hasn't opted-in to the community network, do nothing (privacy first)
     391        if (empty($this->options['enable_community_network'])) {
     392            return;
     393        }
     394       
     395        // If they already have a V3 token, do nothing
     396        if (!empty($this->options['api_token_v3'])) {
     397            return;
     398        }
     399
     400        // Trigger the internal site registration to generate keys and fetch the V3 token
     401        if (isset($this->community_manager)) {
     402            $this->community_manager->register_site();
     403           
     404            // Re-load options into memory as register_site() modifies the DB directly sometimes
     405            $this->options = get_option(self::OPTION_SETTINGS, []);
     406        }
     407    }
    379408   
    380409      /**
     
    460489        }
    461490       
    462         if (wp_is_json_request() && strpos($request_uri, '/telemetry/') === false && strpos($request_uri, '/aib-network/') === false && strpos($request_uri, '/aib-scanner/') === false) {
     491        if (wp_is_json_request() && strpos($request_uri, '/telemetry/') === false && strpos($request_uri, '/aib-network/') === false && strpos($request_uri, '/aib-scanner/') === false && strpos($request_uri, '/aib-api/') === false) {
    463492            return;
    464493        }
     
    593622        $wpdb->insert($table_name, $data_to_log);
    594623    }           
     624
     625    /**
     626     * Purges all cache from popular caching plugins.
     627     * Called when security settings or blocklists change to ensure immediate effect.
     628     */
     629    public function purge_all_page_caches() {
     630        // LiteSpeed Cache
     631        if (has_action('litespeed_purge_all')) {
     632            do_action('litespeed_purge_all');
     633        } elseif (defined('LSCWP_V')) {
     634            do_action('litespeed_purge_all_hook');
     635        }
     636
     637        // WP Rocket
     638        if (function_exists('rocket_clean_domain')) {
     639            rocket_clean_domain();
     640        }
     641
     642        // W3 Total Cache
     643        if (function_exists('w3tc_flush_all')) {
     644            w3tc_flush_all();
     645        }
     646
     647        // WP Super Cache
     648        if (function_exists('wp_cache_clear_cache')) {
     649            wp_cache_clear_cache();
     650        }
     651
     652        // WP Fastest Cache
     653        if (isset($GLOBALS['wp_fastest_cache']) && method_exists($GLOBALS['wp_fastest_cache'], 'deleteCache')) {
     654            $GLOBALS['wp_fastest_cache']->deleteCache(true);
     655        }
     656
     657        // Autoptimize
     658        if (class_exists('autoptimizeCache')) {
     659            autoptimizeCache::clearall();
     660        }
     661
     662        // SG Optimizer (SiteGround)
     663        if (function_exists('sg_cachepress_purge_cache')) {
     664            sg_cachepress_purge_cache();
     665        }
     666       
     667        // Kinsta Cache
     668        if (class_exists('Kinsta\Cache')) {
     669            do_action('kinsta_purge_edge_cache');
     670        }
     671       
     672        /* translators: %s: Type of action triggered. */
     673        $this->log_event(__('System page caches successfully flushed after a security update.', 'advanced-ip-blocker'), 'info', 'localhost');
     674    }
    595675     
    596676    /**
     
    20032083        }
    20042084    }
     2085
     2086    /**
     2087     * Handles the geo-restriction of the login page.
     2088     * Hooked to 'login_init' with priority 2.
     2089     */
     2090    public function handle_login_geo_restriction() {
     2091        if ( ! empty( $this->options['login_restrict_countries'] ) && is_array( $this->options['login_restrict_countries'] ) ) {
     2092            $client_ip = $this->get_client_ip();
     2093           
     2094            // Bypass logic: whitelisted IPs are always allowed
     2095            if ( $this->is_whitelisted( $client_ip ) ) {
     2096                return;
     2097            }
     2098
     2099            // Fetch the location of the IP
     2100            $location = $this->geolocation_manager->fetch_location( $client_ip );
     2101            $country_code = $location['country_code'] ?? '';
     2102
     2103            // If the country is not found, or it's not in the allowed list, block it
     2104            if ( empty($country_code) || ! in_array( $country_code, $this->options['login_restrict_countries'], true ) ) {
     2105               
     2106                /* translators: 1: The IP address, 2: The Country Code */
     2107                $this->log_event( sprintf( __( 'Login access denied due to Geo-Blocking restrictions for IP %1$s (%2$s)', 'advanced-ip-blocker' ), $client_ip, empty($country_code) ? 'Unknown' : $country_code ), 'warning', ['ip' => $client_ip] );
     2108               
     2109                // Show a generic access denied message
     2110                wp_die(
     2111                    esc_html__( 'Login access from your location is not allowed.', 'advanced-ip-blocker' ),
     2112                    esc_html__( 'Access Denied', 'advanced-ip-blocker' ),
     2113                    [ 'response' => 403 ]
     2114                );
     2115            }
     2116        }
     2117    }
    20052118   
    20062119    /**
     
    26332746        // Valor por defecto '1.0' para instalaciones muy antiguas
    26342747        $current_db_version = get_option('advaipbl_db_version', '1.0');
     2748        $installed_plugin_ver = get_option('advaipbl_version_installed', '0.0.0');
    26352749       
    26362750        // Si la versión guardada es menor que la versión actual del código ('1.9')
     
    26622776                 $this->log_event('Community list forced update during DB upgrade.', 'info');
    26632777            }
     2778        }
     2779       
     2780        // --- Migraciones de nueva generación basadas en Plugin Version ---
     2781        if ( version_compare($installed_plugin_ver, ADVAIPBL_VERSION, '<') ) {
     2782           
     2783            // Migración a 8.9.0: Auto-Generar Token V3
     2784            if ( version_compare($installed_plugin_ver, '8.9.0', '<') && $installed_plugin_ver !== '0.0.0' ) {
     2785                $this->auto_migrate_v3_token();
     2786            }
     2787
     2788            // Actualizar la versión instalada en la base de datos
     2789            update_option('advaipbl_version_installed', ADVAIPBL_VERSION);
    26642790        }
    26652791       
     
    55575683     * Se ejecuta a través de una tarea de WP-Cron.
    55585684     */
    5559             public function send_telemetry_data() {
    5560         if (empty($this->options['allow_telemetry']) || '1' !== $this->options['allow_telemetry']) {
    5561             return;
    5562         }
    5563 
     5685    /**
     5686     * Genera el payload de telemetría de uso del plugin.
     5687     * @return array
     5688     */
     5689    private function get_telemetry_payload() {
    55645690        global $wpdb;
    55655691        $is_woocommerce_active = class_exists('WooCommerce');
     
    55775703            'is_woo_active'  => $is_woocommerce_active,
    55785704            'geo_provider'   => $this->options['geolocation_provider'] ?? 'N/A',
    5579             'geolocation_method' => $this->options['geolocation_method'] ?? 'api',
     5705            'geolocation_method' => $this->options['geolocation_method'] ?? 'api',
    55805706        ];
    55815707
     
    55895715       
    55905716        // Array de settings completo que refleja todos los módulos principales.
    5591         // Las claves coinciden con las opciones reales para mayor claridad.
    55925717        $telemetry_data['settings'] = [
    55935718            'enable_waf'                  => !empty($this->options['enable_waf']),
     
    55985723            'enable_spamhaus_asn'         => !empty($this->options['enable_spamhaus_asn']),
    55995724            'enable_manual_asn'           => !empty($this->options['enable_manual_asn']),
    5600             'enable_abuseipdb'            => !empty($this->options['enable_abuseipdb']),
     5725            'enable_abuseipdb'            => !empty($this->options['enable_abuseipdb']),
    56015726            'xmlrpc_protection_mode'      => $this->options['xmlrpc_protection_mode'] ?? 'smart',
    56025727            'recaptcha_enable'            => !empty($this->options['recaptcha_enable']),
    56035728            'enable_push_notifications'   => !empty($this->options['enable_push_notifications']),
    5604             'enable_threat_scoring'       => !empty($this->options['enable_threat_scoring']),
    5605             'auto_whitelist_admin'        => !empty($this->options['auto_whitelist_admin']),
     5729            'enable_threat_scoring'       => !empty($this->options['enable_threat_scoring']),
     5730            'auto_whitelist_admin'        => !empty($this->options['auto_whitelist_admin']),
    56065731            'disable_user_enumeration'    => !empty($this->options['disable_user_enumeration']),
    56075732            'prevent_author_scanning'     => !empty($this->options['prevent_author_scanning']),
    56085733            'restrict_login_page'         => !empty($this->options['restrict_login_page']),
    5609             'prevent_login_hinting'       => !empty($this->options['prevent_login_hinting']),
    5610             'enable_email_notifications'  => !empty($this->options['enable_email_notifications']),
    5611             'enable_signature_engine'     => !empty($this->options['enable_signature_engine']),
     5734            'prevent_login_hinting'       => !empty($this->options['prevent_login_hinting']),
     5735            'enable_email_notifications'  => !empty($this->options['enable_email_notifications']),
     5736            'enable_signature_engine'     => !empty($this->options['enable_signature_engine']),
    56125737            'enable_signature_analysis'   => !empty($this->options['enable_signature_analysis']),
    56135738            'enable_signature_blocking'   => !empty($this->options['enable_signature_blocking']),
    5614             'enable_2fa'                  => !empty($this->options['enable_2fa']),
    5615             'enable_xmlrpc_lockdown'      => !empty($this->options['enable_xmlrpc_lockdown']),
     5739            'enable_2fa'                  => !empty($this->options['enable_2fa']),
     5740            'enable_xmlrpc_lockdown'      => !empty($this->options['enable_xmlrpc_lockdown']),
    56165741            'enable_login_lockdown'       => !empty($this->options['enable_login_lockdown']),
    56175742            'enable_404_lockdown'         => !empty($this->options['enable_404_lockdown']),
     
    56215746            'enable_audit_log'            => !empty($this->options['enable_audit_log']),
    56225747            'enable_fim'                  => !empty($this->options['enable_fim']),
    5623 
    5624             'enable_bot_verification'   => !empty($this->options['enable_bot_verification']),
    5625             'enable_geo_challenge'      => !empty($this->options['enable_geo_challenge']),
     5748            'enable_bot_verification'     => !empty($this->options['enable_bot_verification']),
     5749            'enable_geo_challenge'        => !empty($this->options['enable_geo_challenge']),
    56265750            'htaccess_write'              => !empty($this->options['enable_htaccess_write']),
    56275751            'htaccess_sync_ips'           => !empty($this->options['enable_htaccess_ip_blocking']),
     
    56305754            'htaccess_hardening_config'   => !empty($this->options['htaccess_protect_wp_config']),
    56315755            'htaccess_hardening_readme'   => !empty($this->options['htaccess_protect_readme']),
    5632             'cloudflare_enabled'          => !empty($this->options['enable_cloudflare']),
     5756            'cloudflare_enabled'          => !empty($this->options['enable_cloudflare']),
    56335757            'cloudflare_sync_manual'      => !empty($this->options['cf_sync_manual']),
    56345758            'cloudflare_sync_temp'        => !empty($this->options['cf_sync_temporary']),
     
    56645788            'ips_with_active_score'   => (int) $wpdb->get_var("SELECT COUNT(id) FROM {$wpdb->prefix}advaipbl_ip_scores"),
    56655789            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    5666             'active_malicious_signatures' => (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(id) FROM {$wpdb->prefix}advaipbl_malicious_signatures WHERE expires_at > %d", time())),
     5790            'active_malicious_signatures' => (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(id) FROM {$wpdb->prefix}advaipbl_malicious_signatures WHERE expires_at > %d", time())),
    56675791            'geo_challenge_country_count' => count($this->options['geo_challenge_countries'] ?? []),
    5668         ];
     5792        ];
    56695793
    56705794        $server_ip = $this->get_server_ip();
     
    56755799            }
    56765800        }
     5801       
     5802        return $telemetry_data;
     5803    }
     5804
     5805    /**
     5806     * Recopila y envía datos de telemetría anónimos a un endpoint de API REST.
     5807     * Se ejecuta a través de una tarea de WP-Cron.
     5808     */
     5809    public function send_telemetry_data() {
     5810        if (empty($this->options['allow_telemetry']) || '1' !== $this->options['allow_telemetry']) {
     5811            return;
     5812        }
     5813
     5814        // Si tenemos V3 y participamos en la red comunitaria, la telemetría viaja incrustada
     5815        // en 'execute_community_report' para ahorrar envíos. Se anula el envío V2 aislado.
     5816        if (!empty($this->options['api_token_v3']) && !empty($this->options['enable_community_network'])) {
     5817            return;
     5818        }
     5819
     5820        $telemetry_data = $this->get_telemetry_payload();
    56775821
    56785822        $endpoint_url = 'https://advaipbl.com/wp-json/telemetry/v2/submit';
    56795823        $secret_key   = 'yV.vZRp|g6E{zJ,DI7WcMIiGDejmH($$~<0-I$$Bd7Y) D5Z65M/*P:h>w:/E<D<';
     5824
     5825        $headers = [
     5826            'Content-Type'    => 'application/json',
     5827            'X-Telemetry-Key' => $secret_key
     5828        ];
     5829
     5830        // Incluir Token V3 como fallback si llegara aquí, aunque V3 debe saltar arriba.
     5831        if (!empty($this->options['api_token_v3'])) {
     5832            $headers['Authorization'] = 'Bearer ' . $this->options['api_token_v3'];
     5833        }
    56805834
    56815835        wp_remote_post($endpoint_url, [
    56825836            'timeout'   => 15,
    56835837            'blocking'  => false,
    5684             'headers'   => [
    5685                 'Content-Type'    => 'application/json',
    5686                 'X-Telemetry-Key' => $secret_key
    5687             ],
     5838            'headers'   => $headers,
    56885839            'body'      => wp_json_encode($telemetry_data),
    56895840        ]);
     
    57245875                'api_key_ipapicom', 'api_key_ipstackcom', 'api_key_ipinfocom',
    57255876                'api_key_ip_apicom', 'maxmind_license_key', 'push_webhook_urls',
    5726                 'cf_api_token', 'cf_zone_id', 'abuseipdb_api_key'
     5877                'cf_api_token', 'cf_zone_id', 'abuseipdb_api_key', 'api_token_v3'
    57275878            ];
    57285879            foreach ($sensitive_keys as $sensitive_key) {
     
    69337084public function maybe_redirect_to_wizard() {
    69347085    if ( get_option( 'advaipbl_run_setup_wizard' ) ) {
    6935         // Eliminamos la opción para que no se redirija en bucle
     7086       
     7087        // Evitar bucles infinitos en servidores con Object Caches muy lentos (ej. LiteSpeed)
     7088        // comprobando si YA estamos en la ruta de destino.
     7089        $is_already_on_wizard = ( isset( $_GET['page'] ) && $_GET['page'] === 'advaipbl-setup-wizard' );
     7090       
    69367091        delete_option( 'advaipbl_run_setup_wizard' );
    69377092       
    6938         if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || isset( $_GET['activate-multi'] ) ) {
    6939             // Si es una activación masiva, volvemos a añadir la opción para que se muestre el aviso.
    6940             add_option( 'advaipbl_run_setup_wizard', true );
     7093        if ( $is_already_on_wizard || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || isset( $_GET['activate-multi'] ) ) {
     7094            // Si es activación masiva, volvemos a poner para mostrar mensaje.
     7095            if ( isset( $_GET['activate-multi'] ) ) {
     7096                add_option( 'advaipbl_run_setup_wizard', true );
     7097            }
    69417098            return;
    69427099        }
     7100
    69437101        wp_safe_redirect( admin_url( 'admin.php?page=advaipbl-setup-wizard' ) );
    69447102        exit;
     
    72147372}
    72157373
    7216 /**
     7374    /**
    72177375     * Ejecuta el envío de reportes a la API central.
    72187376     */
    72197377    public function execute_community_report() {
    7220         // 1. Verificar si el usuario participa
     7378        // 1. Obtener el lote de bloqueos (solo si participa en AIB Network)
     7379        $payload = [
     7380            'site_hash' => hash('sha256', home_url()),
     7381            'version'   => ADVAIPBL_VERSION,
     7382            'reports'   => []
     7383        ];
     7384       
     7385        global $wpdb;
    72217386        if ( empty($this->options['enable_community_network']) ) {
    7222             // Si no participa, limpiamos la tabla local para no acumular basura
    7223             global $wpdb;
     7387            // Si no participa en red, no enviamos reportes de amenazas (pero la telemetria puede enviarse abajo)
    72247388            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    72257389            $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}advaipbl_pending_reports");
    7226             return;
    7227         }
    7228 
    7229         // 2. Obtener el lote
    7230         $payload = $this->reporter_manager->get_batch_for_api(100); // 100 reportes por envío
    7231        
    7232         if ( empty($payload['reports']) ) {
    7233             return; // Nada que enviar
    7234         }
    7235 
    7236         // 3. Enviar a la API (V2 con strict anti-poisoning)
    7237         $api_url = 'https://advaipbl.com/wp-json/aib-network/v2/report';
    7238        
    7239         // La API V2 requiere el site_hash explicitamente en el root del JSON para aplicar el Rate Limiting local
     7390        } else {
     7391            $gathered_payload = $this->reporter_manager->get_batch_for_api(100);
     7392            if (!empty($gathered_payload)) {
     7393                $payload = $gathered_payload;
     7394            }
     7395        }
     7396
     7397        $has_reports = !empty($payload['reports']);
     7398       
     7399        $has_v3_token = !empty($this->options['api_token_v3']);
     7400
     7401        // 3. Fallback a V2 si no hay token V3 (V2 no agrupa telemetría, aborta si no hay reportes)
     7402        if (!$has_v3_token && !$has_reports) {
     7403            return;
     7404        }
     7405
     7406        // Asegurar site_hash para Rate Limiting V2 o info cruda
    72407407        $payload_data = $payload;
    72417408        $payload_data['site_hash'] = $payload['site_hash'] ?? hash('sha256', get_site_url());
    72427409
     7410        // 4. Parámetros de envío
     7411        if ($has_v3_token) {
     7412            $api_url = 'https://advaipbl.com/wp-json/aib-api/v3/report';
     7413            $headers = [
     7414                'Content-Type'  => 'application/json',
     7415                'Authorization' => 'Bearer ' . $this->options['api_token_v3']
     7416            ];
     7417           
     7418            // En V3 inyectamos la telemetría general en el mismo paquete para ahorrar recursos del cliente y servidor
     7419            if (!empty($this->options['allow_telemetry']) && '1' === $this->options['allow_telemetry']) {
     7420                $payload_data['telemetry'] = $this->get_telemetry_payload();
     7421            } else {
     7422                $payload_data['telemetry'] = [];
     7423            }
     7424           
     7425            // Si después de intentarlo no hay ni amenazas locales ni métricas permitidas, no saturamos la red
     7426            if (!$has_reports && empty($payload_data['telemetry'])) {
     7427                return;
     7428            }
     7429           
     7430        } else {
     7431            // Configuración V2 (Asegurar que nunca llegamos aquí si !$has_reports por la comprobación anterior)
     7432            $api_url = 'https://advaipbl.com/wp-json/aib-network/v2/report';
     7433            $headers = [
     7434                'Content-Type'    => 'application/json',
     7435                'X-AIB-Site-Hash' => $payload_data['site_hash'],
     7436            ];
     7437        }
     7438
    72437439        $response = wp_remote_post( $api_url, [
    7244             'body'    => wp_json_encode($payload_data),
    7245             'headers' => [
    7246                 'Content-Type' => 'application/json',
    7247                 'X-AIB-Site-Hash' => $payload_data['site_hash'], // Cabecera de autenticación simple
    7248             ],
    7249             'timeout' => 10,
    7250             'blocking' => false // "Fire and forget" para no ralentizar el cron
     7440            'body'     => wp_json_encode($payload_data),
     7441            'headers'  => $headers,
     7442            'timeout'  => 5,
     7443            'blocking' => false
    72517444        ]);
    7252 
    7253         if ( is_wp_error($response) ) {
    7254             // Loguear error silenciosamente para debug
    7255             // error_log('AIB Network Report Failed: ' . $response->get_error_message());
    7256         }
    72577445    }
    72587446
  • advanced-ip-blocker/trunk/includes/class-advaipbl-settings-manager.php

    r3464093 r3481949  
    4141    add_settings_field('advaipbl_show_admin_bar_menu', __( 'Admin Bar Menu', 'advanced-ip-blocker' ), [$this, 'switch_field_callback'], $page, 'advaipbl_general_settings_section', ['name'  => 'show_admin_bar_menu', 'label' => __( 'Show security menu in the WordPress admin bar', 'advanced-ip-blocker' )]);
    4242   
     43
     44
    4345    add_settings_section('advaipbl_general_settings_section', null, null, $page);
    4446 add_settings_field(
     
    229231// --- AIB COMMUNITY NETWORK SECTION ---
    230232        add_settings_section('advaipbl_community_network_section', null, null, $page);
     233
     234        // --- V3 API Connection ---
     235        add_settings_field(
     236            'advaipbl_api_connection_status',
     237            __('AIB Account Connection', 'advanced-ip-blocker'),
     238            [$this, 'api_connection_status_callback'],
     239            $page,
     240            'advaipbl_community_network_section'
     241        );
     242
     243        add_settings_field(
     244            'advaipbl_api_token_v3',
     245            __('API Token', 'advanced-ip-blocker'),
     246            [$this, 'api_token_field_callback'],
     247            $page,
     248            'advaipbl_community_network_section',
     249            [
     250                'name' => 'api_token_v3',
     251                'description' => __('Connecting to the Advanced IP Blocker cloud network gives you access to the community blocklist and deep site scanning.', 'advanced-ip-blocker'),
     252            ]
     253        );
    231254
    232255        add_settings_field(
     
    475498    );
    476499    add_settings_field('advaipbl_restrict_login_page', __( 'Whitelist Login Access', 'advanced-ip-blocker' ), [ $this, 'restrict_login_page_callback' ], $page, 'advaipbl_advanced_login_section');
     500       
     501    add_settings_field('advaipbl_login_restrict_countries', __( 'Whitelist Login Countries', 'advanced-ip-blocker' ), [ $this, 'geoblock_countries_callback' ], $page, 'advaipbl_advanced_login_section', [
     502        'type' => 'login_restrict',
     503        'description' => __('Select one or more countries that are allowed to access wp-login.php. If empty, all countries are allowed.', 'advanced-ip-blocker')
     504    ]);
     505   
    477506    add_settings_section('advaipbl_advanced_xmlrpc_section', null, null, $page);   
    478507    add_settings_field('advaipbl_xmlrpc_protection_mode', __('XML-RPC Protection Mode', 'advanced-ip-blocker'), [$this, 'xmlrpc_protection_mode_callback'], $page, 'advaipbl_advanced_xmlrpc_section');
     
    10211050            'xmlrpc_protection_mode', 'geolocation_method', 'trusted_proxies', 'abuseipdb_api_key', 'abuseipdb_action',
    10221051            'cf_api_token', 'cf_zone_id', 'community_blocking_action', 'scan_frequency', 'scan_notification_email',
    1023             'fim_alert_email'
     1052            'fim_alert_email', 'api_token_v3'
    10241053        ];
    10251054
     
    10431072        }
    10441073       
     1074        // V3 API Token Validation (only if changed manually)
     1075        if (isset($new_input['api_token_v3']) && $new_input['api_token_v3'] !== ($this->plugin->options['api_token_v3'] ?? '')) {
     1076            if (!empty($new_input['api_token_v3'])) {
     1077                $response = wp_remote_get('https://advaipbl.com/wp-json/aib-api/v3/verify-token', [
     1078                    'headers' => [
     1079                        'Authorization' => 'Bearer ' . $new_input['api_token_v3'],
     1080                        'Accept'        => 'application/json'
     1081                    ],
     1082                    'timeout' => 15
     1083                ]);
     1084               
     1085                if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
     1086                    add_settings_error('advaipbl_settings_messages', 'invalid_v3_token', __('Invalid AIB Cloud Network Token. Connection failed.', 'advanced-ip-blocker'), 'error');
     1087                    $new_input['api_token_v3'] = $this->plugin->options['api_token_v3'] ?? ''; // Revert to previous or empty
     1088                }
     1089            }
     1090        }
     1091
    10451092        if (isset($input['geoblock_countries'])) {
    10461093            $countries = (array) $input['geoblock_countries'];
     
    10581105            $new_input['geo_challenge_countries'] = [];
    10591106        }
     1107
     1108        if (isset($input['login_restrict_countries'])) {
     1109            $countries = (array) $input['login_restrict_countries'];
     1110            $all_country_codes = array_keys($this->plugin->get_country_list());
     1111            $new_input['login_restrict_countries'] = array_values(array_intersect($countries, $all_country_codes));
     1112        } else {
     1113            $new_input['login_restrict_countries'] = [];
     1114        }
    10601115       
    10611116        if (isset($input['recaptcha_score_threshold'])) {
    10621117            $new_input['recaptcha_score_threshold'] = $this->plugin->sanitize_score_threshold($input['recaptcha_score_threshold']);
    10631118        }
     1119       
     1120        // Purge page caches when security settings are updated
     1121        $this->plugin->purge_all_page_caches();
    10641122       
    10651123        return $new_input;
     
    14091467        }
    14101468    }
     1469
     1470    /**
     1471     * Callback para mostrar el estado de la conexión con la API V3.
     1472     */
     1473    public function api_connection_status_callback() {
     1474        $token = $this->plugin->options['api_token_v3'] ?? '';
     1475       
     1476        if (empty($token)) {
     1477            echo '<span class="dashicons dashicons-dismiss" style="color: #d63638;"></span> <strong style="color: #d63638;">' . esc_html__('Not Connected', 'advanced-ip-blocker') . '</strong>';
     1478            echo '<p class="description">' . esc_html__('Get a Free API Key below to connect your site to the AIB Cloud Network.', 'advanced-ip-blocker') . '</p>';
     1479        } else {
     1480            echo '<div id="advaipbl-api-status-container">';
     1481            echo '<span class="dashicons dashicons-yes-alt" style="color: #00a32a;"></span> <strong style="color: #00a32a;">' . esc_html__('Connected', 'advanced-ip-blocker') . '</strong>';
     1482           
     1483            // Si tuviéramos el tipo de plan guardado, lo mostraríamos aquí. Por ahora, asumimos conectado.
     1484            echo ' <span class="advaipbl-badge advaipbl-badge-free" style="margin-left:5px;">' . esc_html__('AIB Cloud Network', 'advanced-ip-blocker') . '</span>';
     1485           
     1486            echo '<p class="description" style="margin-top:5px;">';
     1487            echo '<button type="button" class="button button-secondary button-small" id="advaipbl-verify-api-token">' . esc_html__('Verify Connection', 'advanced-ip-blocker') . '</button>';
     1488            echo '<span id="advaipbl-api-verification-result" style="margin-left: 8px;"></span>';
     1489            echo '</p>';
     1490            echo '</div>';
     1491        }
     1492    }
     1493
     1494    /**
     1495     * Callback específico para el campo del Token API con ofuscación.
     1496     */
     1497    public function api_token_field_callback($args) {
     1498        $name = $args['name'];
     1499        $value = isset($this->plugin->options[$name]) ? $this->plugin->options[$name] : '';
     1500       
     1501        // Ofuscar si existe
     1502        $display_val = $value;
     1503        if (!empty($value) && strlen($value) > 8) {
     1504            $display_val = substr($value, 0, 4) . str_repeat('•', 24) . substr($value, -4);
     1505        }
     1506
     1507        echo '<div style="display: flex; gap: 10px; position: relative; align-items: center;">';
     1508       
     1509        echo '<div style="display: flex; gap: 10px; max-width: 400px; position: relative; align-items: center;">';
     1510       
     1511        // Campo visible al usuario (puede estar ofuscado si ya hay valor)
     1512        echo '<input type="text" id="advaipbl_' . esc_attr($name) . '_display" class="regular-text" style="font-family: monospace;" ';
     1513        if (!empty($value)) {
     1514            echo 'value="' . esc_attr($display_val) . '" disabled';
     1515            echo '>';
     1516            // Campo oculto real que se enviará en el formulario POST sólo si no se edita
     1517            echo '<input type="hidden" name="' . esc_attr(ADVAIPBL_Main::OPTION_SETTINGS) . '[' . esc_attr($name) . ']" id="advaipbl_' . esc_attr($name) . '" value="' . esc_attr($value) . '">';
     1518            // Botón para editar
     1519            echo '<button type="button" class="button" id="advaipbl-edit-api-token" title="' . esc_attr__('Edit API Key', 'advanced-ip-blocker') . '"><span class="dashicons dashicons-edit" style="margin-top: 2px;"></span></button>';
     1520        } else {
     1521             // Si no hay valor, lo mostramos normal como input type="text" pero que envía
     1522            echo 'name="' . esc_attr(ADVAIPBL_Main::OPTION_SETTINGS) . '[' . esc_attr($name) . ']" id="advaipbl_' . esc_attr($name) . '" value="" placeholder="AIB_xxxxxxxxxxxxxxxxxxxxxxxxxx">';
     1523             // Botón mágico para generar clave
     1524            echo '<button type="button" class="button button-primary" id="advaipbl-get-api-token" title="' . esc_attr__('Get a Free VIP Key instantly', 'advanced-ip-blocker') . '">' . esc_html__('Get Free Key', 'advanced-ip-blocker') . '</button>';
     1525            echo '<span class="spinner" id="advaipbl-api-token-spinner" style="float:none; margin:0;"></span>';
     1526        }
     1527
     1528        echo '</div>';
     1529       
     1530        if (isset($args['description']) && empty($value)) {
     1531            echo '<p class="description" style="margin-top:5px;">' . wp_kses_post($args['description']) . '</p>';
     1532        } else if (!empty($value)) {
     1533            echo '<p class="description" style="margin-top:5px;">' . esc_html__('Your API key is hidden for security.', 'advanced-ip-blocker') . '</p>';
     1534        }
     1535
     1536        // Script para manejar la edición y la validación
     1537        ?>
     1538        <script>
     1539        jQuery(document).ready(function($) {
     1540            $('#advaipbl-edit-api-token').on('click', function(e) {
     1541                e.preventDefault();
     1542                var $display = $('#advaipbl_<?php echo esc_js($name); ?>_display');
     1543                var $hidden = $('#advaipbl_<?php echo esc_js($name); ?>');
     1544               
     1545                // Convert display to a real input field connected to POST
     1546                $display.prop('disabled', false)
     1547                        .val('')
     1548                        .attr('name', '<?php echo esc_js(ADVAIPBL_Main::OPTION_SETTINGS); ?>[<?php echo esc_js($name); ?>]')
     1549                        .focus();
     1550               
     1551                // Remove hidden field and button
     1552                $hidden.remove();
     1553                $(this).remove();
     1554            });
     1555
     1556            // Validación simple del lado del cliente antes de enviar
     1557            $('form').on('submit', function() {
     1558                var apiTokenInput = $('input[name="<?php echo esc_js(ADVAIPBL_Main::OPTION_SETTINGS); ?>[<?php echo esc_js($name); ?>]"]');
     1559                if (apiTokenInput.length && !apiTokenInput.prop('disabled')) {
     1560                    var val = apiTokenInput.val().trim();
     1561                    if (val !== '' && !val.startsWith('AIB_')) {
     1562                        alert('<?php echo esc_js(__('Invalid API Key format. It should start with AIB_.', 'advanced-ip-blocker')); ?>');
     1563                        apiTokenInput.focus();
     1564                        return false;
     1565                    }
     1566                }
     1567                return true;
     1568            });
     1569        });
     1570        </script>
     1571        <?php
     1572    }
    14111573    /**
    14121574 * Muestra el campo <select> para el modo de protección XML-RPC.
     
    14501612        $type = $args['type'] ?? 'geoblock'; // Por defecto es geoblock para retrocompatibilidad
    14511613       
    1452         $option_name = ($type === 'geo_challenge') ? 'geo_challenge_countries' : 'geoblock_countries';
     1614        if ($type === 'geo_challenge') {
     1615            $option_name = 'geo_challenge_countries';
     1616            $placeholder_text = __('Search for a country to challenge...', 'advanced-ip-blocker');
     1617        } elseif ($type === 'login_restrict') {
     1618            $option_name = 'login_restrict_countries';
     1619            $placeholder_text = __('Search for an allowed country...', 'advanced-ip-blocker');
     1620        } else {
     1621            $option_name = 'geoblock_countries';
     1622            $placeholder_text = __('Search for a country to block...', 'advanced-ip-blocker');
     1623        }
     1624       
    14531625        $select_id = 'advaipbl_' . $option_name;
    1454         $placeholder_text = ($type === 'geo_challenge')
    1455             ? __('Search for a country to challenge...', 'advanced-ip-blocker')
    1456             : __('Search for a country to block...', 'advanced-ip-blocker');
    14571626
    14581627        $selected_countries = $this->plugin->options[$option_name] ?? [];
     
    14661635            <?php endforeach; ?>
    14671636        </select>
    1468         <p class="description">
    1469             <?php esc_html_e( 'Select one or more countries. Type in the box to search.', 'advanced-ip-blocker' ); ?>
    1470         </p>
     1637        <?php if (isset($args['description'])) : ?>
     1638            <p class="description"><?php echo wp_kses_post($args['description']); ?></p>
     1639        <?php else : ?>
     1640            <p class="description">
     1641                <?php esc_html_e( 'Select one or more countries. Type in the box to search.', 'advanced-ip-blocker' ); ?>
     1642            </p>
     1643        <?php endif; ?>
    14711644        <?php
    14721645    }
  • advanced-ip-blocker/trunk/includes/class-advaipbl-site-scanner.php

    r3469306 r3481949  
    237237        $site_hash = hash('sha256', get_site_url());
    238238       
     239        $headers = [
     240            'Content-Type' => 'application/json',
     241            'X-AIB-Site-Hash' => $site_hash
     242        ];
     243
     244        // Incluir V3 Token y saltar a endpoint V3 si existe
     245        if (!empty($this->plugin->options['api_token_v3'])) {
     246            $api_url = 'https://advaipbl.com/wp-json/aib-api/v3/scanner/check';
     247            $headers['Authorization'] = 'Bearer ' . $this->plugin->options['api_token_v3'];
     248        }
     249
    239250        $response = wp_remote_post( $api_url, [
    240251            'body'    => wp_json_encode( $payload ),
    241             'headers' => [
    242                 'Content-Type' => 'application/json',
    243                 'X-AIB-Site-Hash' => $site_hash
    244             ],
     252            'headers' => $headers,
    245253            'timeout' => 15 // Subimos un poco el timeout por si la lista es larga
    246254        ]);
     
    290298        $api_check_url = 'https://advaipbl.com/wp-json/aib-scanner/v2/check-ip?ip=' . $server_ip;
    291299        $site_hash = hash('sha256', get_site_url());
     300
     301        $headers = [
     302            'X-AIB-Site-Hash' => $site_hash
     303        ];
     304
     305        // Incluir V3 Token y saltar a endpoint V3 si existe
     306        if (!empty($this->plugin->options['api_token_v3'])) {
     307            $api_check_url = 'https://advaipbl.com/wp-json/aib-api/v3/scanner/check-ip?ip=' . $server_ip;
     308            $headers['Authorization'] = 'Bearer ' . $this->plugin->options['api_token_v3'];
     309        }
    292310       
    293311        $response = wp_remote_get($api_check_url, [
    294312            'timeout' => 5,
    295             'headers' => [
    296                 'X-AIB-Site-Hash' => $site_hash
    297             ]
     313            'headers' => $headers
    298314        ]);
    299315       
  • advanced-ip-blocker/trunk/js/admin-settings.js

    r3455663 r3481949  
    561561                });
    562562        });
     563
     564        // Handler específico para el Token API V3
     565        $('body').on('click', '#advaipbl-verify-api-token', function (e) {
     566            e.preventDefault();
     567            const $button = $(this);
     568            // El selector del token V3 ahora usa _display o el input oculto tras darle a editar
     569            let apiKey = $('#advaipbl_api_token_v3_display').val() || $('#advaipbl_api_token_v3').val();
     570            const $statusContainer = $button.closest('.advaipbl-status-indicator');
     571            const texts = adminData.text || {};
     572
     573            $button.text(texts.verifying_api || 'Verifying...').prop('disabled', true);
     574
     575            if (!apiKey || apiKey.indexOf('•') !== -1) { // If it's obfuscated, we just grab the real one
     576                apiKey = $('#advaipbl_api_token_v3').val();
     577            }
     578
     579            if (!apiKey) {
     580                showAdminNotice(texts.enter_api_key || 'Please enter an API key first.', 'error');
     581                $button.text('Verify Connection').prop('disabled', false);
     582                return;
     583            }
     584
     585            $.post(ajaxurl, {
     586                action: 'advaipbl_verify_api_key',
     587                nonce: adminData.nonces.verify_api,
     588                provider: 'api_token_v3',
     589                api_key: apiKey
     590            })
     591                .done(function (response) {
     592                    const $resultSpan = $('#advaipbl-api-verification-result');
     593                    if (response.success) {
     594                        $resultSpan.text(response.data.message).css({ 'color': 'green', 'font-weight': 'bold' });
     595                        $button.text('Verify Connection');
     596                    } else {
     597                        $resultSpan.text('Error: ' + response.data.message).css({ 'color': 'red', 'font-weight': 'bold' });
     598                        $button.text('Verify Connection');
     599                    }
     600                })
     601                .fail(function () {
     602                    const $resultSpan = $('#advaipbl-api-verification-result');
     603                    $resultSpan.text(texts.ajax_error || 'AJAX error.').css({ 'color': 'red', 'font-weight': 'bold' });
     604                    $button.text('Verify Connection');
     605                })
     606                .always(function () {
     607                    $button.prop('disabled', false);
     608                });
     609        });
     610
     611        // Handler para generar una clave gratuita (In-App Registration)
     612        $('body').on('click', '#advaipbl-get-api-token', function (e) {
     613            e.preventDefault();
     614            const $button = $(this);
     615            const $spinner = $('#advaipbl-api-token-spinner');
     616            const originalText = $button.text();
     617
     618            $button.prop('disabled', true).text('Generating...');
     619            $spinner.addClass('is-active');
     620
     621            $.post(ajaxurl, {
     622                action: 'advaipbl_get_free_api_key',
     623                nonce: adminData.nonces.verify_api, // Reutilizamos el nonce de ajustes generales
     624                _t: Date.now() // Cache buster para Cloudflare
     625            })
     626                .done(function (response) {
     627                    if (response.success) {
     628                        showAdminNotice(response.data.message, 'success');
     629                        // Actualizar el valor visual localmente sin recargar
     630                        const newHtml = `
     631                        <input type="text" id="advaipbl_api_token_v3_display" class="regular-text" style="font-family: monospace;" value="${response.data.api_token_visual}" disabled>
     632                        <input type="hidden" name="advaipbl_settings[api_token_v3]" id="advaipbl_api_token_v3" value="${response.data.api_token}">
     633                        <button type="button" class="button" id="advaipbl-edit-api-token" title="Edit API Key"><span class="dashicons dashicons-edit" style="margin-top: 2px;"></span></button>
     634                    `;
     635                        // Recargar suavemente para asegurar que todo WordPress capte el Token V3 en backend sin error
     636                        // Eliminado reload automático para no perder el token sin guardar.
     637                        // setTimeout(() => window.location.reload(), 2000);
     638                       
     639                        // En su lugar, actualizamos el texto de validación también
     640                        $('#advaipbl-api-verification-result').html('<span style="color: green;">' + (adminData.text.api_key_generated || 'API Key Generated!') + '</span>');
     641                       
     642                        // Localizar el indicador de estado de la red AIB y cambiarlo a activo visualmente
     643                        var $statusIndicator = $('.advaipbl-status-indicator');
     644                        if($statusIndicator.length) {
     645                             $statusIndicator.css({
     646                                 'background': '#f0f6fc',
     647                                 'border': '1px solid #cce5ff'
     648                             });
     649                             $statusIndicator.html('<span class="dashicons dashicons-cloud-saved" style="color: #2271b1; vertical-align: middle;"></span> <strong>' + (adminData.text.protection_active || 'Protection Active:') + '</strong> <span style="margin-left:5px;">Connected (Waiting for first sync)</span>');
     650                        } else {
     651                             // Si estaba 'Not Connected', no existe el div con esa clase, así que lo inyectamos
     652                             var $card = $button.closest('.advaipbl-card');
     653                             var $header = $card.find('h3').first();
     654                             $header.after('<div class="advaipbl-status-indicator" style="margin-bottom: 15px; padding: 10px; background: #f0f6fc; border: 1px solid #cce5ff; border-radius: 4px;"><span class="dashicons dashicons-cloud-saved" style="color: #2271b1; vertical-align: middle;"></span> <strong>' + (adminData.text.protection_active || 'Protection Active:') + '</strong> <span style="margin-left:5px;">Connected (Waiting for first sync)</span></div>');
     655                             // Si hay un texto de 'No Conectado' previo en la tabla, podríamos querer ocultarlo
     656                             $card.find('td:contains("Not Connected"), td:contains("No Conectado")').html('<span style="color:green; font-weight:bold;">' + (adminData.text.connected || 'Connected') + '</span>');
     657                        }
     658                    } else {
     659                        showAdminNotice('Error: ' + response.data.message, 'error');
     660                        $button.prop('disabled', false).text(originalText);
     661                        $spinner.removeClass('is-active');
     662                    }
     663                })
     664                .fail(function () {
     665                    showAdminNotice(adminData.text.ajax_error || 'AJAX error.', 'error');
     666                    $button.prop('disabled', false).text(originalText);
     667                    $spinner.removeClass('is-active');
     668                });
     669        });
    563670    }
    564671
  • advanced-ip-blocker/trunk/languages/advanced-ip-blocker-es_ES.po

    r3471934 r3481949  
    55"blocker\n"
    66"POT-Creation-Date: 2025-07-22 14:47+0200\n"
    7 "PO-Revision-Date: 2026-03-01 08:26+0100\n"
     7"PO-Revision-Date: 2026-03-11 11:29+0100\n"
    88"Last-Translator: \n"
    99"Language-Team: \n"
     
    15901590msgid "ASN blocklist saved successfully."
    15911591msgstr "Lista de bloqueo de ASN guardada exitosamente."
     1592
     1593#: includes/class-advaipbl-settings-manager.php:501
     1594msgid "Whitelist Login Countries"
     1595msgstr "Países de inicio de sesión en la lista blanca"
     1596
     1597#: includes/class-advaipbl-settings-manager.php:1210
     1598msgid ""
     1599"Select one or more countries that are allowed to access wp-login.php. If "
     1600"empty, all countries are allowed."
     1601msgstr ""
     1602"Seleccione uno o más países con permiso para acceder a wp-login.php. Si deja "
     1603"el campo vacío, se permite el acceso a todos los países."
     1604
     1605#: includes/class-advaipbl-settings-manager.php:1434
     1606msgid "AIB Account Connection"
     1607msgstr "Conexión de cuenta AIB"
     1608
     1609#: includes/class-advaipbl-settings-manager.php:1435
     1610msgid "Not Connected"
     1611msgstr "No Conectado"
     1612
     1613#: includes/class-advaipbl-settings-manager.php:1436
     1614msgid "Get a Free API Key below to connect your site to the AIB Cloud Network."
     1615msgstr ""
     1616"Obtenga una clave API gratuita a continuación para conectar su sitio a la "
     1617"red en la nube de AIB."
     1618
     1619#: includes/class-advaipbl-settings-manager.php:1437
     1620msgid "API Token"
     1621msgstr "API Token"
     1622
     1623#: includes/class-advaipbl-settings-manager.php:1438
     1624msgid "Get Free Key"
     1625msgstr "Obtener clave gratis"
     1626
     1627#: includes/class-advaipbl-settings-manager.php:1439
     1628msgid ""
     1629"Connecting to the Advanced IP Blocker cloud network gives you access to the "
     1630"community blocklist and deep site scanning."
     1631msgstr ""
     1632"Al conectarse a la red en la nube de Advanced IP Blocker, tendrá acceso a la "
     1633"lista de bloqueo de la comunidad y al escaneo profundo del sitio."
    15921634
    15931635#: includes/class-advaipbl-main.php:3078
  • advanced-ip-blocker/trunk/languages/advanced-ip-blocker.pot

    r3476940 r3481949  
    44msgid ""
    55msgstr ""
    6 "Project-Id-Version: Advanced IP Blocker 8.8.9\n"
     6"Project-Id-Version: Advanced IP Blocker 8.9.0\n"
    77"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/advanced-ip-blocker\n"
    88"POT-Creation-Date: 2025-07-22 14:47+0200\n"
     
    14151415msgstr ""
    14161416
     1417#: includes/class-advaipbl-settings-manager.php:501
     1418msgid "Whitelist Login Countries"
     1419msgstr ""
     1420
     1421#: includes/class-advaipbl-settings-manager.php:1210
     1422msgid "Select one or more countries that are allowed to access wp-login.php. If empty, all countries are allowed."
     1423msgstr ""
     1424
     1425#: includes/class-advaipbl-settings-manager.php:1434
     1426msgid "AIB Account Connection"
     1427msgstr ""
     1428
     1429#: includes/class-advaipbl-settings-manager.php:1435
     1430msgid "Not Connected"
     1431msgstr ""
     1432
     1433#: includes/class-advaipbl-settings-manager.php:1436
     1434msgid "Get a Free API Key below to connect your site to the AIB Cloud Network."
     1435msgstr ""
     1436
     1437#: includes/class-advaipbl-settings-manager.php:1437
     1438msgid "API Token"
     1439msgstr ""
     1440
     1441#: includes/class-advaipbl-settings-manager.php:1438
     1442msgid "Get Free Key"
     1443msgstr ""
     1444
     1445#: includes/class-advaipbl-settings-manager.php:1439
     1446msgid "Connecting to the Advanced IP Blocker cloud network gives you access to the community blocklist and deep site scanning."
     1447msgstr ""
     1448
    14171449#: includes/class-advaipbl-main.php:3078
    14181450#, php-format
  • advanced-ip-blocker/trunk/readme.txt

    r3476940 r3481949  
    66Requires at least: 6.7
    77Tested up to: 6.9
    8 Stable tag: 8.8.9
     8Stable tag: 8.9.0
    99Requires PHP: 8.1
    1010License: GPLv2 or later
     
    2121
    2222**Key Features:**
     23*   **(NEW) AIB Cloud Network V3:** Upgrade to the next-generation distributed threat intelligence network. The new API V3 provides secure, individual API Keys per site, drastically improving synchronization reliability, threat telemetry, and global network stability.
     24*   **(NEW) Whitelist Login Countries:** Take absolute control over administrative access. Easily restrict your WordPress login page and XML-RPC to only allow connections from specific, whitelisted countries, instantly blocking unauthorized foreign login attempts.
    2325*   **(IMPROVED) Bulk Import/Export for Blocked IPs & Whitelist:** Seamlessly import massive lists of IPs via CSV or manual entry. The system now features a bulletproof "Bulk Import" type, strict duration inheritance, and intelligent conflict resolution.
    2426*   **(NEW) Internal Security & Forensics:** A complete audit suite solely for WordPress. Track every sensitive event (plugin installs, settings changes, user logins) and monitor your critical files for unauthorized modifications with the integrated File Integrity Monitor.
     
    224226== Changelog ==
    225227
     228= 8.9.0 =
     229*   **NEW MAJOR FEATURE:** AIB Cloud Network V3. We've completely overhauled our Community Defense Network infrastructure. The new API V3 introduces secure, individual API Keys for every connected site, drastically improving synchronization reliability, security telemetry, and overall network stability.
     230*   **NEW FEATURE:** Whitelist Login Countries. A highly requested feature! You can now explicitly select which countries are allowed to access your WordPress login page (`wp-login.php`) and XML-RPC, automatically blocking all other nations.
     231*   **ENHANCEMENT:** Upgraded the "Verify Connection" UI for the Cloud Network to provide instant, inline diagnostic feedback (success/error) without page reloads.
     232*   **CRITICAL FIX:** Resolved an issue where verifying an API key could trigger local 404 block rules due to an incorrect HTTP request method (POST instead of GET).
     233*   **ROBUSTNESS:** Improved plugin uninstallation logic. When using the "Delete All Data" option, the plugin now seamlessly unregisters the local API Key from the Central Server to keep your account clean for future reinstallations.
     234*   **ROBUSTNESS:** Prevented the automatic AIB Network protection list from getting stuck on "Downloading..." if an API key fails to generate due to rate limiting on a fresh install.
     235
    226236= 8.8.9 =
    227237*   **SECURITY UPDATE:** Fixed an issue where the "Export Template" feature was inadvertently including sensitive API keys (`cf_api_token`, `cf_zone_id`, `abuseipdb_api_key`) in the generated JSON. Templates are now completely clean of private tokens.
     
    229239*   **MAINTENANCE:** Analyzed and ensured that the background Cloudflare Sync task (`advaipbl_cloudflare_sync_event`) schedules properly.
    230240
    231 = 8.8.8 =
    232 *   **CRITICAL FIX:** Resolved an issue where IPs imported via the Bulk Import tool were not immediately synchronized with the Server-Level Firewall (`.htaccess`). Synchronization is now instantaneous and safely batched.
    233 *   **CRITICAL FIX:** Addressed a regression introduced in the `.htaccess` fix where Cloudflare Edge Firewalls stopped synchronizing imported IPs. Bulk Imports will now immediately trigger a background Cloudflare Sync Event to safely upload large IP batches without dropping connections.
    234 
    235 = 8.8.7 =
    236 *   **ENHANCEMENT:** Bulk Import overhaul. Imported IPs are now strictly categorized under a new `bulk_import` block type, fixing issues where imported blocks were permanently categorized as Manual blocks.
    237 *   **ENHANCEMENT:** Streamlined the "Bulk Import" modal UI. Removed the redundant "Reason" field since all imports now securely force the "Bulk Import" reason programmatically to ensure filtering reliability.
    238 *   **UX/SECURITY:** Changed the default duration in the Bulk Import block modal from "Permanent" to "24 Hours". This is in line with strict security industry standards, preventing unmanageable boundless table growth from unintended permanent bans.
    239 *   **ROBUSTNESS:** Enhanced JavaScript handling in the Bulk Import modal to prevent caching issues from crashing the import button.
    240 
    241 = 8.8.6 =
    242 *   **SECURITY ENHANCEMENT:** Upgraded the Threat Intelligence synchronization engine. Client scanning endpoints now enforce an authenticated V2 handshake to prevent unauthorized harvesting of the vulnerability database.
    243 *   **OPTIMIZATION:** The AIB Community Network now fully offloads blocklist distribution to the Cloudflare Edge, dramatically improving sync speed and eliminating server load during large blocklist updates.
    244 
    245 = 8.8.5 =
    246 *   **CRITICAL FIX (AJAX/Editor Conflict):** Resolved a severe conflict where the "Attack Signature Engine" was incorrectly intercepting background AJAX requests (like `admin-ajax.php`), causing infinite loading loops in page builders (Elementor) and our own Security Dashboard. Standard AJAX and REST API requests are now safely excluded.
    247 *   **PERFORMANCE (Cloudflare Sync):** Implemented a "Memory Reconciliation" (Delta Sync) module for Cloudflare. The plugin now fetches active rules via a single API call and only pushes missing IPs (the delta) to Cloudflare, eliminating the `cURL error 28: Operation timed out` that occurred when syncing large databases on shared hosting.
    248 
    249 = 8.8.4 =
    250 *   **CRITICAL FIX:** Resolved the "Spamhaus Drop List" automatic update failure. The cron job is now correctly registered and updating the list daily.
    251 *   **FIX:** Fixed a UI issue where the "Bulk Import" form in the Whitelist section was displaying inline instead of in a modal.
    252 
    253 = 8.8.3 =
    254 *   **NEW FEATURE:** "Impersonator Detection" (Fake Crawler Protection). Bots claiming to be Google/Bing are now verified via DNS. If they fail, they are flagged as "Impersonators" and blocked/logged.
    255 *   **NEW FEATURE:** Bulk Import/Export for IP Lists! Easily manage large IP whitelists via CSV/Text.
    256 *   **ENHANCEMENT:** Attack Signature Notifications. You can now configure the frequency (Instant, Daily, Weekly) and recipients for signature detection alerts.
    257 *   **FIX:** Resolved a critical issue where "Signature Flagged" events were missing from the Security Log filters.
    258 *   **FIX:** Fixed an incorrect log call in the login protection logic that caused PHP warnings.
    259 
    260 = 8.8.2 =
    261 *   **NEW FEATURE:** Granular Site Scanner settings. You can now enable/disable specific checks (SSL, Updates, PHP, WP, Debug) to tailor the scanner to your environment.
    262 *   **IMPROVEMENT:** The Site Scanner now respects user preferences and skips disabled checks in both manual and scheduled scans.
    263 *   **IMPROVEMENT:** Refined logic to prevent "skipped" checks from triggering false positive email alerts.
    264 *   **DEV:** Added `apply_filters('advaipbl_site_scanner_results')` for developer customization.
    265 
    266 
    267 = 8.8.1 =
    268 *   **CRITICAL FIX:** Resolved a PHP Fatal Error in the AbuseIPDB module that occurred when the API rate limit was exceeded.
    269 *   **COMPATIBILITY:** Fixed XML-RPC compatibility issues with external editors (like MarsEdit). Valid authenticated requests now correctly bypass 2FA and 404/403 Lockdown protections.
    270 *   **FIX:** Added missing event logging for "Signatures Flagged" in the Security Log.
    271 *   **I18N:** Added missing translator comments to signature flagging reason strings.
    272 
    273 = 8.8.0 =
    274 *   **MAJOR REFACTOR:** Complete architectural modernization. The plugin core has been refactored into modular "Managers", significantly improving stability, maintainability, and code organization.
    275 *   **PERFORMANCE:** JavaScript assets have been split into feature-specific modules (Admin, Rules, Logs), reducing the initial page load weight and improving admin panel responsiveness.
    276 *   **IMPROVEMENT:** Centralized Cron Manager. Background tasks are now handled by a dedicated controller, ensuring reliable scheduling and clean uninstallation of all events.
    277 *   **IMPROVEMENT:** Enhanced "Plan A" reliability for REST API protection. The User Enumeration blocking logic now respects both IP and User-Agent whitelists.
    278 *   **STABILITY:** Fixed various legacy bugs and race conditions identified during the code audit.
    279 
    280 = 8.7.5 =
    281 *   **CRITICAL FIX:** Improved database integrity checks. The update process now strictly verifies the existence of all critical tables (like Blocked IPs and Signatures) and re-creates them if missing. This resolves improper installation states where tables might be absent after a file-only update.
    282 *   **FIX:** Resolved an "Invalid rule data received" error when creating Advanced Rules with regex patterns. The JSON handling logic now correctly preserves escaped characters (backslashes).
    283 *   **FIX:** Addressed a false positive in the SSL DeepScan where cron/loopback requests could report SSL as critical. The scanner now verifies the site's configured URL protocol in addition to the current connection.
    284 
    285 = 8.7.4 =
    286 *   **NEW FEATURE:** Threat Intelligence Services (AIB Community & AbuseIPDB) now support the "JavaScript Challenge" action in addition to instant blocking. This allows you to verify suspicious traffic without blocking it outright, saving server resources.
    287 *   **NEW FEATURE:** Internal Security & Forensics (Audit Logs) now support Push Notifications (Webhooks). Get instant alerts on Slack/Discord when critical audit events occur.
    288 *   **FIX:** Resolved a synchronization issue in the "Block Duration" logic. The Blocked IPs table, Security Logs, and Notifications now consistently display the correct duration (e.g., 1440 mins) instead of defaulting to "Permanent" or showing discrepancies for Impersonation blocks.
    289 *   **FIX:** Fixed the "Bot Impersonation" logic to correctly register the configured "Threat Score" points (e.g., 100/100) in the IP Trust Log before executing the immediate block.
    290 *   **FIX:** Improved Unblock logic. Manually unblocking an IP (or using "Unblock All") now automatically removes it from the "Pending Reports" queue, preventing false positives from being sent to the Community Network.
    291 *   **Code Quality:** Addressed various PHPCS warnings, including a security improvement in output escaping logic.
    292 
    293 = 8.7.3 =
    294 *   **FEATURE:** Added search functionality to the "Blocked IPs" list, allowing admins to quickly find specific IPs or ranges.
    295 *   **FIX:** Resolved "Table doesn't exist" error in the Audit Log when the feature is inactive.
    296 *   **FIX:** Corrected "Array to string conversion" warning in Community Blocklist updates due to complex whitelist formats.
    297 *   **IMPROVEMENT:** Enhanced robustness of IP whitelisting logic during community feed synchronization.
    298 
    299 = 8.7.2 =
    300 *   **Fix (PHP 8.1+ Compatibility):** Resolved a deprecated warning ("passing null to parameter") in WordPress core. Updated the internal `status_header` hook usage to strict compliance.
    301 *   **Fix:** Resolved a PHP warning ("Undefined variable $type") that occurred when removing IPs from the whitelist via the admin interface.
    302 *   **Performance:** Optimized the Blocklist Generator. It now uses a "Fast Mode" to skip expensive DNS lookups during bulk generation, fixing execution timeouts on large lists.
    303 *   **Improvement:** Migrated the Cloudflare "Clear All Rules" operation to a background asynchronous task, preventing UI freezes.
    304 
    305 = 8.7.1 =
    306 *   **Performance:** Migrated the Cloudflare "Clear All" operation to a background process (Async). This ensures instant UI feedback and prevents PHP timeouts when clearing thousands of rules.
    307 *   **Critical Fix (Priority):** Resolved a logic error where the AbuseIPDB check (Priority 10) was ignoring Global URI Exclusions. Excluded URLs are now correctly bypassed before any API calls.
    308 *   **Fix:** The "Unblock All" button now correctly removes all plugin-managed rules (`[AIB]` tagged) from Cloudflare, fixing potential "phantom blocks" synchronization issues.
    309 *   **Maintenance:** Improved plugin deactivation/uninstall routines to ensure all scheduled background tasks are properly cleaned up.
    310 
    311 = 8.7 =
    312 *   **NEW MAJOR FEATURE: Internal Security & Forensics Suite.** A comprehensive auditing system that tracks user activity (Audit Log) and monitors file system integrity (FIM) to detect breaches or unauthorized changes.
    313 *   **NEW FEATURE: Activity Audit Log.** Records critical events like plugin changes, setting updates, and login activity. Includes a searchable UI and automated log rotation.
    314 *   **NEW FEATURE: File Integrity Monitor (FIM).** Automatically scans critical core and plugin files for unauthorized modifications. Alerts you instantly via email if a file hash changes.
    315 *   **NEW FEATURE: Deep Scan Email Reports.** Enhanced weekly security report now includes "Pending Updates" status and a summary of known vulnerabilities, keeping you informed without logging in.
    316 *   **Enhancement:** Added "Downloads History" to the Telemetry Dashboard card.
    317 *   **Enhancement:** Improved "Clear Audit Logs" reliability with AJAX handling.
    318 *   **Security Fix:** Fixed CSP header to correctly allow Stripe/Sift scripts only on the "About" page.
    319 *   **Fix:** Resolved a race condition in AbuseIPDB checks that could cause duplicate email notifications.
    320 *   **Fix:** Addressed various PHPCS and linting warnings for cleaner code.
    321 
    322 = 8.6.11 =
    323 *   **Critical Fix (ASN Whitelisting):** Corrected a validation issue where ASN Whitelist entries with comments (e.g., "AS32934 # Facebook") were failing the strict check. The logic now properly sanitizes the whitelist before validation.
    324 *   **Critical Fix (IPv6):** Fixed CIDR validation logic to correctly support 128-bit IPv6 ranges. Previously, it was incorrectly restricted to 32 bits, causing valid IPv6 ranges to be rejected.
    325 *   **Improvement (Geolocation):** Implemented a robust fallback mechanism. If the Local MaxMind Database lookup fails (or finds no data), the system now seamlessly attempts to fetch the data via the real-time API, ensuring critical ASN/Country checks don't fail silently.
    326 
    327 = 8.6.10 =
    328 *   **NEW FEATURE: Enhanced Lockdown Forensics.** Added detailed sampling for Distributed Lockdown events (404/403). Administrators can now see the exact URIs, timestamps, and user-agents of the requests that triggered a lockdown in the details popup.
    329 *   **Fix:** Resolved a PHP warning (`undefined variable $block_reason_code`) in the `monitor_threat_score` function, ensuring cleaner error logs.
    330 
    331 = 8.6.9 =
    332 *   **NEW FEATURE: Username Blocking.** Added "Username" as a new condition type for Advanced Rules. You can now create rules to Block, Challenge, or Score login attempts based on specific usernames (e.g., block "admin" or "test").
    333 *   **Enhanced Notifications:** Enabled Email and Push notifications for Distributed Lockdowns (404/403 errors), ensuring administrators are alerted when these automated defenses kick in.
    334 *   **Security Fix (Compliance):** Added strict direct file access protection to `advaipbl-loader.php`. Implemented a smart check to satisfy security scanners (Plugin Check) while maintaining Edge Mode compatibility.
    335 *   **Improvement (Logging):** Added "Endpoint Challenge" event logging. Challenges served by the 404/403 Lockdown system are now properly recorded in the Security Log for auditing.
    336 *   **Improvement (Smarter Scanning):** Enhanced theme detection logic in the Site Scanner to better identify active themes even when standard WordPress functions return incomplete data.
    337 *   **Improvement (Onboarding):** New installations now come with a default whitelist of safe ASNs (Cloudflare, Google, etc.) to prevent accidental blocking of critical infrastructure.
    338 
    339 = 8.6.8 =
    340 *   **NEW MAJOR FEATURE: Admin Bar Menu.** Added a comprehensive "Security" menu to the WordPress Admin Bar. Administrators can now quickly access settings, flush caches, view logs, and toggle "Panic Mode" from any page.
    341 *   **NEW FEATURE: Distributed Lockdown (403/404).** Introduced a smart defense mechanism that automatically locks down the site for an IP subnet or country if they trigger excessive 404 or 403 errors, protecting against distributed brute-force and resource probing.
    342 *   **Critical Fix (ASN Whitelisting):** Resolved a logic conflict that prevented API-based geolocation users from successfully whitelisting critical services like Stripe or Google via their ASN.
    343 *   **Bugfix:** Fixed a JavaScript error ("cannot read properties of undefined") on the Post Editor screen that could interrupt the "Add Tag" functionality.
    344 *   **Telemetry:** Enhanced telemetry to track the usage of the new Distributed Lockdown features.
    345 
    346 = 8.6.7 =
    347 *   **NEW MAJOR FEATURE: HTTP Security Headers.** Added a comprehensive manager to easily configure and enforce security headers (HSTS, X-Frame-Options, CSP, etc.) directly from the dashboard. This improves your site's security grade and protects users from browser-based attacks.
    348 *   **Enhancement:** Integrated the "Security Headers" menu into the Admin Bar for quick access.
    349 *   **Critical Fix (Advanced Rules):** Refined the "Allow" rule logic. Global Allow rules now correctly bypass subsequent IP checks (like AbuseIPDB), ensuring that whitelisted traffic is never blocked by external threat intelligence.
    350 *   **UX:** Added a direct help link to the Security Headers page.
    351 
    352 = 8.6.6 =
    353 *   **Critical Security Fix (Zero-Tolerance):** "Impersonation" events are now blocked instantly, bypassing the Threat Scoring system. This ensures that any bot pretending to be Google/Bing but failing DNS verification is stopped immediately, regardless of score settings.
    354 *   **Logic Refinement (The "Equilibrium" Fix):** Reordered the security check priority. The "Allow" Advanced Rules and IP Whitelists are now evaluated *before* the global blocklists (like Community Network). This allows administrators to create effective "bypass rules" for IPs that might be listed in global blacklists.
    355 *   **Documentation:** Added verified credits for AbuseIPDB and Wordfence Intelligence. Added context-aware help links for reCAPTCHA settings.
    356 *   **Code Quality:** Addressed strict PHPCS warnings (SQL preparation, Nonce verification, Output escaping) across the codebase for improved security and stability.
    357 
    358 = 8.6.5 =
    359 *   **Improvement: Enhanced the "Known Bot Verification" engine. Refined DNS validation logic to better handle specific SEO crawlers (like Ahrefs, MJ12bot) and IPv6 configurations, preventing false "Impersonation" blocks.
    360 *   **Fix: Resolved a DNS resolution edge case where hostnames with trailing dots could cause validation failures on some server environments.
    361 *   **Tweak: Updated the default list of User-Agents and WAF rules to include protection against modern scraper libraries (Scrapy, Go-http-client).
    362 
    363 = 8.6.4 =
    364 *   **Critical Fix (Performance): Resolved a bug where the "Community List Update" cron job could be scheduled multiple times, causing excessive background tasks. This update automatically cleans up duplicate events.
    365 *   **NEW FEATURE: Server Reputation Scanner. Added a tool in the "Site Scanner" tab to check if your server's IP address is blacklisted by Spamhaus or AbuseIPDB, helping you identify hosting-related issues.
    366 *   **Improvement: Optimized the cron scheduling logic to prevent future duplication of tasks.
    367 *   **Improvement: Enhanced the Site Scanner UI with clearer status indicators and action buttons.
    368 
    369 
    370 = 8.6.3 =
    371 *   **NEW MAJOR FEATURE: Site Health & Vulnerability Scanner. Added a comprehensive security audit tool. It checks for critical issues like outdated PHP, debug mode risks, and scans your plugins/themes against a database of 30,000+ known vulnerabilities.
    372 *   **Architecture Upgrade: Migrated the "Community Defense Network" IP list to a dedicated custom database table for extreme performance and scalability. This eliminates memory overhead even with thousands of blocked IPs.
    373 *   **Compatibility: Verified full compatibility with WordPress 6.9.
    374 *   **UI Enhancement: Added help icons and direct documentation links to advanced settings for easier configuration.
    375 *   **Improvement: The setup wizard now automatically enables the Server-Level Firewall (.htaccess) for stronger default protection.
    376 
    377 = 8.6.2 =
    378 *   **NEW MAJOR FEATURE: Community Defense Network (Beta). Launched our collaborative threat intelligence network. You can now opt-in to share anonymized attack reports and protect your site with a global blocklist generated from verified community data.
    379 *   **Enhancement: Increased default block duration to 24 hours (1440 mins) for stronger protection and better data quality for the community network.
    380 *   **Performance: Optimized the wp_options storage for the community blocklist to prevent autoloading, ensuring zero impact on site load time.
    381 *   **Security Hardening: Updated default WAF rules to include protection against Scrapy, Go-http-client, and common log/backup file scanners (.sql, .log).
    382 *   **Improvement: The "Clean Expired IPs" cron job now automatically syncs removals with Cloudflare and Htaccess, ensuring that temporary bans are lifted correctly across all firewalls.
    383 *   **Fix: Resolved a display issue where the "Settings" tab content could be malformed if certain options were disabled.
    384 
    385 = 8.6.1 =
    386 *   **NEW MAJOR FEATURE: Cloud Edge Defense. Introducing cloud-based blocking. Integrate seamlessly with Cloudflare to sync your "Manually Blocked" and "Permanent" IPs directly to the Cloudflare Firewall (WAF). This stops attackers at the network edge, reducing server load to zero.
    387 *   **NEW MAJOR FEATURE: Server-Level Firewall. Added a high-performance module that writes blocking rules and file hardening directives (wp-config.php, .git, etc.) directly to your .htaccess file. Includes automatic backups and dual-stack Apache support.
    388 *   **Critical Fix: Resolved a false positive issue affecting legitimate iOS users (iCloud Private Relay) and social media link previews, which were incorrectly flagged as "Bot Impersonators".
    389 *   **Enhancement: Completely redesigned the Settings experience with a new "Help Center" approach, providing direct links to documentation for complex features.
    390 *   **Enhancement: Updated the Setup Wizard to include Server-Level Firewall activation and better guidance for advanced integrations.
    391 *   **Performance: Optimized the IP blocking logic to handle bulk actions efficiently by updating external firewalls (Htaccess/Cloudflare) only once per batch.
    392 *   **Telemetría: Updated data points to track adoption of Cloudflare and Htaccess features.
    393 
    394 = 8.6.0 =
    395 *   **NEW MAJOR FEATURE: Server-Level Firewall (.htaccess).** Introducing the ultimate performance upgrade. You can now write blocking rules directly to your server's `.htaccess` file. This blocks threats before WordPress loads, saving massive server resources. Includes automatic backups, proxy awareness (`SetEnvIF`), and support for Apache 2.2/2.4.
    396 *   **Feature: File Hardening.** Easily block access to sensitive system files (`wp-config.php`, `readme.html`, etc.) at the server level.
    397 *   **Feature: Auto-Synchronization.** Automatically syncs your "Manually Blocked" and "Permanent" IPs from the database to the server firewall.
    398 *   **Feature: Temporary Block Offloading.** Optionally push temporary blocks (like 404 abusers or failed logins) to the server firewall for the duration of their ban.
    399 *   **Critical Fix: Bot Verification.** Resolved a false positive issue where legitimate iOS users (using iCloud Private Relay) or social media app browsers (Instagram/Facebook in-app) were being blocked as "Bot Impersonators". The verification logic has been refined to exclude social bots from strict DNS checks while maintaining security for search engine crawlers.
    400 *   **Enhancement:** Updated Telemetry receiver to track the adoption of the new firewall features.
    401 *   **UI/UX:** Integrated the new firewall controls into the main Settings tab for a streamlined experience.
    402 
    403 
    404 
    405241== Upgrade Notice ==
    406242
    407 = 8.8.9 =
    408 **SECURITY UPDATE:** Patches a vulnerability in the Settings exporter where the "Template (No API Keys)" file was accidentally leaking Cloudflare and AbuseIPDB API keys. Update immediately if you plan to export your configuration templates to share with others.
    409 
    410 = 8.8.8 =
    411 **CRITICAL UPDATE:** Fixes a `.htaccess` synchronization bug with the Bulk Import tool. Resolves a regression where Bulk Imports stopped pushing new blocks to Cloudflare. Update immediately to ensure imported IPs are actively blocking threats at both the server level and the cloud edge.
    412 
    413 = 8.8.7 =
    414 **FEATURE & UX UPDATE:** Overhauled the Bulk Import Blocked IPs functionality. Includes a new `bulk_import` filtering type and smarter, safer defaults (24 hours instead of Permanent).
    415 
    416 = 8.8.6 =
    417 **SECURITY UPDATE:** Introduces V2 authenticated synchronization for threat intelligence endpoints and shifts blocklist distribution to Cloudflare Edge. Recommended update for all users.
    418 
    419 = 8.8.5 =
    420 **CRITICAL UPDATE:** Fixes infinite loading conflicts with Elementor and background AJAX requests. Also resolves Cloudflare synchronization timeouts. Update immediately.
    421 
    422 = 8.8.4 =
    423 **CRITICAL UPDATE:** Fixes automatic Spamhaus updates and Bulk Import display issues. Update immediately.
    424 
    425 = 8.8.3 =
    426 **NEW FEATURES:** Impersonator Detection & Bulk Whitelist Import! Also fixes a critical issue with Security Log visibility and improves notification logic.
    427 = 8.8.2 =
    428 **FEATURE UPDATE:** Granular Site Scanner controls! You can now selectively enable/disable specific security checks (SSL, Updates, etc.) to prevent false positives.
     243= 8.9.0 =
     244**MAJOR UPDATE:** This release launches the AIB Cloud Network V3 and the powerful "Whitelist Login Countries" feature. Update immediately to connect to the new, more secure threat intelligence network infrastructure and take absolute control over your login page access.
  • advanced-ip-blocker/trunk/uninstall.php

    r3476940 r3481949  
    2626
    2727if ( ! empty( $settings_option['delete_data_on_uninstall'] ) && '1' === $settings_option['delete_data_on_uninstall'] ) {
     28
     29    // --- 0. UNREGISTER FROM CENTRAL SERVER ---
     30    // Must be done before options are deleted from the database
     31    $api_token = $settings_option['api_token_v3'] ?? '';
     32    if (!empty($api_token)) {
     33        wp_remote_post('https://advaipbl.com/wp-json/aib-api/v3/unregister', [
     34            'headers' => [
     35                'Authorization' => 'Bearer ' . $api_token,
     36                'Content-Type'  => 'application/json',
     37                'Accept'        => 'application/json'
     38            ],
     39            'timeout' => 10,
     40            'blocking' => false // No need to wait for response during uninstall
     41        ]);
     42    }
    2843
    2944    global $wpdb;
     
    179194    }
    180195
    181     // --- 3. Borrar Transients ---
     196    // --- 4. Borrar Transients ---
    182197    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    183198    $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE %s OR `option_name` LIKE %s", $wpdb->esc_like( '_transient_advaipbl_' ) . '%', $wpdb->esc_like( '_transient_timeout_advaipbl_' ) . '%' ) );
    184199
    185     // --- 4. Borrar Metadatos de Usuario de 2FA ---
     200    // --- 5. Borrar Metadatos de Usuario de 2FA ---
    186201    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    187202    $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE `meta_key` LIKE %s", $wpdb->esc_like( '_advaipbl_2fa_' ) . '%' ) );
Note: See TracChangeset for help on using the changeset viewer.