Changeset 3391428
- Timestamp:
- 11/06/2025 08:39:22 PM (5 weeks ago)
- Location:
- ht-security
- Files:
-
- 4 added
- 2 edited
-
tags/1.3.0 (added)
-
tags/1.3.0/ht-security.php (added)
-
tags/1.3.0/readme.txt (added)
-
tags/1.3.0/security.txt (added)
-
trunk/ht-security.php (modified) (7 diffs)
-
trunk/readme.txt (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ht-security/trunk/ht-security.php
r3355589 r3391428 3 3 Plugin Name: HT Security 4 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. 2.05 Version: 1.3.0 6 6 Requires at least: 6.5 7 7 Requires PHP: 8.2 … … 72 72 'default' => '', 73 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' => 1, 88 ] ); 74 89 75 90 add_settings_section( … … 175 190 'htsec_main_section' 176 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', 1 ); 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 ); 177 240 } 178 241 … … 191 254 192 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 /** 193 500 * Renderiza a página de configurações e faz a verificação de integridade do Core via API. 194 501 */ … … 252 559 submit_button(); 253 560 echo '</form>'; 254 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 255 589 // Auditoria de Permissões de Arquivos 256 590 echo '<h2>' . esc_html__( 'Auditoria de Permissões de Arquivos', 'ht-security' ) . '</h2>'; 257 591 htsec_display_file_permissions_audit(); 258 592 259 593 echo '</div>'; 260 594 } … … 491 825 return; 492 826 } 493 827 494 828 if ( current_user_can( 'manage_options' ) ) { 495 829 return; 496 830 } 497 831 498 832 $allowed_ips = get_option( 'htsec_maintenance_ips', '' ); 499 833 $allowed_ips = array_filter( array_map( 'trim', explode( "\n", $allowed_ips ) ) ); 500 834 $current_ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '' ) ); 501 835 502 836 if ( ! empty( $allowed_ips ) && in_array( $current_ip, $allowed_ips, true ) ) { 503 837 return; 504 838 } 505 839 506 840 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; 507 841 508 842 if ( ! is_admin() && ! strpos( $request_uri, '/wp-login.php' ) ) { 509 843 wp_die( … … 514 848 } 515 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 // Lista de termos genéricos que causam falsos positivos 1142 $generic_terms = [ 1143 'apache', 'tomcat', 'connector', 'owasp', 'antisamy', '.net', 1144 'java', 'spring', 'framework', 'library', 'module', 'component', 1145 ]; 1146 1147 // Para WordPress, usar busca específica 1148 if ( 'WordPress' === $software_name ) { 1149 $search_term = 'WordPress Core ' . $version; 1150 } else { 1151 // Para plugins, usar nome específico 1152 $search_term = sanitize_text_field( $software_name ); 1153 } 1154 1155 $url = add_query_arg( [ 1156 'keywordSearch' => rawurlencode( $search_term ), 1157 'keywordExactMatch' => '', // Busca exata da frase 1158 'resultsPerPage' => 20, 1159 ], 'https://services.nvd.nist.gov/rest/json/cves/2.0' ); 1160 1161 $headers = [ 'timeout' => 30 ]; 1162 1163 if ( ! empty( $api_key ) ) { 1164 $headers['headers'] = [ 1165 'apiKey' => sanitize_text_field( $api_key ), 1166 ]; 1167 } 1168 1169 $response = wp_remote_get( $url, $headers ); 1170 1171 if ( is_wp_error( $response ) ) { 1172 return $vulnerabilities; 1173 } 1174 1175 $response_code = wp_remote_retrieve_response_code( $response ); 1176 if ( 200 !== $response_code ) { 1177 return $vulnerabilities; 1178 } 1179 1180 $body = wp_remote_retrieve_body( $response ); 1181 $data = json_decode( $body, true ); 1182 1183 if ( empty( $data['vulnerabilities'] ) ) { 1184 return $vulnerabilities; 1185 } 1186 1187 foreach ( $data['vulnerabilities'] as $vuln ) { 1188 if ( empty( $vuln['cve'] ) ) { 1189 continue; 1190 } 1191 1192 $cve = $vuln['cve']; 1193 $cve_id = isset( $cve['id'] ) ? $cve['id'] : ''; 1194 1195 if ( empty( $cve_id ) ) { 1196 continue; 1197 } 1198 1199 // Extrair descrição 1200 $description = ''; 1201 if ( isset( $cve['descriptions'] ) && is_array( $cve['descriptions'] ) ) { 1202 foreach ( $cve['descriptions'] as $desc ) { 1203 if ( isset( $desc['lang'] ) && 'en' === $desc['lang'] ) { 1204 $description = $desc['value']; 1205 break; 1206 } 1207 } 1208 } 1209 1210 // ANTI-FALSO POSITIVO: Validar se a CVE realmente se aplica ao software 1211 if ( ! htsec_validate_cve_match( $software_name, $description, $cve ) ) { 1212 continue; 1213 } 1214 1215 // ANTI-FALSO POSITIVO: Verificar se não contém termos genéricos que não correspondem 1216 $description_lower = strtolower( $description ); 1217 $software_lower = strtolower( $software_name ); 1218 1219 $has_generic_mismatch = false; 1220 foreach ( $generic_terms as $term ) { 1221 // Se a descrição menciona um termo genérico que não está no nome do software 1222 if ( strpos( $description_lower, $term ) !== false && strpos( $software_lower, $term ) === false ) { 1223 $has_generic_mismatch = true; 1224 break; 1225 } 1226 } 1227 1228 if ( $has_generic_mismatch ) { 1229 continue; 1230 } 1231 1232 // VALIDAÇÃO DE VERSÃO: Verificar se a versão instalada é vulnerável 1233 if ( ! htsec_version_is_vulnerable( $version, $cve ) ) { 1234 continue; 1235 } 1236 1237 // Extrair severidade e score 1238 $severity = 'UNKNOWN'; 1239 $cvss_score = null; 1240 1241 if ( isset( $cve['metrics'] ) ) { 1242 $metrics = $cve['metrics']; 1243 1244 // Tentar CVSS v3.1 primeiro 1245 if ( isset( $metrics['cvssMetricV31'][0] ) ) { 1246 $cvss = $metrics['cvssMetricV31'][0]; 1247 $severity = isset( $cvss['cvssData']['baseSeverity'] ) ? $cvss['cvssData']['baseSeverity'] : 'UNKNOWN'; 1248 $cvss_score = isset( $cvss['cvssData']['baseScore'] ) ? $cvss['cvssData']['baseScore'] : null; 1249 } elseif ( isset( $metrics['cvssMetricV30'][0] ) ) { 1250 $cvss = $metrics['cvssMetricV30'][0]; 1251 $severity = isset( $cvss['cvssData']['baseSeverity'] ) ? $cvss['cvssData']['baseSeverity'] : 'UNKNOWN'; 1252 $cvss_score = isset( $cvss['cvssData']['baseScore'] ) ? $cvss['cvssData']['baseScore'] : null; 1253 } elseif ( isset( $metrics['cvssMetricV2'][0] ) ) { 1254 $cvss = $metrics['cvssMetricV2'][0]; 1255 $severity = isset( $cvss['baseSeverity'] ) ? $cvss['baseSeverity'] : 'UNKNOWN'; 1256 $cvss_score = isset( $cvss['cvssData']['baseScore'] ) ? $cvss['cvssData']['baseScore'] : null; 1257 } 1258 } 1259 1260 $vulnerabilities[] = [ 1261 'software' => $software_name, 1262 'version' => $version, 1263 'cve_id' => $cve_id, 1264 'cve_url' => 'https://nvd.nist.gov/vuln/detail/' . $cve_id, 1265 'description' => wp_trim_words( $description, 50 ), 1266 'full_description' => $description, 1267 'severity' => $severity, 1268 'cvss_score' => $cvss_score, 1269 ]; 1270 } 1271 1272 return $vulnerabilities; 1273 } 1274 1275 /** 1276 * Verifica se a versão instalada está vulnerável baseada nos dados da CVE. 1277 */ 1278 function htsec_version_is_vulnerable( $installed_version, $cve_data ) { 1279 // Se não tiver versão instalada, não pode validar 1280 if ( empty( $installed_version ) ) { 1281 return false; 1282 } 1283 1284 // Normalizar versão instalada (remover v, V, etc.) 1285 $installed_version = strtolower( trim( $installed_version ) ); 1286 $installed_version = preg_replace( '/^v/', '', $installed_version ); 1287 1288 // Extrair versões afetadas das configurações da CVE 1289 $version_ranges = htsec_extract_version_ranges( $cve_data ); 1290 1291 // Se não conseguiu extrair ranges, tentar da descrição 1292 if ( empty( $version_ranges ) ) { 1293 $description = ''; 1294 if ( isset( $cve_data['descriptions'] ) && is_array( $cve_data['descriptions'] ) ) { 1295 foreach ( $cve_data['descriptions'] as $desc ) { 1296 if ( isset( $desc['lang'] ) && 'en' === $desc['lang'] ) { 1297 $description = $desc['value']; 1298 break; 1299 } 1300 } 1301 } 1302 $version_ranges = htsec_extract_version_from_description( $description ); 1303 } 1304 1305 // Se ainda não tem ranges, não pode validar (assume que pode ser vulnerável para ser conservador) 1306 if ( empty( $version_ranges ) ) { 1307 return false; // Mudado para false - se não sabemos, não reportamos 1308 } 1309 1310 // Verificar se a versão instalada está em algum dos ranges vulneráveis 1311 foreach ( $version_ranges as $range ) { 1312 if ( htsec_version_in_range( $installed_version, $range ) ) { 1313 return true; 1314 } 1315 } 1316 1317 return false; 1318 } 1319 1320 /** 1321 * Extrai ranges de versões afetadas dos dados da CVE. 1322 */ 1323 function htsec_extract_version_ranges( $cve_data ) { 1324 $ranges = []; 1325 1326 if ( ! isset( $cve_data['configurations'] ) || ! is_array( $cve_data['configurations'] ) ) { 1327 return $ranges; 1328 } 1329 1330 foreach ( $cve_data['configurations'] as $config ) { 1331 if ( ! isset( $config['nodes'] ) || ! is_array( $config['nodes'] ) ) { 1332 continue; 1333 } 1334 1335 foreach ( $config['nodes'] as $node ) { 1336 if ( ! isset( $node['cpeMatch'] ) || ! is_array( $node['cpeMatch'] ) ) { 1337 continue; 1338 } 1339 1340 foreach ( $node['cpeMatch'] as $match ) { 1341 if ( ! isset( $match['vulnerable'] ) || ! $match['vulnerable'] ) { 1342 continue; 1343 } 1344 1345 $range = [ 1346 'start_version' => null, 1347 'end_version' => null, 1348 'start_including' => true, 1349 'end_including' => true, 1350 ]; 1351 1352 if ( isset( $match['versionStartIncluding'] ) ) { 1353 $range['start_version'] = $match['versionStartIncluding']; 1354 $range['start_including'] = true; 1355 } elseif ( isset( $match['versionStartExcluding'] ) ) { 1356 $range['start_version'] = $match['versionStartExcluding']; 1357 $range['start_including'] = false; 1358 } 1359 1360 if ( isset( $match['versionEndIncluding'] ) ) { 1361 $range['end_version'] = $match['versionEndIncluding']; 1362 $range['end_including'] = true; 1363 } elseif ( isset( $match['versionEndExcluding'] ) ) { 1364 $range['end_version'] = $match['versionEndExcluding']; 1365 $range['end_including'] = false; 1366 } 1367 1368 // Se tem pelo menos uma versão de limite, adiciona o range 1369 if ( $range['start_version'] !== null || $range['end_version'] !== null ) { 1370 $ranges[] = $range; 1371 } 1372 } 1373 } 1374 } 1375 1376 return $ranges; 1377 } 1378 1379 /** 1380 * Extrai informação de versão da descrição da CVE. 1381 */ 1382 function htsec_extract_version_from_description( $description ) { 1383 $ranges = []; 1384 1385 if ( empty( $description ) ) { 1386 return $ranges; 1387 } 1388 1389 $description_lower = strtolower( $description ); 1390 1391 // Padrões comuns: 1392 // "before X.X.X", "prior to X.X.X", "up to X.X.X", "through X.X.X" 1393 // "versions up to, and including, X.X.X" 1394 // "in versions up to X.X.X" 1395 1396 $patterns = [ 1397 '/(?:before|prior to|up to|through)\s+(?:version\s+)?v?(\d+\.\d+(?:\.\d+)?)/i', 1398 '/versions?\s+up\s+to,?\s+(?:and\s+)?including,?\s+v?(\d+\.\d+(?:\.\d+)?)/i', 1399 '/in\s+versions?\s+up\s+to\s+v?(\d+\.\d+(?:\.\d+)?)/i', 1400 '/(?:version|versions?)\s+v?(\d+\.\d+(?:\.\d+)?)\s+and\s+(?:below|earlier)/i', 1401 ]; 1402 1403 foreach ( $patterns as $pattern ) { 1404 if ( preg_match( $pattern, $description, $matches ) ) { 1405 $version = $matches[1]; 1406 1407 // Determinar se é inclusive ou exclusive baseado nas palavras 1408 $is_including = ( 1409 strpos( $description_lower, 'including' ) !== false || 1410 strpos( $description_lower, 'through' ) !== false 1411 ); 1412 1413 $ranges[] = [ 1414 'start_version' => null, 1415 'end_version' => $version, 1416 'start_including' => true, 1417 'end_including' => $is_including, 1418 ]; 1419 1420 break; // Pega apenas o primeiro match 1421 } 1422 } 1423 1424 return $ranges; 1425 } 1426 1427 /** 1428 * Verifica se uma versão está dentro de um range específico. 1429 */ 1430 function htsec_version_in_range( $version, $range ) { 1431 // Verificar limite inferior 1432 if ( $range['start_version'] !== null ) { 1433 $comparison = version_compare( $version, $range['start_version'] ); 1434 1435 if ( $range['start_including'] ) { 1436 // Versão deve ser >= start 1437 if ( $comparison < 0 ) { 1438 return false; 1439 } 1440 } else { 1441 // Versão deve ser > start 1442 if ( $comparison <= 0 ) { 1443 return false; 1444 } 1445 } 1446 } 1447 1448 // Verificar limite superior 1449 if ( $range['end_version'] !== null ) { 1450 $comparison = version_compare( $version, $range['end_version'] ); 1451 1452 if ( $range['end_including'] ) { 1453 // Versão deve ser <= end 1454 if ( $comparison > 0 ) { 1455 return false; 1456 } 1457 } else { 1458 // Versão deve ser < end 1459 if ( $comparison >= 0 ) { 1460 return false; 1461 } 1462 } 1463 } 1464 1465 return true; 1466 } 1467 1468 /** 1469 * Valida se uma CVE realmente corresponde ao software especificado. 1470 */ 1471 function htsec_validate_cve_match( $software_name, $description, $cve_data ) { 1472 $software_lower = strtolower( $software_name ); 1473 $description_lower = strtolower( $description ); 1474 1475 // Para WordPress Core 1476 if ( 'WordPress' === $software_name ) { 1477 // Deve mencionar explicitamente "wordpress" 1478 if ( strpos( $description_lower, 'wordpress' ) === false ) { 1479 return false; 1480 } 1481 1482 // Não deve ser sobre plugin ou tema 1483 if ( strpos( $description_lower, 'plugin' ) !== false || strpos( $description_lower, 'theme' ) !== false ) { 1484 return false; 1485 } 1486 1487 return true; 1488 } 1489 1490 // NOVA VALIDAÇÃO: Detectar plugins addon/relacionados 1491 // Palavras que indicam que é um plugin diferente (addon, extension, etc.) 1492 $addon_keywords = [ 1493 'addon', 'add-on', 'add on', 1494 'extension', 'extend', 1495 'pro version', 'premium', 1496 'ultimate', 'advanced', 'extra', 1497 'plus', 'premium', 1498 ]; 1499 1500 foreach ( $addon_keywords as $keyword ) { 1501 if ( strpos( $description_lower, $keyword ) !== false ) { 1502 // Se menciona addon, verificar se o nome do plugin instalado também menciona 1503 $has_keyword_in_name = false; 1504 foreach ( explode( ' ', $keyword ) as $kword ) { 1505 if ( strpos( $software_lower, $kword ) !== false ) { 1506 $has_keyword_in_name = true; 1507 break; 1508 } 1509 } 1510 1511 // Se a CVE menciona addon mas o plugin instalado não menciona, é outro plugin 1512 if ( ! $has_keyword_in_name ) { 1513 // Verificação adicional: extrair nome completo do plugin da descrição 1514 $cve_plugin_name = htsec_extract_plugin_name_from_description( $description ); 1515 1516 if ( ! empty( $cve_plugin_name ) && strtolower( $cve_plugin_name ) !== $software_lower ) { 1517 // Os nomes são diferentes - é outro plugin 1518 return false; 1519 } 1520 } 1521 } 1522 } 1523 1524 // VALIDAÇÃO EXATA: Tentar extrair o nome exato do plugin da descrição 1525 $cve_plugin_name = htsec_extract_plugin_name_from_description( $description ); 1526 1527 if ( ! empty( $cve_plugin_name ) ) { 1528 $cve_plugin_lower = strtolower( $cve_plugin_name ); 1529 1530 // Se o nome extraído é MUITO diferente do nome do plugin, filtrar 1531 $similarity = 0; 1532 similar_text( $software_lower, $cve_plugin_lower, $similarity ); 1533 1534 // Se similaridade < 60%, provavelmente é plugin diferente 1535 if ( $similarity < 60 ) { 1536 // Mas apenas se o nome extraído é substancialmente diferente 1537 // Ex: "Elementor" vs "Plus Addons for Elementor" = muito diferente 1538 if ( strlen( $cve_plugin_lower ) > strlen( $software_lower ) * 1.5 ) { 1539 return false; 1540 } 1541 } 1542 } 1543 1544 // Para plugins WordPress 1545 // Verificar se a descrição menciona o nome do plugin 1546 $words = explode( ' ', $software_lower ); 1547 $match_count = 0; 1548 1549 foreach ( $words as $word ) { 1550 if ( strlen( $word ) < 3 ) { 1551 continue; // Ignorar palavras muito curtas 1552 } 1553 1554 if ( strpos( $description_lower, $word ) !== false ) { 1555 $match_count++; 1556 } 1557 } 1558 1559 // Se nenhuma palavra do nome do software aparece na descrição, provavelmente é falso positivo 1560 if ( $match_count === 0 ) { 1561 return false; 1562 } 1563 1564 // Verificar se menciona WordPress plugin 1565 $is_wp_plugin_cve = ( strpos( $description_lower, 'wordpress' ) !== false && strpos( $description_lower, 'plugin' ) !== false ); 1566 1567 // Se menciona WordPress plugin, verificar se não é addon de outro plugin 1568 if ( $is_wp_plugin_cve ) { 1569 // Padrão: "The [Nome do Plugin] WordPress plugin" 1570 if ( preg_match( '/the\s+(.+?)\s+wordpress\s+plugin/i', $description, $matches ) ) { 1571 $extracted_name = trim( $matches[1] ); 1572 $extracted_lower = strtolower( $extracted_name ); 1573 1574 // Se o nome extraído é muito diferente, é outro plugin 1575 if ( strpos( $extracted_lower, $software_lower ) === false && 1576 strpos( $software_lower, $extracted_lower ) === false ) { 1577 return false; 1578 } 1579 } 1580 } 1581 1582 // Se tem pelo menos 50% das palavras correspondentes, considera válido 1583 $total_words = count( array_filter( $words, function( $w ) { 1584 return strlen( $w ) >= 3; 1585 } ) ); 1586 1587 if ( $total_words > 0 && ( $match_count / $total_words ) >= 0.5 ) { 1588 return true; 1589 } 1590 1591 // Caso contrário, é provável falso positivo 1592 return false; 1593 } 1594 1595 /** 1596 * Extrai o nome do plugin da descrição da CVE. 1597 */ 1598 function htsec_extract_plugin_name_from_description( $description ) { 1599 // Padrões comuns de nomeação em descrições de CVE: 1600 // "The [Plugin Name] WordPress plugin" 1601 // "The [Plugin Name] plugin for WordPress" 1602 // "[Plugin Name] plugin" 1603 1604 $patterns = [ 1605 '/the\s+(.+?)\s+wordpress\s+plugin/i', 1606 '/the\s+(.+?)\s+plugin\s+for\s+wordpress/i', 1607 '/^(.+?)\s+plugin/i', 1608 ]; 1609 1610 foreach ( $patterns as $pattern ) { 1611 if ( preg_match( $pattern, trim( $description ), $matches ) ) { 1612 return trim( $matches[1] ); 1613 } 1614 } 1615 1616 return ''; 1617 } 1618 1619 /** 1620 * Envia alerta por e-mail sobre vulnerabilidades encontradas. 1621 */ 1622 function htsec_send_cve_alert( $vulnerabilities ) { 1623 $email = sanitize_email( get_option( 'htsec_alert_email', get_option( 'admin_email' ) ) ); 1624 1625 if ( empty( $email ) ) { 1626 return; 1627 } 1628 1629 $subject = sprintf( 1630 /* translators: 1: Site name, 2: Number of vulnerabilities */ 1631 esc_html__( '[%1$s] Alerta: %2$d vulnerabilidades detectadas', 'ht-security' ), 1632 get_bloginfo( 'name' ), 1633 count( $vulnerabilities ) 1634 ); 1635 1636 $message = esc_html__( 'Estimado Administrador,', 'ht-security' ) . "\n\n"; 1637 $message .= sprintf( 1638 /* translators: %d: Number of vulnerabilities */ 1639 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' ) ), 1640 count( $vulnerabilities ) 1641 ) . "\n\n"; 1642 1643 foreach ( $vulnerabilities as $vuln ) { 1644 $message .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n"; 1645 $message .= esc_html__( 'Software:', 'ht-security' ) . ' ' . $vuln['software'] . ' ' . $vuln['version'] . "\n"; 1646 $message .= esc_html__( 'CVE:', 'ht-security' ) . ' ' . $vuln['cve_id'] . "\n"; 1647 $message .= esc_html__( 'Severidade:', 'ht-security' ) . ' ' . $vuln['severity'] . "\n"; 1648 if ( isset( $vuln['cvss_score'] ) ) { 1649 $message .= esc_html__( 'CVSS Score:', 'ht-security' ) . ' ' . $vuln['cvss_score'] . "\n"; 1650 } 1651 $message .= esc_html__( 'Link:', 'ht-security' ) . ' ' . $vuln['cve_url'] . "\n"; 1652 if ( ! empty( $vuln['description'] ) ) { 1653 $message .= esc_html__( 'Descrição:', 'ht-security' ) . ' ' . $vuln['description'] . "\n"; 1654 } 1655 $message .= "\n"; 1656 } 1657 1658 $message .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n\n"; 1659 $message .= esc_html__( 'Recomendamos que você atualize os plugins e o WordPress o mais rápido possível.', 'ht-security' ) . "\n\n"; 1660 $message .= esc_html__( 'Acesse o painel do HT Security para mais detalhes:', 'ht-security' ) . "\n"; 1661 $message .= admin_url( 'options-general.php?page=ht-security' ); 1662 1663 wp_mail( $email, $subject, $message ); 1664 } 1665 1666 /** 1667 * Configura o cron job para verificação automática de vulnerabilidades. 1668 */ 1669 add_action( 'wp', 'htsec_schedule_cve_check' ); 1670 function htsec_schedule_cve_check() { 1671 if ( ! wp_next_scheduled( 'htsec_cve_check_event' ) ) { 1672 wp_schedule_event( time(), 'twicedaily', 'htsec_cve_check_event' ); 1673 } 1674 } 1675 1676 add_action( 'htsec_cve_check_event', 'htsec_run_scheduled_cve_check' ); 1677 function htsec_run_scheduled_cve_check() { 1678 htsec_check_vulnerabilities(); 1679 } 1680 1681 /** 1682 * Remove o cron job ao desativar o plugin. 1683 */ 1684 register_deactivation_hook( __FILE__, 'htsec_deactivation' ); 1685 function htsec_deactivation() { 1686 $timestamp = wp_next_scheduled( 'htsec_cve_check_event' ); 1687 if ( $timestamp ) { 1688 wp_unschedule_event( $timestamp, 'htsec_cve_check_event' ); 1689 } 1690 } 1691 1692 /** 1693 * Adiciona indicador de vulnerabilidades na página de plugins. 1694 */ 1695 add_filter( 'plugin_row_meta', 'htsec_add_plugin_vulnerability_indicator', 10, 2 ); 1696 function htsec_add_plugin_vulnerability_indicator( $plugin_meta, $plugin_file ) { 1697 // Verificar se a opção está ativada 1698 if ( ! get_option( 'htsec_show_plugin_badges', 1 ) ) { 1699 return $plugin_meta; 1700 } 1701 1702 // Obter dados do plugin 1703 if ( ! function_exists( 'get_plugin_data' ) ) { 1704 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 1705 } 1706 1707 $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file ); 1708 $plugin_name = $plugin_data['Name']; 1709 1710 // Verificar vulnerabilidades armazenadas 1711 $all_vulnerabilities = get_option( 'htsec_vulnerabilities', [] ); 1712 $plugin_vulnerabilities = array_filter( $all_vulnerabilities, function( $vuln ) use ( $plugin_name ) { 1713 return $vuln['software'] === $plugin_name; 1714 } ); 1715 1716 $vuln_count = count( $plugin_vulnerabilities ); 1717 1718 if ( $vuln_count > 0 ) { 1719 // Plugin TEM vulnerabilidades - badge vermelho 1720 $message = sprintf( 1721 /* translators: %d: Number of vulnerabilities */ 1722 _n( '%d vulnerabilidade CVE detectada', '%d vulnerabilidades CVE detectadas', $vuln_count, 'ht-security' ), 1723 $vuln_count 1724 ); 1725 1726 $badge = sprintf( 1727 '<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>', 1728 esc_html( $message ) 1729 ); 1730 1731 $link = sprintf( 1732 '<a href="%s" style="text-decoration: none;">%s</a>', 1733 esc_url( admin_url( 'options-general.php?page=ht-security#cve-check' ) ), 1734 esc_html__( 'Ver detalhes', 'ht-security' ) 1735 ); 1736 1737 $plugin_meta[] = $badge . ' ' . $link; 1738 } else { 1739 // Plugin NÃO TEM vulnerabilidades - badge verde 1740 $last_check = get_option( 'htsec_last_cve_check', 0 ); 1741 1742 if ( $last_check ) { 1743 $badge = sprintf( 1744 '<span style="display: inline-block; background-color: #28a745; color: white; padding: 3px 8px; border-radius: 3px; font-size: 11px; font-weight: bold;">✓ %s</span>', 1745 esc_html__( 'Sem vulnerabilidades conhecidas', 'ht-security' ) 1746 ); 1747 1748 $plugin_meta[] = $badge; 1749 } 1750 } 1751 1752 return $plugin_meta; 1753 } 1754 1755 /** 1756 * Adiciona CSS para a página de plugins. 1757 */ 1758 add_action( 'admin_head-plugins.php', 'htsec_plugins_page_styles' ); 1759 function htsec_plugins_page_styles() { 1760 ?> 1761 <style> 1762 .htsec-vuln-badge { 1763 display: inline-block; 1764 padding: 4px 10px; 1765 border-radius: 3px; 1766 font-size: 11px; 1767 font-weight: bold; 1768 margin-right: 8px; 1769 } 1770 .htsec-vuln-badge.danger { 1771 background-color: #dc3545; 1772 color: white; 1773 } 1774 .htsec-vuln-badge.success { 1775 background-color: #28a745; 1776 color: white; 1777 } 1778 .htsec-vuln-badge.warning { 1779 background-color: #ffc107; 1780 color: #333; 1781 } 1782 </style> 1783 <?php 1784 } 1785 1786 /** 1787 * Adiciona notificação no topo da página de plugins se houver vulnerabilidades. 1788 */ 1789 add_action( 'admin_notices', 'htsec_plugins_page_vulnerability_notice' ); 1790 function htsec_plugins_page_vulnerability_notice() { 1791 $screen = get_current_screen(); 1792 1793 if ( isset( $screen->id ) && 'plugins' === $screen->id ) { 1794 $user_id = get_current_user_id(); 1795 $vulnerabilities = get_option( 'htsec_vulnerabilities', [] ); 1796 $vuln_count = count( $vulnerabilities ); 1797 1798 if ( $vuln_count > 0 ) { 1799 // Verificar se o usuário fechou o alerta de erro 1800 $dismissed_error = get_user_meta( $user_id, 'htsec_dismissed_error_notice', true ); 1801 $last_check = get_option( 'htsec_last_cve_check', 0 ); 1802 1803 // Se foi fechado e desde então não houve nova verificação, não mostrar 1804 if ( $dismissed_error && $dismissed_error >= $last_check ) { 1805 return; 1806 } 1807 1808 // Agrupar por plugin 1809 $plugins_affected = []; 1810 foreach ( $vulnerabilities as $vuln ) { 1811 $software = $vuln['software']; 1812 if ( ! isset( $plugins_affected[ $software ] ) ) { 1813 $plugins_affected[ $software ] = 0; 1814 } 1815 $plugins_affected[ $software ]++; 1816 } 1817 1818 $affected_count = count( $plugins_affected ); 1819 1820 echo '<div class="notice notice-error is-dismissible htsec-cve-notice" style="border-left-color: #dc3545;" data-notice-type="error">'; 1821 echo '<p><strong>' . esc_html__( 'Alerta de Segurança do HT Security:', 'ht-security' ) . '</strong></p>'; 1822 echo '<p>' . sprintf( 1823 /* translators: 1: Number of vulnerabilities, 2: Number of affected plugins */ 1824 esc_html( _n( 1825 'Foram detectadas %1$d vulnerabilidade CVE em %2$d plugin.', 1826 'Foram detectadas %1$d vulnerabilidades CVE em %2$d plugins.', 1827 $vuln_count, 1828 'ht-security' 1829 ) ), 1830 esc_html( $vuln_count ), 1831 esc_html( $affected_count ) 1832 ) . '</p>'; 1833 1834 echo '<p>'; 1835 foreach ( $plugins_affected as $software => $count ) { 1836 echo '• <strong>' . esc_html( $software ) . '</strong>: ' . sprintf( 1837 /* translators: %d: Number of vulnerabilities */ 1838 esc_html( _n( '%d CVE', '%d CVEs', $count, 'ht-security' ) ), 1839 esc_html( $count ) 1840 ) . '<br>'; 1841 } 1842 echo '</p>'; 1843 1844 echo '<p>'; 1845 echo '<a href="' . esc_url( admin_url( 'options-general.php?page=ht-security#cve-check' ) ) . '" class="button button-primary">'; 1846 echo esc_html__( 'Ver Detalhes no HT Security', 'ht-security' ); 1847 echo '</a>'; 1848 echo '</p>'; 1849 echo '</div>'; 1850 } else { 1851 $last_check = get_option( 'htsec_last_cve_check', 0 ); 1852 1853 if ( $last_check ) { 1854 // Verificar se o usuário fechou o alerta de sucesso 1855 $dismissed_success = get_user_meta( $user_id, 'htsec_dismissed_success_notice', true ); 1856 1857 // Se foi fechado e desde então não houve nova verificação, não mostrar 1858 if ( $dismissed_success && $dismissed_success >= $last_check ) { 1859 return; 1860 } 1861 1862 echo '<div class="notice notice-success is-dismissible htsec-cve-notice" style="border-left-color: #28a745;" data-notice-type="success">'; 1863 echo '<p><strong>✓ ' . esc_html__( 'HT Security:', 'ht-security' ) . '</strong> '; 1864 echo esc_html__( 'Nenhuma vulnerabilidade conhecida foi detectada nos seus plugins!', 'ht-security' ); 1865 echo '</p>'; 1866 1867 $time_diff = human_time_diff( $last_check, current_time( 'timestamp' ) ); 1868 echo '<p style="font-size: 12px; color: #666; margin: 5px 0 0 0;">' . sprintf( 1869 /* translators: %s: Time since last check */ 1870 esc_html__( 'Última verificação: %s atrás', 'ht-security' ), 1871 esc_html( $time_diff ) 1872 ) . '</p>'; 1873 echo '</div>'; 1874 } 1875 } 1876 } 1877 } 1878 1879 /** 1880 * Adiciona JavaScript para tratar o dismiss do alerta. 1881 */ 1882 add_action( 'admin_footer-plugins.php', 'htsec_dismiss_notice_script' ); 1883 function htsec_dismiss_notice_script() { 1884 ?> 1885 <script type="text/javascript"> 1886 jQuery(document).ready(function($) { 1887 // Quando o usuário clicar no botão de fechar 1888 $(document).on('click', '.htsec-cve-notice .notice-dismiss', function() { 1889 var notice = $(this).parent(); 1890 var noticeType = notice.data('notice-type'); 1891 1892 // Enviar requisição AJAX para salvar que o alerta foi fechado 1893 $.post(ajaxurl, { 1894 action: 'htsec_dismiss_notice', 1895 notice_type: noticeType, 1896 nonce: '<?php echo esc_js( wp_create_nonce( 'htsec_dismiss_notice' ) ); ?>' 1897 }); 1898 }); 1899 }); 1900 </script> 1901 <?php 1902 } 1903 1904 /** 1905 * Handler AJAX para salvar dismiss do alerta. 1906 */ 1907 add_action( 'wp_ajax_htsec_dismiss_notice', 'htsec_ajax_dismiss_notice' ); 1908 function htsec_ajax_dismiss_notice() { 1909 check_ajax_referer( 'htsec_dismiss_notice', 'nonce' ); 1910 1911 $notice_type = isset( $_POST['notice_type'] ) ? sanitize_text_field( wp_unslash( $_POST['notice_type'] ) ) : ''; 1912 $user_id = get_current_user_id(); 1913 1914 if ( 'error' === $notice_type ) { 1915 update_user_meta( $user_id, 'htsec_dismissed_error_notice', current_time( 'timestamp' ) ); 1916 } elseif ( 'success' === $notice_type ) { 1917 update_user_meta( $user_id, 'htsec_dismissed_success_notice', current_time( 'timestamp' ) ); 1918 } 1919 1920 wp_send_json_success(); 1921 } 1922 1923 /** 1924 * Handler AJAX para processar e enviar feedback. 1925 */ 1926 add_action( 'wp_ajax_htsec_send_feedback', 'htsec_ajax_send_feedback' ); 1927 function htsec_ajax_send_feedback() { 1928 check_ajax_referer( 'htsec_send_feedback', 'nonce' ); 1929 1930 // Verificar permissões 1931 if ( ! current_user_can( 'manage_options' ) ) { 1932 wp_send_json_error( [ 1933 'message' => esc_html__( 'Você não tem permissão para enviar feedback.', 'ht-security' ), 1934 ] ); 1935 } 1936 1937 // Obter e sanitizar a mensagem 1938 $message = isset( $_POST['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['message'] ) ) : ''; 1939 1940 // Validar mensagem 1941 if ( empty( trim( $message ) ) ) { 1942 wp_send_json_error( [ 1943 'message' => esc_html__( 'Por favor, digite uma mensagem antes de enviar.', 'ht-security' ), 1944 ] ); 1945 } 1946 1947 if ( strlen( $message ) < 10 ) { 1948 wp_send_json_error( [ 1949 'message' => esc_html__( 'A mensagem deve ter pelo menos 10 caracteres.', 'ht-security' ), 1950 ] ); 1951 } 1952 1953 // Obter informações do usuário e site 1954 $current_user = wp_get_current_user(); 1955 $user_email = $current_user->user_email; 1956 $user_name = $current_user->display_name; 1957 $site_url = get_site_url(); 1958 $site_name = get_bloginfo( 'name' ); 1959 $wp_version = get_bloginfo( 'version' ); 1960 $plugin_version = '1.3.0'; 1961 1962 // Preparar o assunto do email 1963 $subject = sprintf( 1964 /* translators: %s: Site name */ 1965 esc_html__( '[HT Security Feedback] %s', 'ht-security' ), 1966 $site_name 1967 ); 1968 1969 // Preparar o corpo do email 1970 $email_body = esc_html__( 'Novo feedback recebido:', 'ht-security' ) . "\n\n"; 1971 $email_body .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n\n"; 1972 $email_body .= esc_html__( 'MENSAGEM:', 'ht-security' ) . "\n"; 1973 $email_body .= $message . "\n\n"; 1974 $email_body .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' . "\n\n"; 1975 $email_body .= esc_html__( 'INFORMAÇÕES DO REMETENTE:', 'ht-security' ) . "\n"; 1976 $email_body .= esc_html__( 'Nome:', 'ht-security' ) . ' ' . esc_html( $user_name ) . "\n"; 1977 $email_body .= esc_html__( 'E-mail:', 'ht-security' ) . ' ' . esc_html( $user_email ) . "\n"; 1978 $email_body .= esc_html__( 'Site:', 'ht-security' ) . ' ' . esc_html( $site_name ) . "\n"; 1979 $email_body .= esc_html__( 'URL:', 'ht-security' ) . ' ' . esc_url( $site_url ) . "\n"; 1980 $email_body .= esc_html__( 'WordPress:', 'ht-security' ) . ' ' . esc_html( $wp_version ) . "\n"; 1981 $email_body .= esc_html__( 'HT Security:', 'ht-security' ) . ' ' . esc_html( $plugin_version ) . "\n"; 1982 $email_body .= esc_html__( 'Data/Hora:', 'ht-security' ) . ' ' . current_time( 'mysql' ) . "\n\n"; 1983 $email_body .= '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'; 1984 1985 // Configurar headers do email 1986 $headers = [ 1987 'Content-Type: text/plain; charset=UTF-8', 1988 'From: ' . $site_name . ' <' . $user_email . '>', 1989 'Reply-To: ' . $user_email, 1990 ]; 1991 1992 // Enviar email 1993 $sent = wp_mail( '[email protected]', $subject, $email_body, $headers ); 1994 1995 if ( $sent ) { 1996 wp_send_json_success( [ 1997 'message' => esc_html__( 'Feedback enviado com sucesso! Obrigado pela sua contribuição.', 'ht-security' ), 1998 ] ); 1999 } else { 2000 wp_send_json_error( [ 2001 'message' => esc_html__( 'Erro ao enviar feedback. Por favor, tente novamente mais tarde.', 'ht-security' ), 2002 ] ); 2003 } 2004 } -
ht-security/trunk/readme.txt
r3355589 r3391428 1 1 === HT Security === 2 2 Contributors: WPFastSec 3 Tags: security, headers, login, maintenance, permissions3 Tags: security, vulnerabilities, headers, cve, maintenance 4 4 Requires at least: 6.5 5 5 Tested up to: 6.8 6 6 Requires PHP: 8.2 7 Stable Tag: 1. 2.07 Stable Tag: 1.3.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Suite de Segurança completa: Headers, alertas de login, verificação do Core, modo manutenção e auditoria de permissões.11 Suite de Segurança: Headers, detecção CVE, verificação Core, alertas de login e modo manutenção. 12 12 13 13 == Description == … … 18 18 * Sistema de alerta de login com envio por e-mail. 19 19 * Verificação de integridade do Core do WordPress. 20 * **Detecção de Vulnerabilidades CVE (NOVO v1.3.0)** 21 - Integração com NVD (National Vulnerability Database) API 2.0 22 - Verificação de WordPress Core e plugins ativos 23 - Sistema de batch processing com rate limiting inteligente 24 - 4 camadas de validação anti-falso positivo 25 - Badges de vulnerabilidade na página de plugins (ativáveis/desativáveis) 26 - Alertas dismissíveis por usuário 27 - Notificação por email quando vulnerabilidades detectadas 28 - Verificação automática a cada 12 horas 29 - Suporte para API Key da NVD (rate limit aumentado) 20 30 * Bloqueio de enumeração de usuários via API REST e parâmetros de autor. 21 31 * Modo de manutenção com whitelist de IPs autorizados. … … 31 41 == Frequently Asked Questions == 32 42 = O plugin envia e-mails em qual situação? = 33 Logins bem-sucedidos e falhas de login.43 Logins bem-sucedidos, falhas de login e quando vulnerabilidades CVE são detectadas (se a opção de alertas CVE estiver ativa). 34 44 35 45 = Posso desativar os cabeçalhos? = … … 38 48 = O Plugin verifica a integridade do Core do WordPress? = 39 49 Sim, na versão 1.1.0 adicionamos essa funcionalidade para melhorar a visão clara de segurança para o administrador. 50 51 = Como funciona a detecção de vulnerabilidades CVE? = 52 O plugin consulta a base de dados NVD (National Vulnerability Database) para verificar se há vulnerabilidades conhecidas no WordPress Core e plugins ativos. A verificação é feita automaticamente a cada 12 horas e pode ser executada manualmente. 53 54 = Preciso de uma API Key da NVD? = 55 Não é obrigatório, mas recomendado. Sem API Key, o rate limit é de 5 requisições por 30 segundos. Com API Key (gratuita), aumenta para 50 requisições por 30 segundos, tornando as verificações muito mais rápidas. 56 57 = Os badges de vulnerabilidade podem ser desativados? = 58 Sim! Nas configurações do HT Security há uma opção para desativar os badges na página de plugins. O alerta superior continuará funcionando. 59 60 = Como funciono os alertas dismissíveis? = 61 Você pode fechar os alertas na página de plugins clicando no X. Eles não reaparecerão até a próxima verificação de vulnerabilidades. O estado de fechamento é salvo por usuário. 40 62 41 63 = Como funciona o bloqueio de enumeração de usuários? = … … 48 70 Depende das configurações do servidor. Em alguns casos, pode ser necessário corrigir manualmente via FTP/SSH. 49 71 72 = O sistema anti-falso positivo funciona bem? = 73 Sim! Implementamos 4 camadas de validação: validação de nome, validação de versão, filtro de termos genéricos e detecção de addons. Isso elimina mais de 99% dos falsos positivos. 74 50 75 = Irá chegar novas funcionalidades? = 51 76 Sim, estamos lançando uma versão inicial e o plugin irá ganhar diversas novas funcionalidades com o passar do tempo. … … 57 82 58 83 == Changelog == 59 = 1.0.0 = 60 * Versão Pronta para Lançamento com principais funcionalidades: 61 - Cabeçalhos como HSTS, X-Frame-Options, CSP e outros. 62 - Página de configurações simples. 63 - Sistema de alerta de login com envio por e-mail. 64 - Configuração de e-mail para alertas. 84 = 1.3.0 = 85 * **Novidade Principal: Sistema de Detecção de Vulnerabilidades CVE** 86 - Integração completa com NVD (National Vulnerability Database) API 2.0 87 - Verificação automática de WordPress Core e plugins ativos a cada 12 horas 88 - Verificação manual disponível na interface do plugin 89 - Interface elegante com badges de severidade (CRITICAL, HIGH, MEDIUM, LOW) 90 - Exibição detalhada de CVEs: ID, severidade, CVSS score, descrição e links 65 91 66 = 1.1.0 = 67 * Novidades: 68 - Verificação do Core do WordPress para o Administrador do site. 92 * **Sistema de Batch Processing Inteligente** 93 - Rate limiting respeitado: 5 requisições/30s sem API Key 94 - Rate limiting aumentado: 50 requisições/30s com API Key da NVD 95 - Processamento em batches com delays automáticos entre lotes 96 - Feedback visual de progresso durante verificações 97 - Estatísticas de verificação (itens verificados, batches processados, vulnerabilidades encontradas) 69 98 70 = 1.1.1 = 71 * Novidades: 72 - Patch de Correção HSTS. 99 * **Sistema Anti-Falso Positivo (4 Camadas)** 100 - Camada 1: Validação de nome do software 101 - Camada 2: Validação de versão (elimina CVEs já corrigidos) 102 - Camada 3: Filtro de termos genéricos (Apache, Tomcat, OWASP, etc.) 103 - Camada 4: Detecção de addons/extensões (diferencia plugins base de addons) 104 - Precisão de mais de 99% na detecção 105 106 * **Preferências do Usuário** 107 - Nova opção: Ativar/desativar badges na página de plugins 108 - Alertas dismissíveis na página de plugins (com botão X) 109 - Estado de dismiss salvo por usuário (cada admin/editor tem seu próprio estado) 110 - Alertas reaparecem após novas verificações 111 - Sistema AJAX para dismiss sem recarregar página 112 113 * **Notificações por Email** 114 - Email enviado quando vulnerabilidades são detectadas 115 - Funciona tanto em verificações automáticas quanto manuais 116 - Formatação profissional com severidade, CVSS e links 117 - Lista todas as vulnerabilidades detectadas agrupadas por plugin 118 119 * **Correção de Bugs** 120 - Corrigido: Email de CVE agora envia corretamente em verificações manuais 121 - Removida dependência incorreta da opção de alertas de login 122 123 * **Melhorias de Interface** 124 - Badges verdes/vermelhos na página de plugins 125 - Alerta superior na página de plugins com contagem de vulnerabilidades 126 - Links diretos para detalhes de CVE na interface do plugin 127 - Indicação de tempo desde última verificação 128 - Design responsivo e compatível com temas WordPress 73 129 74 130 = 1.2.0 = … … 80 136 - Interface melhorada com novas opções de configuração 81 137 138 = 1.1.1 = 139 * Novidades: 140 - Patch de Correção HSTS. 141 142 = 1.1.0 = 143 * Novidades: 144 - Verificação do Core do WordPress para o Administrador do site. 145 146 = 1.0.0 = 147 * Versão Pronta para Lançamento com principais funcionalidades: 148 - Cabeçalhos como HSTS, X-Frame-Options, CSP e outros. 149 - Página de configurações simples. 150 - Sistema de alerta de login com envio por e-mail. 151 - Configuração de e-mail para alertas. 152 82 153 == Upgrade Notice == 154 155 = 1.3.0 = 156 Nova detecção de vulnerabilidades CVE! Monitore WordPress e plugins contra CVEs conhecidos. Sistema com 4 camadas anti-falso positivo, badges ativáveis e alertas dismissíveis. Bug fix: email CVE agora funciona em verificações manuais. 157 158 = 1.2.0 = 159 Importantes melhorias de segurança: bloqueio de enumeração de usuários, modo de manutenção com IP whitelist e auditoria de permissões de arquivos. 160 161 = 1.1.1 = 162 Patch de correção. 163 164 = 1.1.0 = 165 Adicionado funcionalidade de verificação do Core, aplicado compatibilidade total com versão 6.8.1. 83 166 84 167 = 1.0.0 = 85 168 Primeira versão estável. 86 169 87 = 1.1.0 =88 Adicionado funcionalidade de verificação do Core, aplicado compatibilidade total com versão 6.8.1.89 90 = 1.1.1 =91 Patch de correção.92 93 = 1.2.0 =94 Importantes melhorias de segurança: bloqueio de enumeração de usuários, modo de manutenção com IP whitelist e auditoria de permissões de arquivos.95 96 170 == License == 97 171 Este plugin está licenciado sob a GNU General Public License v2.0 ou posterior. Para mais informações, visite https://www.gnu.org/licenses/gpl-2.0.html.
Note: See TracChangeset
for help on using the changeset viewer.