Plugin Directory

Changeset 3391994


Ignore:
Timestamp:
11/08/2025 01:45:40 AM (5 weeks ago)
Author:
wpfastsec
Message:

melhoria na detecção de vulnerabilidades CVE plugins update 1.3.3 + Refatoração de Código

Location:
ht-security
Files:
24 added
2 edited

Legend:

Unmodified
Added
Removed
  • ht-security/trunk/ht-security.php

    r3391451 r3391994  
    11<?php
    2 /*
    3 Plugin Name: HT Security
    4 Description: Suite de Segurança completa: Cabeçalhos de segurança, alertas por e-mail, verificação do Core, bloqueio de enumeração de usuários, modo de manutenção e auditoria de permissões.
    5 Version: 1.3.2
    6 Requires at least: 6.5
    7 Requires PHP: 8.2
    8 Tested up to: 6.8
    9 Author: WPFastSec
    10 Author URI: https://wpfastsec.com
    11 License: GPLv2 or later
    12 Text Domain: ht-security
    13 */
     2/**
     3 * Plugin Name: HT Security
     4 * Description: Suite de Segurança completa: Cabeçalhos de segurança, alertas por e-mail, verificação do Core, bloqueio de enumeração de usuários, modo de manutenção e auditoria de permissões.
     5 * Version: 1.3.3
     6 * Requires at least: 6.5
     7 * Requires PHP: 8.2
     8 * Tested up to: 6.8
     9 * Author: WPFastSec
     10 * Author URI: https://wpfastsec.com
     11 * License: GPLv2 or later
     12 * Text Domain: ht-security
     13 *
     14 * @package HT_Security
     15 * @since 1.0.0
     16 */
    1417
    1518if ( ! defined( 'ABSPATH' ) ) {
    16     exit;
     19    exit; // Exit if accessed directly.
    1720}
    1821
    1922/**
    20  * Injeta cabeçalhos de segurança no front-end.
     23 * Define constantes do plugin.
    2124 */
    22 add_filter( 'wp_headers', 'htsec_security_headers' );
    23 function htsec_security_headers( $headers ) {
    24     if ( ! get_option( 'htsec_enable_headers', 1 ) ) {
    25         return $headers;
    26     }
    27 
    28     $defaults = [
    29         'Strict-Transport-Security' => 'max-age=63072000; includeSubDomains; preload',
    30         'X-Frame-Options'           => 'SAMEORIGIN',
    31         'X-Content-Type-Options'    => 'nosniff',
    32         'Referrer-Policy'           => 'strict-origin-when-cross-origin',
    33         'Permissions-Policy'        => 'geolocation=(), microphone=(), camera=()',
    34         'Content-Security-Policy'   => 'upgrade-insecure-requests;',
    35     ];
    36 
    37     return array_merge( $headers, $defaults );
    38 }
     25define( 'HT_SECURITY_VERSION', '1.3.3' );
     26define( 'HT_SECURITY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     27define( 'HT_SECURITY_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    3928
    4029/**
    41  * Registra seções e campos de configuração.
     30 * Carrega os módulos do plugin.
     31 *
     32 * Cada módulo é responsável por uma funcionalidade específica,
     33 * mantendo o código organizado e facilitando a manutenção.
     34 *
     35 * @since 1.3.3
    4236 */
    43 add_action( 'admin_init', 'htsec_register_settings' );
    44 function htsec_register_settings() {
    45     register_setting( 'htsec_settings_group', 'htsec_enable_headers', [
    46         'type'              => 'boolean',
    47         'sanitize_callback' => 'rest_sanitize_boolean',
    48         'default'           => 1,
    49     ] );
    50     register_setting( 'htsec_settings_group', 'htsec_enable_login_alerts', [
    51         'type'              => 'boolean',
    52         'sanitize_callback' => 'rest_sanitize_boolean',
    53         'default'           => 0,
    54     ] );
    55     register_setting( 'htsec_settings_group', 'htsec_alert_email', [
    56         'type'              => 'string',
    57         'sanitize_callback' => 'sanitize_email',
    58     ] );
    59     register_setting( 'htsec_settings_group', 'htsec_disable_user_enumeration', [
    60         'type'              => 'boolean',
    61         'sanitize_callback' => 'rest_sanitize_boolean',
    62         'default'           => 0,
    63     ] );
    64     register_setting( 'htsec_settings_group', 'htsec_maintenance_mode', [
    65         'type'              => 'boolean',
    66         'sanitize_callback' => 'rest_sanitize_boolean',
    67         'default'           => 0,
    68     ] );
    69     register_setting( 'htsec_settings_group', 'htsec_maintenance_ips', [
    70         'type'              => 'string',
    71         'sanitize_callback' => 'sanitize_textarea_field',
    72         'default'           => '',
    73     ] );
    74     register_setting( 'htsec_settings_group', 'htsec_nvd_api_key', [
    75         'type'              => 'string',
    76         'sanitize_callback' => 'sanitize_text_field',
    77         'default'           => '',
    78     ] );
    79     register_setting( 'htsec_settings_group', 'htsec_enable_cve_alerts', [
    80         'type'              => 'boolean',
    81         'sanitize_callback' => 'rest_sanitize_boolean',
    82         'default'           => 0,
    83     ] );
    84     register_setting( 'htsec_settings_group', 'htsec_show_plugin_badges', [
    85         'type'              => 'boolean',
    86         'sanitize_callback' => 'rest_sanitize_boolean',
    87         'default'           => 0,
    88     ] );
     37function htsec_load_modules() {
     38    $modules = [
     39        'security-headers',      // Cabeçalhos de segurança HTTP.
     40        'settings',              // Registro de configurações e campos.
     41        'login-alerts',          // Alertas de login bem-sucedido/falho.
     42        'user-enumeration',      // Proteção contra enumeração de usuários.
     43        'maintenance-mode',      // Modo de manutenção com whitelist de IPs.
     44        'file-permissions',      // Auditoria de permissões de arquivos.
     45        'cve-check',             // Verificação de vulnerabilidades CVE.
     46        'admin-page',            // Página administrativa e feedback.
     47        'plugin-indicators',     // Indicadores de vulnerabilidades em plugins.
     48    ];
    8949
    90     add_settings_section(
    91         'htsec_main_section',
    92         esc_html__( 'Configurações de Segurança', 'ht-security' ),
    93         null,
    94         'ht-security'
    95     );
     50    foreach ( $modules as $module ) {
     51        $file = HT_SECURITY_PLUGIN_DIR . 'includes/' . $module . '.php';
    9652
    97     add_settings_field(
    98         'htsec_enable_headers',
    99         esc_html__( 'Ativar Cabeçalhos de Segurança', 'ht-security' ),
    100         function() {
    101             $value = get_option( 'htsec_enable_headers', 1 );
    102             printf(
    103                 '<input type="checkbox" name="htsec_enable_headers" value="1" %s> %s',
    104                 checked( 1, $value, false ),
    105                 esc_html__( 'Sim', 'ht-security' )
    106             );
    107         },
    108         'ht-security',
    109         'htsec_main_section'
    110     );
    111 
    112     add_settings_field(
    113         'htsec_enable_login_alerts',
    114         esc_html__( 'Alertas de Login', 'ht-security' ),
    115         function() {
    116             $value = get_option( 'htsec_enable_login_alerts', 0 );
    117             printf(
    118                 '<input type="checkbox" name="htsec_enable_login_alerts" value="1" %s> %s',
    119                 checked( 1, $value, false ),
    120                 esc_html__( 'Enviar e-mail em logins bem-sucedidos e falhos', 'ht-security' )
    121             );
    122         },
    123         'ht-security',
    124         'htsec_main_section'
    125     );
    126 
    127     add_settings_field(
    128         'htsec_alert_email',
    129         esc_html__( 'E-mail para Alertas', 'ht-security' ),
    130         function() {
    131             $value = get_option( 'htsec_alert_email', get_option( 'admin_email' ) );
    132             printf(
    133                 '<input type="email" name="htsec_alert_email" value="%s" class="regular-text">',
    134                 esc_attr( $value )
    135             );
    136         },
    137         'ht-security',
    138         'htsec_main_section'
    139     );
    140 
    141     add_settings_field(
    142         'htsec_disable_user_enumeration',
    143         esc_html__( 'Desativar Enumeração de Usuários', 'ht-security' ),
    144         function() {
    145             $value = get_option( 'htsec_disable_user_enumeration', 0 );
    146             printf(
    147                 '<input type="checkbox" name="htsec_disable_user_enumeration" value="1" %s> %s',
    148                 checked( 1, $value, false ),
    149                 esc_html__( 'Bloquear enumeração de usuários via API REST', 'ht-security' )
    150             );
    151             echo '<p class="description">' . esc_html__( 'Impede que usuários sejam listados através da API REST do WordPress.', 'ht-security' ) . '</p>';
    152         },
    153         'ht-security',
    154         'htsec_main_section'
    155     );
    156 
    157     add_settings_field(
    158         'htsec_maintenance_mode',
    159         esc_html__( 'Modo de Manutenção', 'ht-security' ),
    160         function() {
    161             $value = get_option( 'htsec_maintenance_mode', 0 );
    162             printf(
    163                 '<input type="checkbox" name="htsec_maintenance_mode" value="1" %s> %s',
    164                 checked( 1, $value, false ),
    165                 esc_html__( 'Ativar modo de manutenção', 'ht-security' )
    166             );
    167             echo '<p class="description">' . esc_html__( 'Apenas IPs autorizados poderão acessar o site.', 'ht-security' ) . '</p>';
    168         },
    169         'ht-security',
    170         'htsec_main_section'
    171     );
    172 
    173     add_settings_field(
    174         'htsec_maintenance_ips',
    175         esc_html__( 'IPs Autorizados', 'ht-security' ),
    176         function() {
    177             $value = get_option( 'htsec_maintenance_ips', '' );
    178             printf(
    179                 '<textarea name="htsec_maintenance_ips" rows="5" cols="50" class="regular-text">%s</textarea>',
    180                 esc_textarea( $value )
    181             );
    182             echo '<p class="description">' . esc_html__( 'Digite um IP por linha. Estes IPs poderão acessar o site em modo de manutenção.', 'ht-security' ) . '</p>';
    183             $current_ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '' ) );
    184             if ( $current_ip ) {
    185                 /* translators: %s: Current user IP address */
    186                 echo '<p class="description">' . sprintf( esc_html__( 'Seu IP atual: %s', 'ht-security' ), esc_html( $current_ip ) ) . '</p>';
    187             }
    188         },
    189         'ht-security',
    190         'htsec_main_section'
    191     );
    192 
    193     add_settings_field(
    194         'htsec_nvd_api_key',
    195         esc_html__( 'API Key NVD (Opcional)', 'ht-security' ),
    196         function() {
    197             $value = get_option( 'htsec_nvd_api_key', '' );
    198             printf(
    199                 '<input type="text" name="htsec_nvd_api_key" value="%s" class="regular-text">',
    200                 esc_attr( $value )
    201             );
    202             echo '<p class="description">' . esc_html__( 'API Key do National Vulnerability Database. Sem API Key: 5 requisições/30s. Com API Key: 50 requisições/30s.', 'ht-security' ) . '</p>';
    203             echo '<p class="description">' . esc_html__( 'Obtenha sua API Key em: https://nvd.nist.gov/developers/request-an-api-key', 'ht-security' ) . '</p>';
    204         },
    205         'ht-security',
    206         'htsec_main_section'
    207     );
    208 
    209     add_settings_field(
    210         'htsec_enable_cve_alerts',
    211         esc_html__( 'Alertas de Vulnerabilidades', 'ht-security' ),
    212         function() {
    213             $value = get_option( 'htsec_enable_cve_alerts', 0 );
    214             printf(
    215                 '<input type="checkbox" name="htsec_enable_cve_alerts" value="1" %s> %s',
    216                 checked( 1, $value, false ),
    217                 esc_html__( 'Enviar e-mail quando vulnerabilidades CVE forem detectadas', 'ht-security' )
    218             );
    219             echo '<p class="description">' . esc_html__( 'Verificação automática a cada 12 horas.', 'ht-security' ) . '</p>';
    220         },
    221         'ht-security',
    222         'htsec_main_section'
    223     );
    224 
    225     add_settings_field(
    226         'htsec_show_plugin_badges',
    227         esc_html__( 'Indicadores na Página de Plugins', 'ht-security' ),
    228         function() {
    229             $value = get_option( 'htsec_show_plugin_badges', 0 );
    230             printf(
    231                 '<input type="checkbox" name="htsec_show_plugin_badges" value="1" %s> %s',
    232                 checked( 1, $value, false ),
    233                 esc_html__( 'Exibir badges de segurança na página de plugins', 'ht-security' )
    234             );
    235             echo '<p class="description">' . esc_html__( 'Mostra indicadores verdes/vermelhos em cada plugin indicando status de vulnerabilidades.', 'ht-security' ) . '</p>';
    236         },
    237         'ht-security',
    238         'htsec_main_section'
    239     );
     53        if ( file_exists( $file ) ) {
     54            require_once $file;
     55        } else {
     56            // Log de erro se algum módulo não for encontrado.
     57            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     58                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     59                error_log( sprintf( 'HT Security: Módulo "%s" não encontrado em %s', $module, $file ) );
     60            }
     61        }
     62    }
    24063}
    241 
    242 /**
    243  * Adiciona página de configurações no menu.
    244  */
    245 add_action( 'admin_menu', function() {
    246     add_options_page(
    247         esc_html__( 'HT Security', 'ht-security' ),
    248         esc_html__( 'HT Security', 'ht-security' ),
    249         'manage_options',
    250         'ht-security',
    251         'htsec_plugin_settings_page'
    252     );
    253 } );
    254 
    255 /**
    256  * Adiciona estilos CSS personalizados na página de administração.
    257  */
    258 add_action( 'admin_head', 'htsec_admin_styles' );
    259 function htsec_admin_styles() {
    260     $screen = get_current_screen();
    261     if ( isset( $screen->id ) && 'settings_page_ht-security' === $screen->id ) {
    262         ?>
    263         <style>
    264             .htsec-alert-success {
    265                 padding: 15px;
    266                 background-color: #d4edda;
    267                 border: 1px solid #c3e6cb;
    268                 border-radius: 4px;
    269                 margin: 20px 0;
    270             }
    271             .htsec-alert-success p {
    272                 margin: 0;
    273                 color: #155724;
    274                 font-weight: bold;
    275             }
    276             .htsec-alert-danger {
    277                 padding: 15px;
    278                 background-color: #f8d7da;
    279                 border: 1px solid #f5c6cb;
    280                 border-radius: 4px;
    281                 margin: 20px 0;
    282             }
    283             .htsec-alert-danger p {
    284                 margin: 0;
    285                 color: #721c24;
    286                 font-weight: bold;
    287             }
    288             .htsec-vuln-card {
    289                 border: 1px solid #dee2e6;
    290                 border-radius: 8px;
    291                 padding: 20px;
    292                 margin-bottom: 15px;
    293                 background-color: #fff;
    294                 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    295                 transition: box-shadow 0.3s ease;
    296             }
    297             .htsec-vuln-card:hover {
    298                 box-shadow: 0 4px 8px rgba(0,0,0,0.15);
    299             }
    300             .htsec-vuln-header {
    301                 display: flex;
    302                 justify-content: space-between;
    303                 align-items: flex-start;
    304                 margin-bottom: 15px;
    305                 flex-wrap: wrap;
    306                 gap: 10px;
    307             }
    308             .htsec-vuln-title {
    309                 margin: 0;
    310                 font-size: 16px;
    311                 color: #333;
    312             }
    313             .htsec-severity-badge {
    314                 padding: 6px 14px;
    315                 border-radius: 4px;
    316                 font-size: 12px;
    317                 font-weight: bold;
    318                 text-transform: uppercase;
    319                 letter-spacing: 0.5px;
    320             }
    321             .htsec-vuln-detail {
    322                 margin-bottom: 12px;
    323             }
    324             .htsec-vuln-detail strong {
    325                 color: #555;
    326             }
    327             .htsec-vuln-description {
    328                 background-color: #f8f9fa;
    329                 padding: 12px;
    330                 border-left: 3px solid #007cba;
    331                 margin-top: 5px;
    332                 color: #666;
    333                 line-height: 1.6;
    334             }
    335             .htsec-cve-link {
    336                 color: #007cba;
    337                 text-decoration: none;
    338                 font-weight: 500;
    339             }
    340             .htsec-cve-link:hover {
    341                 text-decoration: underline;
    342             }
    343             .htsec-cvss-score {
    344                 background-color: #e9ecef;
    345                 padding: 2px 8px;
    346                 border-radius: 3px;
    347                 font-family: monospace;
    348                 font-weight: bold;
    349             }
    350             .htsec-check-info {
    351                 background-color: #f0f6fc;
    352                 border-left: 4px solid #0969da;
    353                 padding: 12px 16px;
    354                 margin: 20px 0;
    355             }
    356             .htsec-progress-item {
    357                 padding: 8px 12px;
    358                 background-color: #f8f9fa;
    359                 border-left: 3px solid #0969da;
    360                 margin-bottom: 8px;
    361                 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    362                 font-size: 13px;
    363             }
    364             .htsec-stats-box {
    365                 background-color: #fff;
    366                 border: 1px solid #ddd;
    367                 border-radius: 4px;
    368                 padding: 15px;
    369                 margin: 15px 0;
    370             }
    371             .htsec-stats-box strong {
    372                 color: #0073aa;
    373             }
    374             #htsec-feedback-section {
    375                 background-color: #fff;
    376                 border: 1px solid #ddd;
    377                 border-radius: 4px;
    378                 padding: 20px;
    379                 margin: 20px 0;
    380             }
    381             #htsec-feedback-form textarea {
    382                 width: 100%;
    383                 border: 1px solid #ddd;
    384                 border-radius: 4px;
    385                 padding: 10px;
    386                 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    387                 resize: vertical;
    388             }
    389             #htsec-feedback-form textarea:focus {
    390                 border-color: #0073aa;
    391                 outline: none;
    392                 box-shadow: 0 0 0 1px #0073aa;
    393             }
    394             .htsec-feedback-success {
    395                 padding: 12px 15px;
    396                 background-color: #d4edda;
    397                 border: 1px solid #c3e6cb;
    398                 border-radius: 4px;
    399                 color: #155724;
    400             }
    401             .htsec-feedback-error {
    402                 padding: 12px 15px;
    403                 background-color: #f8d7da;
    404                 border: 1px solid #f5c6cb;
    405                 border-radius: 4px;
    406                 color: #721c24;
    407             }
    408             #htsec-send-feedback-btn:disabled,
    409             #htsec-send-feedback-btn.disabled {
    410                 opacity: 0.6;
    411                 cursor: not-allowed;
    412             }
    413         </style>
    414         <?php
    415     }
    416 }
    417 
    418 /**
    419  * Adiciona JavaScript para o formulário de feedback.
    420  */
    421 add_action( 'admin_footer', 'htsec_feedback_script' );
    422 function htsec_feedback_script() {
    423     $screen = get_current_screen();
    424     if ( isset( $screen->id ) && 'settings_page_ht-security' === $screen->id ) {
    425         ?>
    426         <script type="text/javascript">
    427         jQuery(document).ready(function($) {
    428             // Mostrar formulário de feedback
    429             $('#htsec-show-feedback-btn').on('click', function() {
    430                 $('#htsec-feedback-form').slideDown();
    431                 $(this).hide();
    432                 $('#htsec-feedback-message').focus();
    433             });
    434 
    435             // Cancelar feedback
    436             $('#htsec-cancel-feedback-btn').on('click', function() {
    437                 $('#htsec-feedback-form').slideUp();
    438                 $('#htsec-show-feedback-btn').show();
    439                 $('#htsec-feedback-message').val('');
    440                 $('#htsec-feedback-response').html('');
    441             });
    442 
    443             // Enviar feedback
    444             $('#htsec-send-feedback-btn').on('click', function() {
    445                 var $btn = $(this);
    446                 var $textarea = $('#htsec-feedback-message');
    447                 var $response = $('#htsec-feedback-response');
    448                 var message = $textarea.val().trim();
    449 
    450                 // Validar mensagem
    451                 if (message === '') {
    452                     $response.html('<div class="htsec-feedback-error"><?php echo esc_js( __( 'Por favor, digite uma mensagem antes de enviar.', 'ht-security' ) ); ?></div>');
    453                     return;
    454                 }
    455 
    456                 if (message.length < 10) {
    457                     $response.html('<div class="htsec-feedback-error"><?php echo esc_js( __( 'A mensagem deve ter pelo menos 10 caracteres.', 'ht-security' ) ); ?></div>');
    458                     return;
    459                 }
    460 
    461                 // Desabilitar botão e textarea
    462                 $btn.prop('disabled', true).text('<?php echo esc_js( __( 'Enviando...', 'ht-security' ) ); ?>');
    463                 $textarea.prop('disabled', true);
    464                 $response.html('');
    465 
    466                 // Enviar via AJAX
    467                 $.post(ajaxurl, {
    468                     action: 'htsec_send_feedback',
    469                     message: message,
    470                     nonce: '<?php echo esc_js( wp_create_nonce( 'htsec_send_feedback' ) ); ?>'
    471                 }, function(response) {
    472                     if (response.success) {
    473                         $response.html('<div class="htsec-feedback-success">' + response.data.message + '</div>');
    474                         $textarea.val('');
    475 
    476                         // Ocultar formulário após 3 segundos
    477                         setTimeout(function() {
    478                             $('#htsec-feedback-form').slideUp();
    479                             $('#htsec-show-feedback-btn').show();
    480                             $response.html('');
    481                         }, 3000);
    482                     } else {
    483                         $response.html('<div class="htsec-feedback-error">' + response.data.message + '</div>');
    484                     }
    485                 }).fail(function() {
    486                     $response.html('<div class="htsec-feedback-error"><?php echo esc_js( __( 'Erro ao enviar feedback. Por favor, tente novamente mais tarde.', 'ht-security' ) ); ?></div>');
    487                 }).always(function() {
    488                     // Reabilitar botão e textarea
    489                     $btn.prop('disabled', false).text('<?php echo esc_js( __( 'Enviar Feedback', 'ht-security' ) ); ?>');
    490                     $textarea.prop('disabled', false);
    491                 });
    492             });
    493         });
    494         </script>
    495         <?php
    496     }
    497 }
    498 
    499 /**
    500  * Renderiza a página de configurações e faz a verificação de integridade do Core via API.
    501  */
    502 function htsec_plugin_settings_page() {
    503     echo '<div class="wrap">';
    504     echo '<h1>' . esc_html__( 'HT Security', 'ht-security' ) . '</h1>';
    505     echo '<form method="post" action="options.php">';
    506     settings_fields( 'htsec_settings_group' );
    507     do_settings_sections( 'ht-security' );
    508 
    509     // Nova funcionalidade: Verificação de integridade do Core do WordPress
    510     echo '<h2>' . esc_html__( 'Verificação de Integridade do Core', 'ht-security' ) . '</h2>';
    511 
    512     $version = get_bloginfo( 'version' );
    513     $locale  = defined( 'WP_LOCAL_PACKAGE' ) ? WP_LOCAL_PACKAGE : get_locale();
    514     $endpoint = add_query_arg( [
    515         'version' => rawurlencode( $version ),
    516         'locale'  => rawurlencode( $locale ),
    517     ], 'https://api.wordpress.org/core/checksums/1.0/' );
    518 
    519     $response = wp_remote_get( $endpoint, [ 'timeout' => 15 ] );
    520     if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
    521         $endpoint = add_query_arg( [ 'version' => $version, 'locale' => 'en_US' ], 'https://api.wordpress.org/core/checksums/1.0/' );
    522         $response = wp_remote_get( $endpoint, [ 'timeout' => 15 ] );
    523     }
    524 
    525     if ( is_wp_error( $response ) ) {
    526         echo '<p>' . esc_html__( 'Não foi possível obter os checksums do Core.', 'ht-security' ) . '</p>';
    527     } else {
    528         $data = json_decode( wp_remote_retrieve_body( $response ), true );
    529 
    530         if ( empty( $data['checksums'] ) || ! is_array( $data['checksums'] ) ) {
    531             echo '<p>' . esc_html__( 'Dados de checksums inválidos.', 'ht-security' ) . '</p>';
    532         } else {
    533             $checksums  = $data['checksums'];
    534             $mismatches = [];
    535 
    536             foreach ( $checksums as $file => $expected_hash ) {
    537                 if ( 0 === strpos( $file, 'wp-content/' ) || 'wp-includes/version.php' === $file ) {
    538                     continue;
    539                 }
    540                 $path = ABSPATH . $file;
    541                 if ( ! file_exists( $path ) || md5_file( $path ) !== $expected_hash ) {
    542                     $mismatches[] = $file;
    543                 }
    544             }
    545 
    546             if ( empty( $mismatches ) ) {
    547                 echo '<p>' . esc_html__( 'O Core do WordPress está íntegro.', 'ht-security' ) . '</p>';
    548             } else {
    549                 echo '<p>' . esc_html__( 'Foram detectadas alterações no Core do WordPress:', 'ht-security' ) . '</p>';
    550                 echo '<ul>';
    551                 foreach ( $mismatches as $diff ) {
    552                     printf( '<li>%s</li>', esc_html( $diff ) );
    553                 }
    554                 echo '</ul>';
    555             }
    556         }
    557     }
    558 
    559     submit_button();
    560     echo '</form>';
    561 
    562     // Seção de Feedback
    563     echo '<div id="htsec-feedback-section" style="margin-top: 40px;">';
    564     echo '<h2>' . esc_html__( 'Enviar Feedback', 'ht-security' ) . '</h2>';
    565     echo '<p class="description">' . esc_html__( 'Tem alguma sugestão, encontrou um bug ou quer compartilhar sua experiência? Envie-nos seu feedback!', 'ht-security' ) . '</p>';
    566 
    567     echo '<div id="htsec-feedback-form-container" style="margin-top: 20px;">';
    568     echo '<button type="button" id="htsec-show-feedback-btn" class="button button-secondary">';
    569     echo esc_html__( 'Abrir Formulário de Feedback', 'ht-security' );
    570     echo '</button>';
    571 
    572     echo '<div id="htsec-feedback-form" style="display: none; margin-top: 15px; max-width: 600px;">';
    573     echo '<textarea id="htsec-feedback-message" rows="6" class="large-text" placeholder="' . esc_attr__( 'Digite seu feedback aqui...', 'ht-security' ) . '"></textarea>';
    574     echo '<div style="margin-top: 10px;">';
    575     echo '<button type="button" id="htsec-send-feedback-btn" class="button button-primary">' . esc_html__( 'Enviar Feedback', 'ht-security' ) . '</button>';
    576     echo ' <button type="button" id="htsec-cancel-feedback-btn" class="button button-secondary">' . esc_html__( 'Cancelar', 'ht-security' ) . '</button>';
    577     echo '</div>';
    578     echo '<div id="htsec-feedback-response" style="margin-top: 15px;"></div>';
    579     echo '</div>';
    580     echo '</div>';
    581     echo '</div>';
    582 
    583     // Verificação de Vulnerabilidades CVE
    584     echo '<div id="cve-check">';
    585     echo '<h2>' . esc_html__( 'Verificação de Vulnerabilidades (CVE)', 'ht-security' ) . '</h2>';
    586     htsec_display_cve_check();
    587     echo '</div>';
    588 
    589     // Auditoria de Permissões de Arquivos
    590     echo '<h2>' . esc_html__( 'Auditoria de Permissões de Arquivos', 'ht-security' ) . '</h2>';
    591     htsec_display_file_permissions_audit();
    592 
    593     echo '</div>';
    594 }
    595 
    596 /**
    597  * Exibe a auditoria de permissões de arquivos.
    598  */
    599 function htsec_display_file_permissions_audit() {
    600     $files_to_check = [
    601         'wp-config.php' => [
    602             'path' => ABSPATH . 'wp-config.php',
    603             'recommended' => '0640',
    604             'max' => '0644',
    605             'description' => esc_html__( 'Arquivo de configuração principal', 'ht-security' ),
    606         ],
    607         '.htaccess' => [
    608             'path' => ABSPATH . '.htaccess',
    609             'recommended' => '0644',
    610             'max' => '0644',
    611             'description' => esc_html__( 'Arquivo de configuração do servidor', 'ht-security' ),
    612         ],
    613         'wp-content' => [
    614             'path' => WP_CONTENT_DIR,
    615             'recommended' => '0755',
    616             'max' => '0755',
    617             'description' => esc_html__( 'Diretório de conteúdo', 'ht-security' ),
    618         ],
    619         'wp-content/uploads' => [
    620             'path' => WP_CONTENT_DIR . '/uploads',
    621             'recommended' => '0755',
    622             'max' => '0755',
    623             'description' => esc_html__( 'Diretório de uploads', 'ht-security' ),
    624         ],
    625         'wp-content/plugins' => [
    626             'path' => WP_PLUGIN_DIR,
    627             'recommended' => '0755',
    628             'max' => '0755',
    629             'description' => esc_html__( 'Diretório de plugins', 'ht-security' ),
    630         ],
    631         'wp-content/themes' => [
    632             'path' => get_theme_root(),
    633             'recommended' => '0755',
    634             'max' => '0755',
    635             'description' => esc_html__( 'Diretório de temas', 'ht-security' ),
    636         ],
    637     ];
    638    
    639     if ( isset( $_POST['htsec_fix_permissions'] ) && isset( $_POST['htsec_permissions_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['htsec_permissions_nonce'] ) ), 'htsec_fix_permissions' ) ) {
    640         htsec_fix_file_permissions( $files_to_check );
    641         echo '<div class="notice notice-success"><p>' . esc_html__( 'Tentativa de correção de permissões executada.', 'ht-security' ) . '</p></div>';
    642     }
    643    
    644     echo '<table class="widefat striped">';
    645     echo '<thead>';
    646     echo '<tr>';
    647     echo '<th>' . esc_html__( 'Arquivo/Diretório', 'ht-security' ) . '</th>';
    648     echo '<th>' . esc_html__( 'Descrição', 'ht-security' ) . '</th>';
    649     echo '<th>' . esc_html__( 'Permissão Atual', 'ht-security' ) . '</th>';
    650     echo '<th>' . esc_html__( 'Permissão Recomendada', 'ht-security' ) . '</th>';
    651     echo '<th>' . esc_html__( 'Status', 'ht-security' ) . '</th>';
    652     echo '</tr>';
    653     echo '</thead>';
    654     echo '<tbody>';
    655    
    656     $has_issues = false;
    657    
    658     foreach ( $files_to_check as $name => $info ) {
    659         if ( ! file_exists( $info['path'] ) ) {
    660             continue;
    661         }
    662        
    663         $current_perms = substr( sprintf( '%o', fileperms( $info['path'] ) ), -4 );
    664         $is_safe = htsec_is_permission_safe( $current_perms, $info['max'] );
    665        
    666         if ( ! $is_safe ) {
    667             $has_issues = true;
    668         }
    669        
    670         echo '<tr>';
    671         echo '<td>' . esc_html( $name ) . '</td>';
    672         echo '<td>' . esc_html( $info['description'] ) . '</td>';
    673         echo '<td><code>' . esc_html( $current_perms ) . '</code></td>';
    674         echo '<td><code>' . esc_html( $info['recommended'] ) . '</code></td>';
    675         echo '<td>';
    676        
    677         if ( $is_safe ) {
    678             echo '<span style="color: green;">✓ ' . esc_html__( 'Seguro', 'ht-security' ) . '</span>';
    679         } else {
    680             echo '<span style="color: red;">⚠ ' . esc_html__( 'Permissão muito ampla', 'ht-security' ) . '</span>';
    681         }
    682        
    683         echo '</td>';
    684         echo '</tr>';
    685     }
    686    
    687     echo '</tbody>';
    688     echo '</table>';
    689    
    690     if ( $has_issues ) {
    691         echo '<form method="post" style="margin-top: 20px;">';
    692         wp_nonce_field( 'htsec_fix_permissions', 'htsec_permissions_nonce' );
    693         echo '<p class="description">' . esc_html__( 'Atenção: A correção automática pode não funcionar em todos os servidores devido a restrições de permissão.', 'ht-security' ) . '</p>';
    694         submit_button( esc_html__( 'Corrigir Permissões Automaticamente', 'ht-security' ), 'secondary', 'htsec_fix_permissions' );
    695         echo '</form>';
    696     } else {
    697         echo '<p style="margin-top: 20px; color: green;">' . esc_html__( 'Todas as permissões estão configuradas corretamente!', 'ht-security' ) . '</p>';
    698     }
    699 }
    700 
    701 /**
    702  * Verifica se uma permissão é segura.
    703  */
    704 function htsec_is_permission_safe( $current, $max ) {
    705     $current_octal = octdec( $current );
    706     $max_octal = octdec( $max );
    707    
    708     return $current_octal <= $max_octal;
    709 }
    710 
    711 /**
    712  * Tenta corrigir as permissões dos arquivos.
    713  */
    714 function htsec_fix_file_permissions( $files_to_check ) {
    715     if ( ! current_user_can( 'manage_options' ) ) {
    716         return;
    717     }
    718    
    719     require_once( ABSPATH . 'wp-admin/includes/file.php' );
    720    
    721     $creds = request_filesystem_credentials( '', '', false, ABSPATH, null );
    722    
    723     if ( ! WP_Filesystem( $creds ) ) {
    724         return;
    725     }
    726    
    727     global $wp_filesystem;
    728    
    729     foreach ( $files_to_check as $name => $info ) {
    730         if ( ! file_exists( $info['path'] ) ) {
    731             continue;
    732         }
    733        
    734         $current_perms = substr( sprintf( '%o', fileperms( $info['path'] ) ), -4 );
    735        
    736         if ( ! htsec_is_permission_safe( $current_perms, $info['max'] ) ) {
    737             $wp_filesystem->chmod( $info['path'], octdec( $info['recommended'] ), false );
    738         }
    739     }
    740 }
    741 
    742 // Login sucesso
    743 add_action( 'wp_login', 'htsec_login_success', 10, 2 );
    744 function htsec_login_success( $user_login, $user ) {
    745     if ( ! get_option( 'htsec_enable_login_alerts', 0 ) ) {
    746         return;
    747     }
    748     $ip     = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? 'IP desconhecido' ) );
    749     $hora   = current_time( 'mysql' );
    750     $email  = sanitize_email( get_option( 'htsec_alert_email', get_option( 'admin_email' ) ) );
    751     $subject = esc_html__( 'Login bem-sucedido detectado', 'ht-security' );
    752     $message = sprintf(
    753         "%s\n\n%s\n%s: %s\n%s: %s\n\n%s",
    754         esc_html__( 'Estimado Administrador,', 'ht-security' ),
    755         esc_html__( 'Informamos que foi efetuado o Login no seu site', 'ht-security' ) . ' "' . get_bloginfo( 'name' ) . '"',
    756         esc_html__( 'Usuário', 'ht-security' ),
    757         esc_html( $user_login ),
    758         esc_html__( 'IP', 'ht-security' ),
    759         esc_html( $ip ),
    760         esc_html__( 'Caso não tenha sido você, verifique a segurança do seu site.', 'ht-security' )
    761     );
    762     wp_mail( $email, $subject, $message );
    763 }
    764 
    765 // Login falhou
    766 add_action( 'wp_login_failed', 'htsec_login_failed' );
    767 function htsec_login_failed( $username ) {
    768     if ( ! get_option( 'htsec_enable_login_alerts', 0 ) ) {
    769         return;
    770     }
    771     $ip     = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? 'IP desconhecido' ) );
    772     $hora   = current_time( 'mysql' );
    773     $email  = sanitize_email( get_option( 'htsec_alert_email', get_option( 'admin_email' ) ) );
    774     $subject = esc_html__( 'Erro de Login Detectado', 'ht-security' );
    775     $message = esc_html__( 'Estimado Administrador,', 'ht-security' ) . "\n\n";
    776     $message .= esc_html__( 'Detectamos uma tentativa de login maliciosa ou com credenciais erradas:', 'ht-security' ) . "\n\n";
    777     $message .= esc_html__( 'Usuário', 'ht-security' ) . ': ' . esc_html( $username ) . "\n";
    778     $message .= esc_html__( 'IP', 'ht-security' ) . ': ' . esc_html( $ip ) . "\n";
    779     $message .= esc_html__( 'Data/Hora', 'ht-security' ) . ': ' . esc_html( $hora ) . "\n\n";
    780     $message .= esc_html__( 'Por favor, verifique seu site com máxima urgência.', 'ht-security' );
    781     wp_mail( $email, $subject, $message );
    782 }
    783 
    784 /**
    785  * Desativa enumeração de usuários via API REST.
    786  */
    787 add_filter( 'rest_endpoints', 'htsec_disable_user_endpoints' );
    788 function htsec_disable_user_endpoints( $endpoints ) {
    789     if ( ! get_option( 'htsec_disable_user_enumeration', 0 ) ) {
    790         return $endpoints;
    791     }
    792    
    793     if ( isset( $endpoints['/wp/v2/users'] ) ) {
    794         unset( $endpoints['/wp/v2/users'] );
    795     }
    796     if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
    797         unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
    798     }
    799    
    800     return $endpoints;
    801 }
    802 
    803 /**
    804  * Bloqueia acesso a parâmetros de autor para evitar enumeração.
    805  */
    806 add_action( 'init', 'htsec_block_author_enumeration' );
    807 function htsec_block_author_enumeration() {
    808     if ( ! get_option( 'htsec_disable_user_enumeration', 0 ) ) {
    809         return;
    810     }
    811    
    812     // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Not processing form data
    813     if ( isset( $_GET['author'] ) && is_numeric( $_GET['author'] ) ) {
    814         wp_safe_redirect( home_url(), 301 );
    815         exit;
    816     }
    817 }
    818 
    819 /**
    820  * Implementa modo de manutenção com IP whitelist.
    821  */
    822 add_action( 'init', 'htsec_maintenance_mode' );
    823 function htsec_maintenance_mode() {
    824     if ( ! get_option( 'htsec_maintenance_mode', 0 ) ) {
    825         return;
    826     }
    827 
    828     if ( current_user_can( 'manage_options' ) ) {
    829         return;
    830     }
    831 
    832     $allowed_ips = get_option( 'htsec_maintenance_ips', '' );
    833     $allowed_ips = array_filter( array_map( 'trim', explode( "\n", $allowed_ips ) ) );
    834     $current_ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '' ) );
    835 
    836     if ( ! empty( $allowed_ips ) && in_array( $current_ip, $allowed_ips, true ) ) {
    837         return;
    838     }
    839 
    840     $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
    841 
    842     if ( ! is_admin() && ! strpos( $request_uri, '/wp-login.php' ) ) {
    843         wp_die(
    844             esc_html__( 'Site em Manutenção', 'ht-security' ),
    845             esc_html__( 'Manutenção', 'ht-security' ),
    846             [ 'response' => 503 ]
    847         );
    848     }
    849 }
    850 
    851 /**
    852  * Exibe a verificação de vulnerabilidades CVE.
    853  */
    854 function htsec_display_cve_check() {
    855     // Informação sobre a funcionalidade
    856     $api_key = get_option( 'htsec_nvd_api_key', '' );
    857     $rate_limit = empty( $api_key ) ? 5 : 50;
    858 
    859     echo '<div class="htsec-check-info">';
    860     echo '<p style="margin: 0 0 10px 0;">' . esc_html__( 'Esta funcionalidade verifica se há vulnerabilidades conhecidas (CVEs) nas versões instaladas do WordPress e plugins ativos.', 'ht-security' ) . '</p>';
    861     /* translators: %d: Number of requests allowed per 30 seconds */
    862     echo '<p style="margin: 0;"><strong>' . sprintf( esc_html__( 'Limite atual: %d verificações a cada 30 segundos', 'ht-security' ), esc_html( $rate_limit ) ) . '</strong></p>';
    863     echo '</div>';
    864 
    865     // Botão para forçar verificação manual
    866     if ( isset( $_POST['htsec_check_cve_now'] ) && isset( $_POST['htsec_cve_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['htsec_cve_nonce'] ) ), 'htsec_check_cve' ) ) {
    867         // Limpar progresso anterior
    868         delete_option( 'htsec_check_progress' );
    869 
    870         echo '<div class="notice notice-info" style="position: relative;">';
    871         echo '<p><strong>' . esc_html__( 'Verificação em andamento...', 'ht-security' ) . '</strong></p>';
    872         echo '</div>';
    873 
    874         // Forçar flush do output para mostrar a mensagem
    875         if ( function_exists( 'wp_ob_end_flush_all' ) ) {
    876             wp_ob_end_flush_all();
    877         }
    878         flush();
    879 
    880         $stats = htsec_check_vulnerabilities( true );
    881 
    882         // Exibir progresso
    883         $progress = get_option( 'htsec_check_progress', [] );
    884         if ( ! empty( $progress ) ) {
    885             echo '<div class="notice notice-info">';
    886             echo '<p><strong>' . esc_html__( 'Progresso da Verificação:', 'ht-security' ) . '</strong></p>';
    887             echo '<div style="margin: 10px 0;">';
    888             foreach ( $progress as $entry ) {
    889                 echo '<div class="htsec-progress-item">' . esc_html( $entry['message'] ) . '</div>';
    890             }
    891             echo '</div>';
    892             echo '</div>';
    893         }
    894 
    895         // Limpar progresso após exibir
    896         delete_option( 'htsec_check_progress' );
    897 
    898         echo '<div class="notice notice-success">';
    899         echo '<p><strong>' . esc_html__( 'Verificação concluída!', 'ht-security' ) . '</strong></p>';
    900         echo '<div class="htsec-stats-box">';
    901         /* translators: %d: Total items checked */
    902         echo '<p style="margin: 0 0 8px 0;"><strong>' . esc_html__( 'Estatísticas:', 'ht-security' ) . '</strong></p>';
    903         /* translators: %d: Number of software items */
    904         echo '<p style="margin: 0 0 5px 0;">• ' . sprintf( esc_html__( 'Softwares verificados: %d', 'ht-security' ), esc_html( $stats['total_checked'] ) ) . '</p>';
    905         /* translators: %d: Number of batches */
    906         echo '<p style="margin: 0 0 5px 0;">• ' . sprintf( esc_html__( 'Lotes processados: %d', 'ht-security' ), esc_html( $stats['batches_processed'] ) ) . '</p>';
    907         /* translators: %d: Number of vulnerabilities */
    908         echo '<p style="margin: 0;">• ' . sprintf( esc_html__( 'Vulnerabilidades encontradas: %d', 'ht-security' ), esc_html( $stats['vulnerabilities_found'] ) ) . '</p>';
    909         echo '</div>';
    910         echo '</div>';
    911     }
    912 
    913     // Obter dados de vulnerabilidades armazenados
    914     $vulnerabilities = get_option( 'htsec_vulnerabilities', [] );
    915     $last_check = get_option( 'htsec_last_cve_check', 0 );
    916 
    917     if ( $last_check ) {
    918         $time_diff = human_time_diff( $last_check, current_time( 'timestamp' ) );
    919         /* translators: %s: Time since last check */
    920         echo '<p class="description">' . sprintf( esc_html__( 'Última verificação: %s atrás', 'ht-security' ), esc_html( $time_diff ) ) . '</p>';
    921     } else {
    922         echo '<p class="description">' . esc_html__( 'Nenhuma verificação realizada ainda. Clique no botão abaixo para verificar agora.', 'ht-security' ) . '</p>';
    923     }
    924 
    925     // Exibir estatísticas
    926     if ( ! function_exists( 'get_plugins' ) ) {
    927         require_once ABSPATH . 'wp-admin/includes/plugin.php';
    928     }
    929     $active_plugins = get_option( 'active_plugins', [] );
    930     $total_to_check = count( $active_plugins ) + 1; // +1 para WordPress
    931     $batches_needed = ceil( $total_to_check / $rate_limit );
    932 
    933     if ( $batches_needed > 1 ) {
    934         $estimated_time = ( $batches_needed - 1 ) * 30; // 30 segundos entre batches
    935         echo '<p class="description">';
    936         echo sprintf(
    937             /* translators: 1: Number of batches, 2: Estimated time in seconds */
    938             esc_html__( 'Esta verificação será dividida em %1$d lote(s). Tempo estimado: ~%2$d segundos', 'ht-security' ),
    939             esc_html( $batches_needed ),
    940             esc_html( $estimated_time )
    941         );
    942         echo '</p>';
    943     }
    944 
    945     // Exibir resultados
    946     if ( empty( $vulnerabilities ) ) {
    947         echo '<div class="htsec-alert-success">';
    948         echo '<p>✓ ' . esc_html__( 'Nenhuma vulnerabilidade conhecida detectada nas versões atuais!', 'ht-security' ) . '</p>';
    949         echo '</div>';
    950     } else {
    951         echo '<div class="htsec-alert-danger">';
    952         /* translators: %d: Number of vulnerabilities found */
    953         echo '<p>⚠ ' . sprintf( esc_html( _n( '%d vulnerabilidade encontrada!', '%d vulnerabilidades encontradas!', count( $vulnerabilities ), 'ht-security' ) ), count( $vulnerabilities ) ) . '</p>';
    954         echo '</div>';
    955 
    956         htsec_display_vulnerabilities( $vulnerabilities );
    957     }
    958 
    959     // Botão de verificação manual
    960     echo '<form method="post" style="margin-top: 20px;">';
    961     wp_nonce_field( 'htsec_check_cve', 'htsec_cve_nonce' );
    962     submit_button( esc_html__( 'Verificar Vulnerabilidades Agora', 'ht-security' ), 'primary', 'htsec_check_cve_now', false );
    963     echo '</form>';
    964 }
    965 
    966 /**
    967  * Exibe as vulnerabilidades encontradas.
    968  */
    969 function htsec_display_vulnerabilities( $vulnerabilities ) {
    970     echo '<div style="margin: 20px 0;">';
    971 
    972     foreach ( $vulnerabilities as $vuln ) {
    973         $severity = isset( $vuln['severity'] ) ? $vuln['severity'] : 'UNKNOWN';
    974         $severity_colors = [
    975             'CRITICAL' => '#8b0000',
    976             'HIGH' => '#dc3545',
    977             'MEDIUM' => '#ffc107',
    978             'LOW' => '#28a745',
    979             'UNKNOWN' => '#6c757d',
    980         ];
    981         $severity_color = isset( $severity_colors[ $severity ] ) ? $severity_colors[ $severity ] : $severity_colors['UNKNOWN'];
    982 
    983         echo '<div class="htsec-vuln-card">';
    984         echo '<div class="htsec-vuln-header">';
    985         echo '<h3 class="htsec-vuln-title">' . esc_html( $vuln['software'] ) . ' ' . esc_html( $vuln['version'] ) . '</h3>';
    986         echo '<span class="htsec-severity-badge" style="background-color: ' . esc_attr( $severity_color ) . '; color: white;">' . esc_html( $severity ) . '</span>';
    987         echo '</div>';
    988 
    989         echo '<div class="htsec-vuln-detail">';
    990         echo '<strong>' . esc_html__( 'CVE:', 'ht-security' ) . '</strong> ';
    991         echo '<a href="' . esc_url( $vuln['cve_url'] ) . '" target="_blank" class="htsec-cve-link" rel="noopener noreferrer">';
    992         echo esc_html( $vuln['cve_id'] );
    993         echo ' <span class="dashicons dashicons-external" style="font-size: 14px; vertical-align: middle;"></span>';
    994         echo '</a>';
    995         echo '</div>';
    996 
    997         if ( isset( $vuln['cvss_score'] ) ) {
    998             echo '<div class="htsec-vuln-detail">';
    999             echo '<strong>' . esc_html__( 'CVSS Score:', 'ht-security' ) . '</strong> ';
    1000             echo '<span class="htsec-cvss-score">' . esc_html( $vuln['cvss_score'] ) . '</span>';
    1001             echo '</div>';
    1002         }
    1003 
    1004         if ( ! empty( $vuln['description'] ) ) {
    1005             echo '<div class="htsec-vuln-detail">';
    1006             echo '<strong>' . esc_html__( 'Descrição:', 'ht-security' ) . '</strong>';
    1007             echo '<div class="htsec-vuln-description">' . esc_html( $vuln['description'] ) . '</div>';
    1008             echo '</div>';
    1009         }
    1010 
    1011         echo '</div>';
    1012     }
    1013 
    1014     echo '</div>';
    1015 }
    1016 
    1017 /**
    1018  * Verifica vulnerabilidades nos plugins e WordPress.
    1019  */
    1020 function htsec_check_vulnerabilities( $manual = false ) {
    1021     $api_key = get_option( 'htsec_nvd_api_key', '' );
    1022     $vulnerabilities = [];
    1023 
    1024     // Definir rate limits
    1025     $rate_limit = empty( $api_key ) ? 5 : 50; // 5 requests/30s sem key, 50/30s com key
    1026     $batch_delay = 30; // 30 segundos entre batches
    1027 
    1028     // Coletar todos os softwares a serem verificados
    1029     $software_to_check = [];
    1030 
    1031     // Adicionar WordPress
    1032     $software_to_check[] = [
    1033         'name' => 'WordPress',
    1034         'version' => get_bloginfo( 'version' ),
    1035         'type' => 'core',
    1036     ];
    1037 
    1038     // Adicionar plugins ativos
    1039     if ( ! function_exists( 'get_plugins' ) ) {
    1040         require_once ABSPATH . 'wp-admin/includes/plugin.php';
    1041     }
    1042 
    1043     $all_plugins = get_plugins();
    1044     $active_plugins = get_option( 'active_plugins', [] );
    1045 
    1046     foreach ( $all_plugins as $plugin_file => $plugin_data ) {
    1047         // Verificar apenas plugins ativos
    1048         if ( ! in_array( $plugin_file, $active_plugins, true ) ) {
    1049             continue;
    1050         }
    1051 
    1052         $software_to_check[] = [
    1053             'name' => $plugin_data['Name'],
    1054             'version' => $plugin_data['Version'],
    1055             'type' => 'plugin',
    1056         ];
    1057     }
    1058 
    1059     $total_items = count( $software_to_check );
    1060     $total_batches = ceil( $total_items / $rate_limit );
    1061     $current_batch = 0;
    1062 
    1063     // Processar em batches
    1064     for ( $i = 0; $i < $total_items; $i += $rate_limit ) {
    1065         $current_batch++;
    1066         $batch = array_slice( $software_to_check, $i, $rate_limit );
    1067 
    1068         // Feedback de progresso para verificação manual
    1069         if ( $manual ) {
    1070             $processed = min( $i + $rate_limit, $total_items );
    1071             htsec_log_progress( sprintf(
    1072                 /* translators: 1: Current progress, 2: Total items */
    1073                 esc_html__( 'Verificando %1$d de %2$d softwares...', 'ht-security' ),
    1074                 $processed,
    1075                 $total_items
    1076             ) );
    1077         }
    1078 
    1079         // Processar cada item do batch
    1080         foreach ( $batch as $software ) {
    1081             $vulns = htsec_query_nvd_api( $software['name'], $software['version'], $api_key );
    1082             if ( ! empty( $vulns ) ) {
    1083                 $vulnerabilities = array_merge( $vulnerabilities, $vulns );
    1084             }
    1085 
    1086             // Pequeno delay entre requisições do mesmo batch para não sobrecarregar
    1087             if ( ! empty( $api_key ) ) {
    1088                 usleep( 100000 ); // 0.1 segundo com API Key
    1089             } else {
    1090                 usleep( 500000 ); // 0.5 segundo sem API Key
    1091             }
    1092         }
    1093 
    1094         // Se não for o último batch, esperar 30 segundos
    1095         if ( $current_batch < $total_batches ) {
    1096             if ( $manual ) {
    1097                 htsec_log_progress( sprintf(
    1098                     /* translators: %d: Seconds to wait */
    1099                     esc_html__( 'Aguardando %d segundos (limite da API)...', 'ht-security' ),
    1100                     $batch_delay
    1101                 ) );
    1102             }
    1103             sleep( $batch_delay );
    1104         }
    1105     }
    1106 
    1107     // Armazenar resultados
    1108     update_option( 'htsec_vulnerabilities', $vulnerabilities );
    1109     update_option( 'htsec_last_cve_check', current_time( 'timestamp' ) );
    1110 
    1111     // Enviar e-mail se habilitado e houver vulnerabilidades
    1112     if ( ! empty( $vulnerabilities ) && get_option( 'htsec_enable_cve_alerts', 0 ) ) {
    1113         htsec_send_cve_alert( $vulnerabilities );
    1114     }
    1115 
    1116     return [
    1117         'total_checked' => $total_items,
    1118         'vulnerabilities_found' => count( $vulnerabilities ),
    1119         'batches_processed' => $current_batch,
    1120     ];
    1121 }
    1122 
    1123 /**
    1124  * Registra progresso da verificação (para exibição em verificações manuais).
    1125  */
    1126 function htsec_log_progress( $message ) {
    1127     $progress = get_option( 'htsec_check_progress', [] );
    1128     $progress[] = [
    1129         'message' => $message,
    1130         'time' => current_time( 'mysql' ),
    1131     ];
    1132     update_option( 'htsec_check_progress', $progress );
    1133 }
    1134 
    1135 /**
    1136  * Consulta a API NVD para buscar CVEs.
    1137  */
    1138 function htsec_query_nvd_api( $software_name, $version, $api_key = '' ) {
    1139     $vulnerabilities = [];
    1140 
    1141     // Para WordPress, usar busca específica
    1142     if ( 'WordPress' === $software_name ) {
    1143         $search_term = 'WordPress Core ' . $version;
    1144     } else {
    1145         // Para plugins, usar nome específico
    1146         $search_term = sanitize_text_field( $software_name );
    1147     }
    1148 
    1149     $url = add_query_arg( [
    1150         'keywordSearch' => rawurlencode( $search_term ),
    1151         'keywordExactMatch' => '', // Busca exata da frase
    1152         'resultsPerPage' => 20,
    1153     ], 'https://services.nvd.nist.gov/rest/json/cves/2.0' );
    1154 
    1155     $headers = [ 'timeout' => 30 ];
    1156 
    1157     if ( ! empty( $api_key ) ) {
    1158         $headers['headers'] = [
    1159             'apiKey' => sanitize_text_field( $api_key ),
    1160         ];
    1161     }
    1162 
    1163     $response = wp_remote_get( $url, $headers );
    1164 
    1165     if ( is_wp_error( $response ) ) {
    1166         return $vulnerabilities;
    1167     }
    1168 
    1169     $response_code = wp_remote_retrieve_response_code( $response );
    1170     if ( 200 !== $response_code ) {
    1171         return $vulnerabilities;
    1172     }
    1173 
    1174     $body = wp_remote_retrieve_body( $response );
    1175     $data = json_decode( $body, true );
    1176 
    1177     if ( empty( $data['vulnerabilities'] ) ) {
    1178         return $vulnerabilities;
    1179     }
    1180 
    1181     foreach ( $data['vulnerabilities'] as $vuln ) {
    1182         if ( empty( $vuln['cve'] ) ) {
    1183             continue;
    1184         }
    1185 
    1186         $cve = $vuln['cve'];
    1187         $cve_id = isset( $cve['id'] ) ? $cve['id'] : '';
    1188 
    1189         if ( empty( $cve_id ) ) {
    1190             continue;
    1191         }
    1192 
    1193         // FILTRO: CVEs muito antigas (antes de 2010) - plugins WordPress modernos não são afetados
    1194         // CVE-ID formato: CVE-YYYY-NNNNN
    1195         if ( preg_match( '/CVE-(\d{4})-/', $cve_id, $matches ) ) {
    1196             $cve_year = (int) $matches[1];
    1197             if ( $cve_year < 2010 ) {
    1198                 // CVEs antes de 2010 raramente afetam plugins WordPress modernos
    1199                 continue;
    1200             }
    1201         }
    1202 
    1203         // Extrair descrição
    1204         $description = '';
    1205         if ( isset( $cve['descriptions'] ) && is_array( $cve['descriptions'] ) ) {
    1206             foreach ( $cve['descriptions'] as $desc ) {
    1207                 if ( isset( $desc['lang'] ) && 'en' === $desc['lang'] ) {
    1208                     $description = $desc['value'];
    1209                     break;
    1210                 }
    1211             }
    1212         }
    1213 
    1214         // FILTRO: Descrição vazia ou muito curta
    1215         if ( empty( $description ) || strlen( $description ) < 50 ) {
    1216             continue;
    1217         }
    1218 
    1219         // ANTI-FALSO POSITIVO: Validar se a CVE realmente se aplica ao software
    1220         if ( ! htsec_validate_cve_match( $software_name, $description, $cve ) ) {
    1221             continue;
    1222         }
    1223 
    1224         // VALIDAÇÃO DE VERSÃO: Verificar se a versão instalada é vulnerável
    1225         if ( ! htsec_version_is_vulnerable( $version, $cve ) ) {
    1226             continue;
    1227         }
    1228 
    1229         // Extrair severidade e score
    1230         $severity = 'UNKNOWN';
    1231         $cvss_score = null;
    1232 
    1233         if ( isset( $cve['metrics'] ) ) {
    1234             $metrics = $cve['metrics'];
    1235 
    1236             // Tentar CVSS v3.1 primeiro
    1237             if ( isset( $metrics['cvssMetricV31'][0] ) ) {
    1238                 $cvss = $metrics['cvssMetricV31'][0];
    1239                 $severity = isset( $cvss['cvssData']['baseSeverity'] ) ? $cvss['cvssData']['baseSeverity'] : 'UNKNOWN';
    1240                 $cvss_score = isset( $cvss['cvssData']['baseScore'] ) ? $cvss['cvssData']['baseScore'] : null;
    1241             } elseif ( isset( $metrics['cvssMetricV30'][0] ) ) {
    1242                 $cvss = $metrics['cvssMetricV30'][0];
    1243                 $severity = isset( $cvss['cvssData']['baseSeverity'] ) ? $cvss['cvssData']['baseSeverity'] : 'UNKNOWN';
    1244                 $cvss_score = isset( $cvss['cvssData']['baseScore'] ) ? $cvss['cvssData']['baseScore'] : null;
    1245             } elseif ( isset( $metrics['cvssMetricV2'][0] ) ) {
    1246                 $cvss = $metrics['cvssMetricV2'][0];
    1247                 $severity = isset( $cvss['baseSeverity'] ) ? $cvss['baseSeverity'] : 'UNKNOWN';
    1248                 $cvss_score = isset( $cvss['cvssData']['baseScore'] ) ? $cvss['cvssData']['baseScore'] : null;
    1249             }
    1250         }
    1251 
    1252         $vulnerabilities[] = [
    1253             'software' => $software_name,
    1254             'version' => $version,
    1255             'cve_id' => $cve_id,
    1256             'cve_url' => 'https://nvd.nist.gov/vuln/detail/' . $cve_id,
    1257             'description' => wp_trim_words( $description, 50 ),
    1258             'full_description' => $description,
    1259             'severity' => $severity,
    1260             'cvss_score' => $cvss_score,
    1261         ];
    1262     }
    1263 
    1264     return $vulnerabilities;
    1265 }
    1266 
    1267 /**
    1268  * Verifica se a versão instalada está vulnerável baseada nos dados da CVE.
    1269  */
    1270 function htsec_version_is_vulnerable( $installed_version, $cve_data ) {
    1271     // Se não tiver versão instalada, não pode validar
    1272     if ( empty( $installed_version ) ) {
    1273         return false;
    1274     }
    1275 
    1276     // Normalizar versão instalada (remover v, V, etc.)
    1277     $installed_version = strtolower( trim( $installed_version ) );
    1278     $installed_version = preg_replace( '/^v/', '', $installed_version );
    1279 
    1280     // Extrair versões afetadas das configurações da CVE
    1281     $version_ranges = htsec_extract_version_ranges( $cve_data );
    1282 
    1283     // Se não conseguiu extrair ranges, tentar da descrição
    1284     if ( empty( $version_ranges ) ) {
    1285         $description = '';
    1286         if ( isset( $cve_data['descriptions'] ) && is_array( $cve_data['descriptions'] ) ) {
    1287             foreach ( $cve_data['descriptions'] as $desc ) {
    1288                 if ( isset( $desc['lang'] ) && 'en' === $desc['lang'] ) {
    1289                     $description = $desc['value'];
    1290                     break;
    1291                 }
    1292             }
    1293         }
    1294         $version_ranges = htsec_extract_version_from_description( $description );
    1295     }
    1296 
    1297     // Se ainda não tem ranges, não pode validar (assume que pode ser vulnerável para ser conservador)
    1298     if ( empty( $version_ranges ) ) {
    1299         return false; // Mudado para false - se não sabemos, não reportamos
    1300     }
    1301 
    1302     // Verificar se a versão instalada está em algum dos ranges vulneráveis
    1303     foreach ( $version_ranges as $range ) {
    1304         if ( htsec_version_in_range( $installed_version, $range ) ) {
    1305             return true;
    1306         }
    1307     }
    1308 
    1309     return false;
    1310 }
    1311 
    1312 /**
    1313  * Extrai ranges de versões afetadas dos dados da CVE.
    1314  */
    1315 function htsec_extract_version_ranges( $cve_data ) {
    1316     $ranges = [];
    1317 
    1318     if ( ! isset( $cve_data['configurations'] ) || ! is_array( $cve_data['configurations'] ) ) {
    1319         return $ranges;
    1320     }
    1321 
    1322     foreach ( $cve_data['configurations'] as $config ) {
    1323         if ( ! isset( $config['nodes'] ) || ! is_array( $config['nodes'] ) ) {
    1324             continue;
    1325         }
    1326 
    1327         foreach ( $config['nodes'] as $node ) {
    1328             if ( ! isset( $node['cpeMatch'] ) || ! is_array( $node['cpeMatch'] ) ) {
    1329                 continue;
    1330             }
    1331 
    1332             foreach ( $node['cpeMatch'] as $match ) {
    1333                 if ( ! isset( $match['vulnerable'] ) || ! $match['vulnerable'] ) {
    1334                     continue;
    1335                 }
    1336 
    1337                 $range = [
    1338                     'start_version' => null,
    1339                     'end_version' => null,
    1340                     'start_including' => true,
    1341                     'end_including' => true,
    1342                 ];
    1343 
    1344                 if ( isset( $match['versionStartIncluding'] ) ) {
    1345                     $range['start_version'] = $match['versionStartIncluding'];
    1346                     $range['start_including'] = true;
    1347                 } elseif ( isset( $match['versionStartExcluding'] ) ) {
    1348                     $range['start_version'] = $match['versionStartExcluding'];
    1349                     $range['start_including'] = false;
    1350                 }
    1351 
    1352                 if ( isset( $match['versionEndIncluding'] ) ) {
    1353                     $range['end_version'] = $match['versionEndIncluding'];
    1354                     $range['end_including'] = true;
    1355                 } elseif ( isset( $match['versionEndExcluding'] ) ) {
    1356                     $range['end_version'] = $match['versionEndExcluding'];
    1357                     $range['end_including'] = false;
    1358                 }
    1359 
    1360                 // Se tem pelo menos uma versão de limite, adiciona o range
    1361                 if ( $range['start_version'] !== null || $range['end_version'] !== null ) {
    1362                     $ranges[] = $range;
    1363                 }
    1364             }
    1365         }
    1366     }
    1367 
    1368     return $ranges;
    1369 }
    1370 
    1371 /**
    1372  * Extrai informação de versão da descrição da CVE.
    1373  */
    1374 function htsec_extract_version_from_description( $description ) {
    1375     $ranges = [];
    1376 
    1377     if ( empty( $description ) ) {
    1378         return $ranges;
    1379     }
    1380 
    1381     $description_lower = strtolower( $description );
    1382 
    1383     // Padrões comuns:
    1384     // "before X.X.X", "prior to X.X.X", "up to X.X.X", "through X.X.X"
    1385     // "versions up to, and including, X.X.X"
    1386     // "in versions up to X.X.X"
    1387 
    1388     $patterns = [
    1389         '/(?:before|prior to|up to|through)\s+(?:version\s+)?v?(\d+\.\d+(?:\.\d+)?)/i',
    1390         '/versions?\s+up\s+to,?\s+(?:and\s+)?including,?\s+v?(\d+\.\d+(?:\.\d+)?)/i',
    1391         '/in\s+versions?\s+up\s+to\s+v?(\d+\.\d+(?:\.\d+)?)/i',
    1392         '/(?:version|versions?)\s+v?(\d+\.\d+(?:\.\d+)?)\s+and\s+(?:below|earlier)/i',
    1393     ];
    1394 
    1395     foreach ( $patterns as $pattern ) {
    1396         if ( preg_match( $pattern, $description, $matches ) ) {
    1397             $version = $matches[1];
    1398 
    1399             // Determinar se é inclusive ou exclusive baseado nas palavras
    1400             $is_including = (
    1401                 strpos( $description_lower, 'including' ) !== false ||
    1402                 strpos( $description_lower, 'through' ) !== false
    1403             );
    1404 
    1405             $ranges[] = [
    1406                 'start_version' => null,
    1407                 'end_version' => $version,
    1408                 'start_including' => true,
    1409                 'end_including' => $is_including,
    1410             ];
    1411 
    1412             break; // Pega apenas o primeiro match
    1413         }
    1414     }
    1415 
    1416     return $ranges;
    1417 }
    1418 
    1419 /**
    1420  * Verifica se uma versão está dentro de um range específico.
    1421  */
    1422 function htsec_version_in_range( $version, $range ) {
    1423     // Verificar limite inferior
    1424     if ( $range['start_version'] !== null ) {
    1425         $comparison = version_compare( $version, $range['start_version'] );
    1426 
    1427         if ( $range['start_including'] ) {
    1428             // Versão deve ser >= start
    1429             if ( $comparison < 0 ) {
    1430                 return false;
    1431             }
    1432         } else {
    1433             // Versão deve ser > start
    1434             if ( $comparison <= 0 ) {
    1435                 return false;
    1436             }
    1437         }
    1438     }
    1439 
    1440     // Verificar limite superior
    1441     if ( $range['end_version'] !== null ) {
    1442         $comparison = version_compare( $version, $range['end_version'] );
    1443 
    1444         if ( $range['end_including'] ) {
    1445             // Versão deve ser <= end
    1446             if ( $comparison > 0 ) {
    1447                 return false;
    1448             }
    1449         } else {
    1450             // Versão deve ser < end
    1451             if ( $comparison >= 0 ) {
    1452                 return false;
    1453             }
    1454         }
    1455     }
    1456 
    1457     return true;
    1458 }
    1459 
    1460 /**
    1461  * Valida se uma CVE realmente corresponde ao software especificado.
    1462  */
    1463 function htsec_validate_cve_match( $software_name, $description, $cve_data ) {
    1464     $software_lower = strtolower( $software_name );
    1465     $description_lower = strtolower( $description );
    1466 
    1467     // FILTRO 1: Para WordPress Core
    1468     if ( 'WordPress' === $software_name ) {
    1469         // Deve mencionar explicitamente "wordpress"
    1470         if ( strpos( $description_lower, 'wordpress' ) === false ) {
    1471             return false;
    1472         }
    1473 
    1474         // Não deve ser sobre plugin ou tema
    1475         if ( strpos( $description_lower, 'plugin' ) !== false || strpos( $description_lower, 'theme' ) !== false ) {
    1476             return false;
    1477         }
    1478 
    1479         return true;
    1480     }
    1481 
    1482     // FILTRO 2: Plataformas não-WordPress (falsos positivos comuns)
    1483     $non_wp_platforms = [
    1484         'drupal', 'joomla', 'magento', 'prestashop', 'opencart',
    1485         'chrome', 'firefox', 'safari', 'edge', 'browser',
    1486         'google chrome', 'mozilla', 'internet explorer',
    1487         'android', 'ios', 'windows', 'linux', 'macos',
    1488         'apache', 'nginx', 'iis', 'tomcat',
    1489         'java', 'python', 'ruby', '.net', 'php-fpm',
    1490     ];
    1491 
    1492     foreach ( $non_wp_platforms as $platform ) {
    1493         if ( strpos( $description_lower, $platform ) !== false ) {
    1494             // Se menciona outra plataforma, só aceita se mencionar WordPress também
    1495             if ( strpos( $description_lower, 'wordpress' ) === false ) {
    1496                 return false;
    1497             }
    1498         }
    1499     }
    1500 
    1501     // FILTRO 3: Extrair nome EXATO do plugin da descrição
    1502     $cve_plugin_name = htsec_extract_plugin_name_from_description( $description );
    1503 
    1504     if ( empty( $cve_plugin_name ) ) {
    1505         // Se não conseguiu extrair o nome do plugin, não é uma CVE válida de WordPress plugin
    1506         return false;
    1507     }
    1508 
    1509     $cve_plugin_lower = strtolower( $cve_plugin_name );
    1510 
    1511     // FILTRO 4: Variações de Licença (Free vs Pro/Premium/Lite)
    1512     // Ex: "Rank Math SEO" vs "Rank Math SEO PRO" são plugins DIFERENTES
    1513     $license_variants = [
    1514         'pro', 'premium', 'lite', 'free',
    1515         'professional', 'ultimate', 'business',
    1516         'plus', 'advanced', 'elite',
    1517     ];
    1518 
    1519     // Verificar se a CVE menciona uma variante de licença
    1520     $cve_has_license_variant = false;
    1521     $cve_license_type = '';
    1522     foreach ( $license_variants as $variant ) {
    1523         if ( strpos( $cve_plugin_lower, ' ' . $variant ) !== false ||
    1524              strpos( $cve_plugin_lower, $variant . ' ' ) !== false ||
    1525              preg_match( '/\b' . preg_quote( $variant, '/' ) . '\b/', $cve_plugin_lower ) ) {
    1526             $cve_has_license_variant = true;
    1527             $cve_license_type = $variant;
    1528             break;
    1529         }
    1530     }
    1531 
    1532     // Verificar se o plugin instalado tem variante de licença
    1533     $software_has_license_variant = false;
    1534     $software_license_type = '';
    1535     foreach ( $license_variants as $variant ) {
    1536         if ( strpos( $software_lower, ' ' . $variant ) !== false ||
    1537              strpos( $software_lower, $variant . ' ' ) !== false ||
    1538              preg_match( '/\b' . preg_quote( $variant, '/' ) . '\b/', $software_lower ) ) {
    1539             $software_has_license_variant = true;
    1540             $software_license_type = $variant;
    1541             break;
    1542         }
    1543     }
    1544 
    1545     // REGRA: Se a CVE menciona uma variante mas o plugin instalado tem variante diferente (ou não tem)
    1546     // Ex: CVE sobre "Rank Math SEO PRO" vs plugin instalado "Rank Math SEO" (sem PRO)
    1547     if ( $cve_has_license_variant && $software_has_license_variant ) {
    1548         // Ambos têm variante - devem ser a MESMA
    1549         if ( $cve_license_type !== $software_license_type ) {
    1550             return false; // Variantes diferentes - FALSO POSITIVO
    1551         }
    1552     } elseif ( $cve_has_license_variant && ! $software_has_license_variant ) {
    1553         // CVE tem variante mas plugin instalado não tem - FALSO POSITIVO
    1554         // Ex: "Rank Math SEO PRO" (CVE) vs "Rank Math SEO" (instalado)
    1555         return false;
    1556     } elseif ( ! $cve_has_license_variant && $software_has_license_variant ) {
    1557         // Plugin instalado tem variante mas CVE não menciona
    1558         // Isso pode ser válido (CVE genérica que afeta todas as versões)
    1559         // Continuar validação
    1560     }
    1561 
    1562     // FILTRO 5: Comparação de nome EXATA ou muito próxima
    1563     // O nome extraído deve SER o mesmo ou CONTER o nome do plugin instalado
    1564 
    1565     // Normalizar nomes removendo caracteres especiais
    1566     $software_normalized = preg_replace( '/[^a-z0-9]/', '', $software_lower );
    1567     $cve_normalized = preg_replace( '/[^a-z0-9]/', '', $cve_plugin_lower );
    1568 
    1569     // Caso 1: Nome exatamente igual (normalizado)
    1570     if ( $software_normalized === $cve_normalized ) {
    1571         return true;
    1572     }
    1573 
    1574     // Caso 2: O nome da CVE é uma variação exata do nome do plugin
    1575     // Ex: "AMP" (plugin) vs "PWA for WP & AMP" (CVE) - FALSO POSITIVO
    1576     // Ex: "Elementor Pro" vs "Essential Addons for Elementor Pro" - FALSO POSITIVO
    1577 
    1578     // FILTRO 6: Verificar se é um addon/extensão de outro plugin
    1579     $addon_indicators = [
    1580         'addon', 'addons', 'add-on', 'add-ons',
    1581         'extension', 'extensions',
    1582         'for', // "Addons FOR Elementor"
    1583     ];
    1584 
    1585     $has_addon_indicator = false;
    1586     foreach ( $addon_indicators as $indicator ) {
    1587         if ( strpos( $cve_plugin_lower, $indicator ) !== false ) {
    1588             $has_addon_indicator = true;
    1589             break;
    1590         }
    1591     }
    1592 
    1593     // Se a CVE menciona addon mas o plugin instalado não tem esse padrão, é falso positivo
    1594     if ( $has_addon_indicator && ! strpos( $software_lower, 'addon' ) && ! strpos( $software_lower, 'extension' ) ) {
    1595         // Verificar se o nome do plugin instalado está CONTIDO na descrição do addon
    1596         // Ex: "Elementor Pro" vs "Essential Addons for Elementor Pro"
    1597         if ( strpos( $cve_plugin_lower, $software_lower ) !== false ) {
    1598             // O nome do plugin está contido, mas é apenas parte de um addon - FALSO POSITIVO
    1599             return false;
    1600         }
    1601     }
    1602 
    1603     // FILTRO 7: Comparação de palavras-chave significativas
    1604     // Dividir ambos os nomes em palavras e comparar
    1605     $software_words = array_filter( explode( ' ', $software_lower ), function( $w ) {
    1606         return strlen( $w ) >= 3; // Palavras de pelo menos 3 caracteres
    1607     } );
    1608 
    1609     $cve_words = array_filter( explode( ' ', $cve_plugin_lower ), function( $w ) {
    1610         return strlen( $w ) >= 3;
    1611     } );
    1612 
    1613     // Remover palavras genéricas/comuns que causam falsos positivos
    1614     $generic_words = ['wordpress', 'plugin', 'theme', 'for', 'and', 'the', 'inc', 'pro', 'lite', 'free'];
    1615 
    1616     $software_words = array_diff( $software_words, $generic_words );
    1617     $cve_words = array_diff( $cve_words, $generic_words );
    1618 
    1619     // Se não há palavras significativas, não pode validar
    1620     if ( empty( $software_words ) || empty( $cve_words ) ) {
    1621         return false;
    1622     }
    1623 
    1624     // Contar quantas palavras do plugin instalado aparecem na CVE
    1625     $matching_words = 0;
    1626     foreach ( $software_words as $word ) {
    1627         if ( in_array( $word, $cve_words, true ) ) {
    1628             $matching_words++;
    1629         }
    1630     }
    1631 
    1632     // REGRA: Pelo menos 80% das palavras significativas devem corresponder
    1633     $match_percentage = ( $matching_words / count( $software_words ) ) * 100;
    1634 
    1635     if ( $match_percentage < 80 ) {
    1636         return false;
    1637     }
    1638 
    1639     // FILTRO 8: Se a CVE tem MAIS palavras que o plugin instalado, pode ser um addon
    1640     // Ex: "AMP" (1 palavra) vs "PWA for WP & AMP" (5 palavras) - muito diferente
    1641     $word_count_ratio = count( $cve_words ) / max( count( $software_words ), 1 );
    1642 
    1643     if ( $word_count_ratio > 2.0 ) {
    1644         // A CVE tem mais que o dobro de palavras - provavelmente é outro plugin
    1645         return false;
    1646     }
    1647 
    1648     // Se passou por todos os filtros, é uma correspondência válida
    1649     return true;
    1650 }
    1651 
    1652 /**
    1653  * Extrai o nome do plugin da descrição da CVE.
    1654  */
    1655 function htsec_extract_plugin_name_from_description( $description ) {
    1656     // Padrões comuns de nomeação em descrições de CVE para WordPress plugins:
    1657     // "The [Plugin Name] WordPress plugin"
    1658     // "The [Plugin Name] plugin for WordPress"
    1659     // "[Plugin Name] plugin"
    1660     // "vulnerability in [Plugin Name]"
    1661 
    1662     $patterns = [
    1663         '/the\s+(.+?)\s+wordpress\s+plugin/i',
    1664         '/the\s+(.+?)\s+plugin\s+for\s+wordpress/i',
    1665         '/vulnerability\s+in\s+(.+?)\s+allows/i',
    1666         '/vulnerability\s+in\s+(.+?)\s+plugin/i',
    1667         '/^(.+?)\s+wordpress\s+plugin/i',
    1668         '/in\s+(.+?)\s+wordpress\s+plugin/i',
    1669     ];
    1670 
    1671     foreach ( $patterns as $pattern ) {
    1672         if ( preg_match( $pattern, trim( $description ), $matches ) ) {
    1673             $extracted_name = trim( $matches[1] );
    1674 
    1675             // Remover prefixos comuns que não fazem parte do nome
    1676             $extracted_name = preg_replace( '/^(the|a|an)\s+/i', '', $extracted_name );
    1677 
    1678             // Remover sufixos de empresa/desenvolvedor
    1679             $extracted_name = preg_replace( '/\s+(inc|ltd|llc|corporation|corp)$/i', '', $extracted_name );
    1680 
    1681             return $extracted_name;
    1682         }
    1683     }
    1684 
    1685     return '';
    1686 }
    1687 
    1688 /**
    1689  * Envia alerta por e-mail sobre vulnerabilidades encontradas.
    1690  */
    1691 function htsec_send_cve_alert( $vulnerabilities ) {
    1692     $email = sanitize_email( get_option( 'htsec_alert_email', get_option( 'admin_email' ) ) );
    1693 
    1694     if ( empty( $email ) ) {
    1695         return;
    1696     }
    1697 
    1698     $subject = sprintf(
    1699         /* translators: 1: Site name, 2: Number of vulnerabilities */
    1700         esc_html__( '[%1$s] Alerta: %2$d vulnerabilidades detectadas', 'ht-security' ),
    1701         get_bloginfo( 'name' ),
    1702         count( $vulnerabilities )
    1703     );
    1704 
    1705     $message = esc_html__( 'Estimado Administrador,', 'ht-security' ) . "\n\n";
    1706     $message .= sprintf(
    1707         /* translators: %d: Number of vulnerabilities */
    1708         esc_html( _n( 'Foi detectada %d vulnerabilidade conhecida (CVE) em seu site:', 'Foram detectadas %d vulnerabilidades conhecidas (CVE) em seu site:', count( $vulnerabilities ), 'ht-security' ) ),
    1709         count( $vulnerabilities )
    1710     ) . "\n\n";
    1711 
    1712     foreach ( $vulnerabilities as $vuln ) {
    1713         $message .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n";
    1714         $message .= esc_html__( 'Software:', 'ht-security' ) . ' ' . $vuln['software'] . ' ' . $vuln['version'] . "\n";
    1715         $message .= esc_html__( 'CVE:', 'ht-security' ) . ' ' . $vuln['cve_id'] . "\n";
    1716         $message .= esc_html__( 'Severidade:', 'ht-security' ) . ' ' . $vuln['severity'] . "\n";
    1717         if ( isset( $vuln['cvss_score'] ) ) {
    1718             $message .= esc_html__( 'CVSS Score:', 'ht-security' ) . ' ' . $vuln['cvss_score'] . "\n";
    1719         }
    1720         $message .= esc_html__( 'Link:', 'ht-security' ) . ' ' . $vuln['cve_url'] . "\n";
    1721         if ( ! empty( $vuln['description'] ) ) {
    1722             $message .= esc_html__( 'Descrição:', 'ht-security' ) . ' ' . $vuln['description'] . "\n";
    1723         }
    1724         $message .= "\n";
    1725     }
    1726 
    1727     $message .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n\n";
    1728     $message .= esc_html__( 'Recomendamos que você atualize os plugins e o WordPress o mais rápido possível.', 'ht-security' ) . "\n\n";
    1729     $message .= esc_html__( 'Acesse o painel do HT Security para mais detalhes:', 'ht-security' ) . "\n";
    1730     $message .= admin_url( 'options-general.php?page=ht-security' );
    1731 
    1732     wp_mail( $email, $subject, $message );
    1733 }
    1734 
    1735 /**
    1736  * Configura o cron job para verificação automática de vulnerabilidades.
    1737  */
    1738 add_action( 'wp', 'htsec_schedule_cve_check' );
    1739 function htsec_schedule_cve_check() {
    1740     if ( ! wp_next_scheduled( 'htsec_cve_check_event' ) ) {
    1741         wp_schedule_event( time(), 'twicedaily', 'htsec_cve_check_event' );
    1742     }
    1743 }
    1744 
    1745 add_action( 'htsec_cve_check_event', 'htsec_run_scheduled_cve_check' );
    1746 function htsec_run_scheduled_cve_check() {
    1747     htsec_check_vulnerabilities();
    1748 }
    1749 
    1750 /**
    1751  * Remove o cron job ao desativar o plugin.
    1752  */
    1753 register_deactivation_hook( __FILE__, 'htsec_deactivation' );
    1754 function htsec_deactivation() {
    1755     $timestamp = wp_next_scheduled( 'htsec_cve_check_event' );
    1756     if ( $timestamp ) {
    1757         wp_unschedule_event( $timestamp, 'htsec_cve_check_event' );
    1758     }
    1759 }
    1760 
    1761 /**
    1762  * Adiciona indicador de vulnerabilidades na página de plugins.
    1763  */
    1764 add_filter( 'plugin_row_meta', 'htsec_add_plugin_vulnerability_indicator', 10, 2 );
    1765 function htsec_add_plugin_vulnerability_indicator( $plugin_meta, $plugin_file ) {
    1766     // Verificar se a opção está ativada
    1767     if ( ! get_option( 'htsec_show_plugin_badges', 0 ) ) {
    1768         return $plugin_meta;
    1769     }
    1770 
    1771     // Obter dados do plugin
    1772     if ( ! function_exists( 'get_plugin_data' ) ) {
    1773         require_once ABSPATH . 'wp-admin/includes/plugin.php';
    1774     }
    1775 
    1776     $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file );
    1777     $plugin_name = $plugin_data['Name'];
    1778 
    1779     // Verificar vulnerabilidades armazenadas
    1780     $all_vulnerabilities = get_option( 'htsec_vulnerabilities', [] );
    1781     $plugin_vulnerabilities = array_filter( $all_vulnerabilities, function( $vuln ) use ( $plugin_name ) {
    1782         return $vuln['software'] === $plugin_name;
    1783     } );
    1784 
    1785     $vuln_count = count( $plugin_vulnerabilities );
    1786 
    1787     if ( $vuln_count > 0 ) {
    1788         // Plugin TEM vulnerabilidades - badge vermelho
    1789         $message = sprintf(
    1790             /* translators: %d: Number of vulnerabilities */
    1791             _n( '%d vulnerabilidade CVE detectada', '%d vulnerabilidades CVE detectadas', $vuln_count, 'ht-security' ),
    1792             $vuln_count
    1793         );
    1794 
    1795         $badge = sprintf(
    1796             '<span style="display: inline-block; background-color: #dc3545; color: white; padding: 3px 8px; border-radius: 3px; font-size: 11px; font-weight: bold; margin-right: 5px;">⚠ %s</span>',
    1797             esc_html( $message )
    1798         );
    1799 
    1800         $link = sprintf(
    1801             '<a href="%s" style="text-decoration: none;">%s</a>',
    1802             esc_url( admin_url( 'options-general.php?page=ht-security#cve-check' ) ),
    1803             esc_html__( 'Ver detalhes', 'ht-security' )
    1804         );
    1805 
    1806         $plugin_meta[] = $badge . ' ' . $link;
    1807     } else {
    1808         // Plugin NÃO TEM vulnerabilidades - badge verde
    1809         $last_check = get_option( 'htsec_last_cve_check', 0 );
    1810 
    1811         if ( $last_check ) {
    1812             $badge = sprintf(
    1813                 '<span style="display: inline-block; background-color: #28a745; color: white; padding: 3px 8px; border-radius: 3px; font-size: 11px; font-weight: bold;">✓ %s</span>',
    1814                 esc_html__( 'Sem vulnerabilidades conhecidas', 'ht-security' )
    1815             );
    1816 
    1817             $plugin_meta[] = $badge;
    1818         }
    1819     }
    1820 
    1821     return $plugin_meta;
    1822 }
    1823 
    1824 /**
    1825  * Adiciona CSS para a página de plugins.
    1826  */
    1827 add_action( 'admin_head-plugins.php', 'htsec_plugins_page_styles' );
    1828 function htsec_plugins_page_styles() {
    1829     ?>
    1830     <style>
    1831         .htsec-vuln-badge {
    1832             display: inline-block;
    1833             padding: 4px 10px;
    1834             border-radius: 3px;
    1835             font-size: 11px;
    1836             font-weight: bold;
    1837             margin-right: 8px;
    1838         }
    1839         .htsec-vuln-badge.danger {
    1840             background-color: #dc3545;
    1841             color: white;
    1842         }
    1843         .htsec-vuln-badge.success {
    1844             background-color: #28a745;
    1845             color: white;
    1846         }
    1847         .htsec-vuln-badge.warning {
    1848             background-color: #ffc107;
    1849             color: #333;
    1850         }
    1851     </style>
    1852     <?php
    1853 }
    1854 
    1855 /**
    1856  * Adiciona notificação no topo da página de plugins se houver vulnerabilidades.
    1857  */
    1858 add_action( 'admin_notices', 'htsec_plugins_page_vulnerability_notice' );
    1859 function htsec_plugins_page_vulnerability_notice() {
    1860     $screen = get_current_screen();
    1861 
    1862     if ( isset( $screen->id ) && 'plugins' === $screen->id ) {
    1863         $user_id = get_current_user_id();
    1864         $vulnerabilities = get_option( 'htsec_vulnerabilities', [] );
    1865         $vuln_count = count( $vulnerabilities );
    1866 
    1867         if ( $vuln_count > 0 ) {
    1868             // Verificar se o usuário fechou o alerta de erro
    1869             $dismissed_error = get_user_meta( $user_id, 'htsec_dismissed_error_notice', true );
    1870             $last_check = get_option( 'htsec_last_cve_check', 0 );
    1871 
    1872             // Se foi fechado e desde então não houve nova verificação, não mostrar
    1873             if ( $dismissed_error && $dismissed_error >= $last_check ) {
    1874                 return;
    1875             }
    1876 
    1877             // Agrupar por plugin
    1878             $plugins_affected = [];
    1879             foreach ( $vulnerabilities as $vuln ) {
    1880                 $software = $vuln['software'];
    1881                 if ( ! isset( $plugins_affected[ $software ] ) ) {
    1882                     $plugins_affected[ $software ] = 0;
    1883                 }
    1884                 $plugins_affected[ $software ]++;
    1885             }
    1886 
    1887             $affected_count = count( $plugins_affected );
    1888 
    1889             echo '<div class="notice notice-error is-dismissible htsec-cve-notice" style="border-left-color: #dc3545;" data-notice-type="error">';
    1890             echo '<p><strong>' . esc_html__( 'Alerta de Segurança do HT Security:', 'ht-security' ) . '</strong></p>';
    1891             echo '<p>' . sprintf(
    1892                 /* translators: 1: Number of vulnerabilities, 2: Number of affected plugins */
    1893                 esc_html( _n(
    1894                     'Foram detectadas %1$d vulnerabilidade CVE em %2$d plugin.',
    1895                     'Foram detectadas %1$d vulnerabilidades CVE em %2$d plugins.',
    1896                     $vuln_count,
    1897                     'ht-security'
    1898                 ) ),
    1899                 esc_html( $vuln_count ),
    1900                 esc_html( $affected_count )
    1901             ) . '</p>';
    1902 
    1903             echo '<p>';
    1904             foreach ( $plugins_affected as $software => $count ) {
    1905                 echo '• <strong>' . esc_html( $software ) . '</strong>: ' . sprintf(
    1906                     /* translators: %d: Number of vulnerabilities */
    1907                     esc_html( _n( '%d CVE', '%d CVEs', $count, 'ht-security' ) ),
    1908                     esc_html( $count )
    1909                 ) . '<br>';
    1910             }
    1911             echo '</p>';
    1912 
    1913             echo '<p>';
    1914             echo '<a href="' . esc_url( admin_url( 'options-general.php?page=ht-security#cve-check' ) ) . '" class="button button-primary">';
    1915             echo esc_html__( 'Ver Detalhes no HT Security', 'ht-security' );
    1916             echo '</a>';
    1917             echo '</p>';
    1918             echo '</div>';
    1919         } else {
    1920             $last_check = get_option( 'htsec_last_cve_check', 0 );
    1921 
    1922             if ( $last_check ) {
    1923                 // Verificar se o usuário fechou o alerta de sucesso
    1924                 $dismissed_success = get_user_meta( $user_id, 'htsec_dismissed_success_notice', true );
    1925 
    1926                 // Se foi fechado e desde então não houve nova verificação, não mostrar
    1927                 if ( $dismissed_success && $dismissed_success >= $last_check ) {
    1928                     return;
    1929                 }
    1930 
    1931                 echo '<div class="notice notice-success is-dismissible htsec-cve-notice" style="border-left-color: #28a745;" data-notice-type="success">';
    1932                 echo '<p><strong>✓ ' . esc_html__( 'HT Security:', 'ht-security' ) . '</strong> ';
    1933                 echo esc_html__( 'Nenhuma vulnerabilidade conhecida foi detectada nos seus plugins!', 'ht-security' );
    1934                 echo '</p>';
    1935 
    1936                 $time_diff = human_time_diff( $last_check, current_time( 'timestamp' ) );
    1937                 echo '<p style="font-size: 12px; color: #666; margin: 5px 0 0 0;">' . sprintf(
    1938                     /* translators: %s: Time since last check */
    1939                     esc_html__( 'Última verificação: %s atrás', 'ht-security' ),
    1940                     esc_html( $time_diff )
    1941                 ) . '</p>';
    1942                 echo '</div>';
    1943             }
    1944         }
    1945     }
    1946 }
    1947 
    1948 /**
    1949  * Adiciona JavaScript para tratar o dismiss do alerta.
    1950  */
    1951 add_action( 'admin_footer-plugins.php', 'htsec_dismiss_notice_script' );
    1952 function htsec_dismiss_notice_script() {
    1953     ?>
    1954     <script type="text/javascript">
    1955     jQuery(document).ready(function($) {
    1956         // Quando o usuário clicar no botão de fechar
    1957         $(document).on('click', '.htsec-cve-notice .notice-dismiss', function() {
    1958             var notice = $(this).parent();
    1959             var noticeType = notice.data('notice-type');
    1960 
    1961             // Enviar requisição AJAX para salvar que o alerta foi fechado
    1962             $.post(ajaxurl, {
    1963                 action: 'htsec_dismiss_notice',
    1964                 notice_type: noticeType,
    1965                 nonce: '<?php echo esc_js( wp_create_nonce( 'htsec_dismiss_notice' ) ); ?>'
    1966             });
    1967         });
    1968     });
    1969     </script>
    1970     <?php
    1971 }
    1972 
    1973 /**
    1974  * Handler AJAX para salvar dismiss do alerta.
    1975  */
    1976 add_action( 'wp_ajax_htsec_dismiss_notice', 'htsec_ajax_dismiss_notice' );
    1977 function htsec_ajax_dismiss_notice() {
    1978     check_ajax_referer( 'htsec_dismiss_notice', 'nonce' );
    1979 
    1980     $notice_type = isset( $_POST['notice_type'] ) ? sanitize_text_field( wp_unslash( $_POST['notice_type'] ) ) : '';
    1981     $user_id = get_current_user_id();
    1982 
    1983     if ( 'error' === $notice_type ) {
    1984         update_user_meta( $user_id, 'htsec_dismissed_error_notice', current_time( 'timestamp' ) );
    1985     } elseif ( 'success' === $notice_type ) {
    1986         update_user_meta( $user_id, 'htsec_dismissed_success_notice', current_time( 'timestamp' ) );
    1987     }
    1988 
    1989     wp_send_json_success();
    1990 }
    1991 
    1992 /**
    1993  * Handler AJAX para processar e enviar feedback.
    1994  */
    1995 add_action( 'wp_ajax_htsec_send_feedback', 'htsec_ajax_send_feedback' );
    1996 function htsec_ajax_send_feedback() {
    1997     check_ajax_referer( 'htsec_send_feedback', 'nonce' );
    1998 
    1999     // Verificar permissões
    2000     if ( ! current_user_can( 'manage_options' ) ) {
    2001         wp_send_json_error( [
    2002             'message' => esc_html__( 'Você não tem permissão para enviar feedback.', 'ht-security' ),
    2003         ] );
    2004     }
    2005 
    2006     // Obter e sanitizar a mensagem
    2007     $message = isset( $_POST['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['message'] ) ) : '';
    2008 
    2009     // Validar mensagem
    2010     if ( empty( trim( $message ) ) ) {
    2011         wp_send_json_error( [
    2012             'message' => esc_html__( 'Por favor, digite uma mensagem antes de enviar.', 'ht-security' ),
    2013         ] );
    2014     }
    2015 
    2016     if ( strlen( $message ) < 10 ) {
    2017         wp_send_json_error( [
    2018             'message' => esc_html__( 'A mensagem deve ter pelo menos 10 caracteres.', 'ht-security' ),
    2019         ] );
    2020     }
    2021 
    2022     // Obter informações do usuário e site
    2023     $current_user = wp_get_current_user();
    2024     $user_email = $current_user->user_email;
    2025     $user_name = $current_user->display_name;
    2026     $site_url = get_site_url();
    2027     $site_name = get_bloginfo( 'name' );
    2028     $wp_version = get_bloginfo( 'version' );
    2029     $plugin_version = '1.3.2';
    2030 
    2031     // Preparar o assunto do email
    2032     $subject = sprintf(
    2033         /* translators: %s: Site name */
    2034         esc_html__( '[HT Security Feedback] %s', 'ht-security' ),
    2035         $site_name
    2036     );
    2037 
    2038     // Preparar o corpo do email
    2039     $email_body = esc_html__( 'Novo feedback recebido:', 'ht-security' ) . "\n\n";
    2040     $email_body .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n\n";
    2041     $email_body .= esc_html__( 'MENSAGEM:', 'ht-security' ) . "\n";
    2042     $email_body .= $message . "\n\n";
    2043     $email_body .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n\n";
    2044     $email_body .= esc_html__( 'INFORMAÇÕES DO REMETENTE:', 'ht-security' ) . "\n";
    2045     $email_body .= esc_html__( 'Nome:', 'ht-security' ) . ' ' . esc_html( $user_name ) . "\n";
    2046     $email_body .= esc_html__( 'E-mail:', 'ht-security' ) . ' ' . esc_html( $user_email ) . "\n";
    2047     $email_body .= esc_html__( 'Site:', 'ht-security' ) . ' ' . esc_html( $site_name ) . "\n";
    2048     $email_body .= esc_html__( 'URL:', 'ht-security' ) . ' ' . esc_url( $site_url ) . "\n";
    2049     $email_body .= esc_html__( 'WordPress:', 'ht-security' ) . ' ' . esc_html( $wp_version ) . "\n";
    2050     $email_body .= esc_html__( 'HT Security:', 'ht-security' ) . ' ' . esc_html( $plugin_version ) . "\n";
    2051     $email_body .= esc_html__( 'Data/Hora:', 'ht-security' ) . ' ' . current_time( 'mysql' ) . "\n\n";
    2052     $email_body .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
    2053 
    2054     // Configurar headers do email
    2055     $headers = [
    2056         'Content-Type: text/plain; charset=UTF-8',
    2057         'From: ' . $site_name . ' <' . $user_email . '>',
    2058         'Reply-To: ' . $user_email,
    2059     ];
    2060 
    2061     // Enviar email
    2062     $sent = wp_mail( '[email protected]', $subject, $email_body, $headers );
    2063 
    2064     if ( $sent ) {
    2065         wp_send_json_success( [
    2066             'message' => esc_html__( 'Feedback enviado com sucesso! Obrigado pela sua contribuição.', 'ht-security' ),
    2067         ] );
    2068     } else {
    2069         wp_send_json_error( [
    2070             'message' => esc_html__( 'Erro ao enviar feedback. Por favor, tente novamente mais tarde.', 'ht-security' ),
    2071         ] );
    2072     }
    2073 }
     64htsec_load_modules();
  • ht-security/trunk/readme.txt

    r3391451 r3391994  
    55Tested up to: 6.8
    66Requires PHP: 8.2
    7 Stable Tag: 1.3.2
     7Stable Tag: 1.3.3
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1313== Description ==
    1414O HT Security é uma suíte de segurança completa para WordPress, oferecendo múltiplas camadas de proteção para seu site.
     15
     16**Importante - Serviço Externo:**
     17Este plugin consulta a API da National Vulnerability Database (NVD) para verificar vulnerabilidades CVE conhecidas. As requisições são feitas para:
     18* URL da API: https://services.nvd.nist.gov/rest/json/cves/2.0
     19* Termos de Uso: https://nvd.nist.gov/general/legal-disclaimer
     20* Política de Privacidade: https://www.nist.gov/privacy-policy
     21* Frequência: Verificação automática a cada 12 horas ou manual sob demanda
     22* Dados enviados: Nome e versão do WordPress/plugins instalados (não envia dados pessoais)
     23
     24A consulta à API da NVD é essencial para a funcionalidade de detecção de vulnerabilidades CVE do plugin.
    1525
    1626== Features ==
     
    8292
    8393== Changelog ==
     94= 1.3.3 =
     95* **Melhoria CRÍTICA: Detecção Aprimorada de Variantes de Licença**
     96  - Corrigido: Plugin FREE não acusa mais vulnerabilidades da versão PRO
     97  - Detecção de variantes com parênteses: "Plugin (PRO)", "Plugin (Premium)"
     98  - Detecção de variantes com colchetes: "Plugin [PRO]", "Plugin [Lite]"
     99  - Bloqueio correto quando CVE menciona variante mas plugin não tem
     100  - Elimina falsos positivos em plugins com nomes similares mas licenças diferentes
     101
     102* **Refatoração Completa da Estrutura do Código**
     103  - Código modularizado em 9 arquivos por funcionalidade
     104  - Organização em diretório /includes/ seguindo padrões WordPress
     105  - Melhor separação de responsabilidades (cada módulo tem função única)
     106  - Facilita manutenção, debug e adição de novas funcionalidades
     107  - Performance otimizada com carregamento modular
     108  - Documentação PHPDoc completa em todos os módulos
     109
     110* **Módulos Criados:**
     111  - security-headers.php: Cabeçalhos HTTP de segurança
     112  - settings.php: Configurações e registro de opções
     113  - login-alerts.php: Sistema de alertas de login
     114  - user-enumeration.php: Proteção contra enumeração
     115  - maintenance-mode.php: Modo de manutenção com IP whitelist
     116  - file-permissions.php: Auditoria de permissões de arquivos
     117  - cve-check.php: Sistema completo de verificação CVE
     118  - admin-page.php: Interface administrativa e feedback
     119  - plugin-indicators.php: Badges e notificações de vulnerabilidades
     120
     121* **Melhorias de Código**
     122  - Seguindo WordPress Coding Standards
     123  - Sanitização e escapamento rigorosos
     124  - Hooks e filtros organizados por módulo
     125  - Código mais limpo e manutenível
     126
    84127= 1.3.2 =
    85128* **Correção de Acertividade na Verificação de CVEs**
     
    198241== Upgrade Notice ==
    199242
     243= 1.3.3 =
     244Correção crítica na detecção de variantes de licença (FREE vs PRO) - elimina falsos positivos. Código completamente refatorado em módulos para melhor manutenibilidade.
     245
    200246= 1.3.2 =
    201247Correção de acertividade na verificação de CVEs. Badges de segurança agora vêm desabilitados por padrão - você escolhe se quer ativar. Melhorias de performance e ajustes no sistema anti-falso positivo.
    202248
    203249= 1.3.1 =
    204 CORREÇÃO CRÍTICA! Sistema anti-falso positivo completamente reformulado com 8 filtros de validação. Novo filtro de variações de licença (Free vs Pro/Premium). Eliminados 99.9% dos falsos positivos. Nova funcionalidade de feedback integrada. Atualização ALTAMENTE RECOMENDADA!
     250Sistema anti-falso positivo completamente reformulado com 8 filtros de validação. Novo filtro de variações de licença (Free vs Pro/Premium). Eliminados 99.9% dos falsos positivos. Nova funcionalidade de feedback integrada.
    205251
    206252= 1.3.0 =
Note: See TracChangeset for help on using the changeset viewer.