mirror of
https://gitlab.com/ArkHost/WP-Security-Pack.git
synced 2026-09-19 17:37:30 +02:00
3232 lines
133 KiB
PHP
3232 lines
133 KiB
PHP
<?php
|
|
/**
|
|
* Admin interface for WP Security Pack.
|
|
*
|
|
* @package WP_Security_Pack
|
|
*/
|
|
|
|
// Prevent direct access.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Admin class for settings page and UI.
|
|
*/
|
|
class WPSP_Admin {
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function __construct() {
|
|
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
|
|
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
|
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
|
|
add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widget' ) );
|
|
add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
|
|
add_filter( 'plugin_action_links_' . WPSP_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ) );
|
|
add_filter( 'submenu_file', array( $this, 'highlight_submenu' ) );
|
|
|
|
// AJAX handlers.
|
|
add_action( 'wp_ajax_wpsp_clear_logs', array( $this, 'ajax_clear_logs' ) );
|
|
add_action( 'wp_ajax_wpsp_export_logs', array( $this, 'ajax_export_logs' ) );
|
|
add_action( 'wp_ajax_wpsp_whitelist_ip', array( $this, 'ajax_whitelist_ip' ) );
|
|
add_action( 'wp_ajax_wpsp_clear_lockouts', array( $this, 'ajax_clear_lockouts' ) );
|
|
add_action( 'wp_ajax_wpsp_run_file_scan', array( $this, 'ajax_run_file_scan' ) );
|
|
add_action( 'wp_ajax_wpsp_run_malware_scan', array( $this, 'ajax_run_malware_scan' ) );
|
|
add_action( 'wp_ajax_wpsp_reset_file_baseline', array( $this, 'ajax_reset_file_baseline' ) );
|
|
add_action( 'wp_ajax_wpsp_clear_malware_results', array( $this, 'ajax_clear_malware_results' ) );
|
|
add_action( 'wp_ajax_wpsp_download_geo_db', array( $this, 'ajax_download_geo_db' ) );
|
|
add_action( 'wp_ajax_wpsp_export_settings', array( $this, 'ajax_export_settings' ) );
|
|
add_action( 'wp_ajax_wpsp_import_settings', array( $this, 'ajax_import_settings' ) );
|
|
add_action( 'wp_ajax_wpsp_reset_settings', array( $this, 'ajax_reset_settings' ) );
|
|
add_action( 'wp_ajax_wpsp_test_email', array( $this, 'ajax_test_email' ) );
|
|
add_action( 'wp_ajax_wpsp_force_logout_all', array( $this, 'ajax_force_logout_all' ) );
|
|
add_action( 'wp_ajax_wpsp_quarantine_file', array( $this, 'ajax_quarantine_file' ) );
|
|
add_action( 'wp_ajax_wpsp_restore_file', array( $this, 'ajax_restore_file' ) );
|
|
add_action( 'wp_ajax_wpsp_delete_quarantined', array( $this, 'ajax_delete_quarantined' ) );
|
|
add_action( 'wp_ajax_wpsp_delete_wp_file', array( $this, 'ajax_delete_wp_file' ) );
|
|
}
|
|
|
|
/**
|
|
* Add admin menu.
|
|
*/
|
|
public function add_admin_menu() {
|
|
// Main menu item.
|
|
add_menu_page(
|
|
__( 'WP Security Pack', 'wp-security-pack' ),
|
|
__( 'Security', 'wp-security-pack' ),
|
|
'manage_options',
|
|
'wp-security-pack',
|
|
array( $this, 'render_settings_page' ),
|
|
'dashicons-shield-alt',
|
|
80
|
|
);
|
|
|
|
// Submenus for each tab.
|
|
$submenus = array(
|
|
'status' => __( 'Status', 'wp-security-pack' ),
|
|
'login' => __( 'Login Protection', 'wp-security-pack' ),
|
|
'ip' => __( 'IP Control', 'wp-security-pack' ),
|
|
'hardening' => __( 'Hardening', 'wp-security-pack' ),
|
|
'headers' => __( 'Security Headers', 'wp-security-pack' ),
|
|
'2fa' => __( '2FA', 'wp-security-pack' ),
|
|
'scanner' => __( 'Scanner', 'wp-security-pack' ),
|
|
'logs' => __( 'Activity Log', 'wp-security-pack' ),
|
|
'tools' => __( 'Tools', 'wp-security-pack' ),
|
|
);
|
|
|
|
foreach ( $submenus as $slug => $title ) {
|
|
add_submenu_page(
|
|
'wp-security-pack',
|
|
$title . ' - ' . __( 'WP Security Pack', 'wp-security-pack' ),
|
|
$title,
|
|
'manage_options',
|
|
'status' === $slug ? 'wp-security-pack' : 'wp-security-pack&tab=' . $slug,
|
|
array( $this, 'render_settings_page' )
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Highlight the correct submenu based on current tab.
|
|
*
|
|
* @param string $submenu_file The current submenu file.
|
|
* @return string
|
|
*/
|
|
public function highlight_submenu( $submenu_file ) {
|
|
global $pagenow;
|
|
|
|
if ( 'admin.php' !== $pagenow ) {
|
|
return $submenu_file;
|
|
}
|
|
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
|
|
|
|
if ( 'wp-security-pack' !== $page ) {
|
|
return $submenu_file;
|
|
}
|
|
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'status';
|
|
|
|
if ( 'status' === $tab ) {
|
|
return 'wp-security-pack';
|
|
}
|
|
|
|
return 'wp-security-pack&tab=' . $tab;
|
|
}
|
|
|
|
/**
|
|
* Add plugin action links (left side - Settings, Deactivate, etc.).
|
|
*
|
|
* @param array $links Existing links.
|
|
* @return array
|
|
*/
|
|
public function plugin_action_links( $links ) {
|
|
$settings_link = sprintf(
|
|
'<a href="%s">%s</a>',
|
|
admin_url( 'admin.php?page=wp-security-pack' ),
|
|
__( 'Settings', 'wp-security-pack' )
|
|
);
|
|
array_unshift( $links, $settings_link );
|
|
return $links;
|
|
}
|
|
|
|
/**
|
|
* Add plugin row meta links (right side - after version).
|
|
*
|
|
* @param array $links Existing meta links.
|
|
* @param string $file Plugin file.
|
|
* @return array
|
|
*/
|
|
public function plugin_row_meta( $links, $file ) {
|
|
if ( WPSP_PLUGIN_BASENAME !== $file ) {
|
|
return $links;
|
|
}
|
|
|
|
$links[] = sprintf(
|
|
'<a href="%s" target="_blank">%s</a>',
|
|
'https://arkhost.com/products-menu.php',
|
|
__( 'Get Hosting', 'wp-security-pack' )
|
|
);
|
|
|
|
return $links;
|
|
}
|
|
|
|
/**
|
|
* Add dashboard widget.
|
|
*/
|
|
public function add_dashboard_widget() {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
|
|
wp_add_dashboard_widget(
|
|
'wpsp_security_widget',
|
|
__( 'Security Status', 'wp-security-pack' ),
|
|
array( $this, 'render_dashboard_widget' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render dashboard widget.
|
|
*/
|
|
public function render_dashboard_widget() {
|
|
$checks = $this->get_security_checks();
|
|
$enabled_count = 0;
|
|
$total_count = count( $checks );
|
|
|
|
foreach ( $checks as $check ) {
|
|
if ( $check['status'] ) {
|
|
$enabled_count++;
|
|
}
|
|
}
|
|
|
|
$score = $total_count > 0 ? round( ( $enabled_count / $total_count ) * 100 ) : 0;
|
|
|
|
// Get recent activity stats.
|
|
$stats = WPSP_Activity_Log::get_stats( 7 );
|
|
?>
|
|
<style>
|
|
.wpsp-widget-score {
|
|
text-align: center;
|
|
padding: 15px 0;
|
|
border-bottom: 1px solid #eee;
|
|
margin-bottom: 15px;
|
|
}
|
|
.wpsp-widget-score-value {
|
|
font-size: 42px;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
}
|
|
.wpsp-widget-score-label {
|
|
color: #666;
|
|
font-size: 12px;
|
|
margin-top: 5px;
|
|
}
|
|
.wpsp-widget-score.good .wpsp-widget-score-value { color: #00a32a; }
|
|
.wpsp-widget-score.warning .wpsp-widget-score-value { color: #dba617; }
|
|
.wpsp-widget-score.bad .wpsp-widget-score-value { color: #d63638; }
|
|
.wpsp-widget-stats {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
text-align: center;
|
|
margin-bottom: 15px;
|
|
}
|
|
.wpsp-widget-stat-value {
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
}
|
|
.wpsp-widget-stat-label {
|
|
font-size: 11px;
|
|
color: #666;
|
|
}
|
|
.wpsp-widget-links {
|
|
border-top: 1px solid #eee;
|
|
padding-top: 12px;
|
|
text-align: center;
|
|
}
|
|
.wpsp-widget-links a {
|
|
margin: 0 8px;
|
|
}
|
|
</style>
|
|
|
|
<div class="wpsp-widget-score <?php echo $score >= 80 ? 'good' : ( $score >= 50 ? 'warning' : 'bad' ); ?>">
|
|
<div class="wpsp-widget-score-value"><?php echo esc_html( $score ); ?>%</div>
|
|
<div class="wpsp-widget-score-label"><?php esc_html_e( 'Security Score', 'wp-security-pack' ); ?></div>
|
|
</div>
|
|
|
|
<div class="wpsp-widget-stats">
|
|
<div>
|
|
<div class="wpsp-widget-stat-value"><?php echo esc_html( $stats['login_success'] ?? 0 ); ?></div>
|
|
<div class="wpsp-widget-stat-label"><?php esc_html_e( 'Logins (7d)', 'wp-security-pack' ); ?></div>
|
|
</div>
|
|
<div>
|
|
<div class="wpsp-widget-stat-value" style="color: #dba617;"><?php echo esc_html( $stats['login_failed'] ?? 0 ); ?></div>
|
|
<div class="wpsp-widget-stat-label"><?php esc_html_e( 'Failed (7d)', 'wp-security-pack' ); ?></div>
|
|
</div>
|
|
<div>
|
|
<div class="wpsp-widget-stat-value" style="color: #d63638;"><?php echo esc_html( $stats['lockout'] ?? 0 ); ?></div>
|
|
<div class="wpsp-widget-stat-label"><?php esc_html_e( 'Lockouts (7d)', 'wp-security-pack' ); ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wpsp-widget-links">
|
|
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-security-pack' ) ); ?>"><?php esc_html_e( 'Dashboard', 'wp-security-pack' ); ?></a>
|
|
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-security-pack&tab=logs' ) ); ?>"><?php esc_html_e( 'Activity Log', 'wp-security-pack' ); ?></a>
|
|
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-security-pack&tab=scanner' ) ); ?>"><?php esc_html_e( 'Scanner', 'wp-security-pack' ); ?></a>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Register settings.
|
|
*/
|
|
public function register_settings() {
|
|
register_setting( 'wpsp_settings', 'wpsp_settings', array( $this, 'sanitize_settings' ) );
|
|
}
|
|
|
|
/**
|
|
* Sanitize settings.
|
|
*
|
|
* @param array $input Input settings.
|
|
* @return array
|
|
*/
|
|
public function sanitize_settings( $input ) {
|
|
// Start with existing settings to preserve values from other tabs.
|
|
$existing = get_option( 'wpsp_settings', array() );
|
|
$sanitized = is_array( $existing ) ? $existing : array();
|
|
$defaults = WP_Security_Pack::get_default_settings();
|
|
|
|
// Determine which tab is being saved (from hidden field).
|
|
$current_tab = isset( $input['wpsp_current_tab'] ) ? sanitize_text_field( $input['wpsp_current_tab'] ) : '';
|
|
|
|
// Only sanitize settings for the current tab to avoid resetting other tabs.
|
|
// If no tab specified (e.g., import), sanitize all provided settings.
|
|
|
|
if ( '' === $current_tab || 'login' === $current_tab ) {
|
|
// Login Protection - only update if this tab is submitted or field is present.
|
|
$sanitized['login_limit_enabled'] = ! empty( $input['login_limit_enabled'] );
|
|
$sanitized['login_max_attempts'] = isset( $input['login_max_attempts'] ) ? absint( $input['login_max_attempts'] ) : ( $sanitized['login_max_attempts'] ?? 5 );
|
|
$sanitized['login_lockout_duration'] = isset( $input['login_lockout_duration'] ) ? absint( $input['login_lockout_duration'] ) : ( $sanitized['login_lockout_duration'] ?? 15 );
|
|
$sanitized['login_rename_enabled'] = ! empty( $input['login_rename_enabled'] );
|
|
$sanitized['login_custom_url'] = isset( $input['login_custom_url'] ) ? WPSP_Helper::sanitize_login_slug( $input['login_custom_url'] ) : ( $sanitized['login_custom_url'] ?? '' );
|
|
$sanitized['hide_wp_admin'] = ! empty( $input['hide_wp_admin'] );
|
|
$sanitized['honeypot_enabled'] = ! empty( $input['honeypot_enabled'] );
|
|
$sanitized['honeypot_ban_duration'] = isset( $input['honeypot_ban_duration'] ) ? absint( $input['honeypot_ban_duration'] ) : ( $sanitized['honeypot_ban_duration'] ?? 60 );
|
|
$sanitized['hide_login_errors'] = ! empty( $input['hide_login_errors'] );
|
|
$sanitized['admin_login_notify'] = ! empty( $input['admin_login_notify'] );
|
|
|
|
// Login Access Restriction.
|
|
$sanitized['admin_access_restriction'] = ! empty( $input['admin_access_restriction'] );
|
|
if ( 'login' === $current_tab ) {
|
|
$sanitized['admin_allowed_countries'] = isset( $input['admin_allowed_countries'] ) && is_array( $input['admin_allowed_countries'] )
|
|
? array_map( 'sanitize_text_field', $input['admin_allowed_countries'] )
|
|
: array();
|
|
} elseif ( isset( $input['admin_allowed_countries'] ) ) {
|
|
$sanitized['admin_allowed_countries'] = is_array( $input['admin_allowed_countries'] )
|
|
? array_map( 'sanitize_text_field', $input['admin_allowed_countries'] )
|
|
: array();
|
|
}
|
|
$sanitized['admin_allowed_ips'] = isset( $input['admin_allowed_ips'] ) ? sanitize_textarea_field( $input['admin_allowed_ips'] ) : ( $sanitized['admin_allowed_ips'] ?? '' );
|
|
|
|
// Email Alerts (also on login tab).
|
|
$sanitized['email_alerts_enabled'] = ! empty( $input['email_alerts_enabled'] );
|
|
$sanitized['email_alerts_address'] = isset( $input['email_alerts_address'] ) ? sanitize_email( $input['email_alerts_address'] ) : ( $sanitized['email_alerts_address'] ?? '' );
|
|
$sanitized['email_alert_threshold'] = isset( $input['email_alert_threshold'] ) ? absint( $input['email_alert_threshold'] ) : ( $sanitized['email_alert_threshold'] ?? 3 );
|
|
}
|
|
|
|
if ( '' === $current_tab || 'ip' === $current_tab ) {
|
|
// IP Control.
|
|
$sanitized['ip_whitelist'] = isset( $input['ip_whitelist'] ) ? sanitize_textarea_field( $input['ip_whitelist'] ) : ( $sanitized['ip_whitelist'] ?? '' );
|
|
$sanitized['ip_blacklist'] = isset( $input['ip_blacklist'] ) ? sanitize_textarea_field( $input['ip_blacklist'] ) : ( $sanitized['ip_blacklist'] ?? '' );
|
|
$sanitized['auto_blacklist_enabled'] = ! empty( $input['auto_blacklist_enabled'] );
|
|
$sanitized['auto_blacklist_threshold'] = isset( $input['auto_blacklist_threshold'] ) ? max( 1, absint( $input['auto_blacklist_threshold'] ) ) : ( $sanitized['auto_blacklist_threshold'] ?? 3 );
|
|
|
|
// Geo Blocking.
|
|
$sanitized['geo_blocking_enabled'] = ! empty( $input['geo_blocking_enabled'] );
|
|
// For geo_blocked_countries, if the tab is IP and field not set, it means nothing selected.
|
|
if ( 'ip' === $current_tab ) {
|
|
$sanitized['geo_blocked_countries'] = isset( $input['geo_blocked_countries'] ) && is_array( $input['geo_blocked_countries'] )
|
|
? array_map( 'sanitize_text_field', $input['geo_blocked_countries'] )
|
|
: array();
|
|
} elseif ( isset( $input['geo_blocked_countries'] ) ) {
|
|
$sanitized['geo_blocked_countries'] = is_array( $input['geo_blocked_countries'] )
|
|
? array_map( 'sanitize_text_field', $input['geo_blocked_countries'] )
|
|
: array();
|
|
}
|
|
$sanitized['geo_database_path'] = isset( $input['geo_database_path'] ) ? sanitize_text_field( $input['geo_database_path'] ) : ( $sanitized['geo_database_path'] ?? '' );
|
|
}
|
|
|
|
if ( '' === $current_tab || 'hardening' === $current_tab ) {
|
|
// Hardening.
|
|
$sanitized['disable_xmlrpc'] = ! empty( $input['disable_xmlrpc'] );
|
|
$sanitized['disable_file_editing'] = ! empty( $input['disable_file_editing'] );
|
|
$sanitized['disable_application_passwords'] = ! empty( $input['disable_application_passwords'] );
|
|
$sanitized['restrict_rest_api'] = ! empty( $input['restrict_rest_api'] );
|
|
$sanitized['remove_wp_version'] = ! empty( $input['remove_wp_version'] );
|
|
$sanitized['remove_feed_links'] = ! empty( $input['remove_feed_links'] );
|
|
$sanitized['add_security_headers'] = ! empty( $input['add_security_headers'] );
|
|
$sanitized['disable_user_enumeration'] = ! empty( $input['disable_user_enumeration'] );
|
|
$sanitized['disable_pingbacks'] = ! empty( $input['disable_pingbacks'] );
|
|
}
|
|
|
|
if ( '' === $current_tab || 'headers' === $current_tab ) {
|
|
// Security Headers.
|
|
if ( isset( $input['security_headers'] ) && is_array( $input['security_headers'] ) ) {
|
|
$sanitized['security_headers'] = array_map( 'sanitize_text_field', $input['security_headers'] );
|
|
}
|
|
}
|
|
|
|
if ( '' === $current_tab || '2fa' === $current_tab ) {
|
|
// Two-Factor.
|
|
$sanitized['two_factor_enabled'] = ! empty( $input['two_factor_enabled'] );
|
|
$sanitized['two_factor_enforce_admin'] = ! empty( $input['two_factor_enforce_admin'] );
|
|
}
|
|
|
|
if ( '' === $current_tab || 'scanner' === $current_tab ) {
|
|
// File Integrity.
|
|
$sanitized['file_integrity_enabled'] = ! empty( $input['file_integrity_enabled'] );
|
|
|
|
// Malware Scanner.
|
|
$sanitized['malware_scan_enabled'] = ! empty( $input['malware_scan_enabled'] );
|
|
}
|
|
|
|
if ( '' === $current_tab || 'logs' === $current_tab ) {
|
|
// Activity Log.
|
|
$sanitized['log_retention_days'] = isset( $input['log_retention_days'] ) ? absint( $input['log_retention_days'] ) : ( $sanitized['log_retention_days'] ?? 30 );
|
|
}
|
|
|
|
// Flush rewrite rules if login URL changed.
|
|
if (
|
|
( isset( $existing['login_custom_url'] ) && $existing['login_custom_url'] !== ( $sanitized['login_custom_url'] ?? '' ) ) ||
|
|
( isset( $existing['login_rename_enabled'] ) && $existing['login_rename_enabled'] !== ( $sanitized['login_rename_enabled'] ?? false ) )
|
|
) {
|
|
flush_rewrite_rules();
|
|
}
|
|
|
|
return $sanitized;
|
|
}
|
|
|
|
/**
|
|
* Enqueue admin assets.
|
|
*
|
|
* @param string $hook Admin page hook.
|
|
*/
|
|
public function enqueue_admin_assets( $hook ) {
|
|
// Check for top-level page and all subpages.
|
|
if ( 'toplevel_page_wp-security-pack' !== $hook && strpos( $hook, 'security_page_wp-security-pack' ) === false ) {
|
|
return;
|
|
}
|
|
|
|
wp_enqueue_style(
|
|
'wpsp-admin',
|
|
WPSP_PLUGIN_URL . 'assets/css/admin.css',
|
|
array(),
|
|
WPSP_VERSION
|
|
);
|
|
|
|
wp_enqueue_script( 'jquery' );
|
|
}
|
|
|
|
/**
|
|
* Render settings page.
|
|
*/
|
|
public function render_settings_page() {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
|
|
// Get current tab.
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$current_tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'status';
|
|
|
|
$tabs = array(
|
|
'status' => __( 'Status', 'wp-security-pack' ),
|
|
'login' => __( 'Login Protection', 'wp-security-pack' ),
|
|
'ip' => __( 'IP Control', 'wp-security-pack' ),
|
|
'hardening' => __( 'Hardening', 'wp-security-pack' ),
|
|
'headers' => __( 'Security Headers', 'wp-security-pack' ),
|
|
'2fa' => __( '2FA', 'wp-security-pack' ),
|
|
'scanner' => __( 'Scanner', 'wp-security-pack' ),
|
|
'logs' => __( 'Activity Log', 'wp-security-pack' ),
|
|
'tools' => __( 'Tools', 'wp-security-pack' ),
|
|
);
|
|
?>
|
|
<div class="wrap wpsp-wrap">
|
|
<h1><?php esc_html_e( 'WP Security Pack', 'wp-security-pack' ); ?></h1>
|
|
|
|
<nav class="nav-tab-wrapper wpsp-tabs">
|
|
<?php foreach ( $tabs as $tab => $label ) : ?>
|
|
<a href="<?php echo esc_url( add_query_arg( 'tab', $tab ) ); ?>"
|
|
class="nav-tab <?php echo $tab === $current_tab ? 'nav-tab-active' : ''; ?>">
|
|
<?php echo esc_html( $label ); ?>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</nav>
|
|
|
|
<form method="post" action="options.php" class="wpsp-form">
|
|
<?php
|
|
settings_fields( 'wpsp_settings' );
|
|
|
|
switch ( $current_tab ) {
|
|
case 'status':
|
|
$this->render_status_tab();
|
|
break;
|
|
case 'login':
|
|
$this->render_login_tab();
|
|
break;
|
|
case 'ip':
|
|
$this->render_ip_tab();
|
|
break;
|
|
case 'hardening':
|
|
$this->render_hardening_tab();
|
|
break;
|
|
case 'headers':
|
|
$this->render_headers_tab();
|
|
break;
|
|
case '2fa':
|
|
$this->render_2fa_tab();
|
|
break;
|
|
case 'scanner':
|
|
$this->render_scanner_tab();
|
|
break;
|
|
case 'logs':
|
|
$this->render_logs_tab();
|
|
break;
|
|
case 'tools':
|
|
$this->render_tools_tab();
|
|
break;
|
|
}
|
|
|
|
if ( ! in_array( $current_tab, array( 'status', 'logs', 'tools' ), true ) ) {
|
|
submit_button();
|
|
}
|
|
?>
|
|
</form>
|
|
|
|
<p class="wpsp-footer">
|
|
<?php
|
|
printf(
|
|
/* translators: %s: ArkHost link */
|
|
esc_html__( 'WP Security Pack by %s - Free forever, no bullshit.', 'wp-security-pack' ),
|
|
'<a href="https://arkhost.com" target="_blank">ArkHost</a>'
|
|
);
|
|
?>
|
|
</p>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render status/checklist tab.
|
|
*/
|
|
private function render_status_tab() {
|
|
// Gather all security status information.
|
|
$checks = $this->get_security_checks();
|
|
$enabled_count = 0;
|
|
$total_count = count( $checks );
|
|
|
|
foreach ( $checks as $check ) {
|
|
if ( $check['status'] ) {
|
|
$enabled_count++;
|
|
}
|
|
}
|
|
|
|
$score = $total_count > 0 ? round( ( $enabled_count / $total_count ) * 100 ) : 0;
|
|
?>
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Security Status', 'wp-security-pack' ); ?></h2>
|
|
|
|
<div class="wpsp-stats">
|
|
<div class="wpsp-stat <?php echo $score >= 80 ? 'wpsp-stat-success' : ( $score >= 50 ? 'wpsp-stat-warning' : 'wpsp-stat-danger' ); ?>">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $score ); ?>%</span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Security Score', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
<div class="wpsp-stat wpsp-stat-success">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $enabled_count ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Protections Active', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
<div class="wpsp-stat <?php echo ( $total_count - $enabled_count ) > 0 ? 'wpsp-stat-warning' : 'wpsp-stat-info'; ?>">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $total_count - $enabled_count ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Not Enabled', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="widefat striped" style="margin-top: 20px;">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 40px;"><?php esc_html_e( 'Status', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Security Feature', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Description', 'wp-security-pack' ); ?></th>
|
|
<th style="width: 100px;"><?php esc_html_e( 'Action', 'wp-security-pack' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ( $checks as $key => $check ) : ?>
|
|
<tr>
|
|
<td>
|
|
<?php if ( $check['status'] ) : ?>
|
|
<span style="color: #46b450; font-size: 18px;">✓</span>
|
|
<?php else : ?>
|
|
<span style="color: #dc3232; font-size: 18px;">✗</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><strong><?php echo esc_html( $check['name'] ); ?></strong></td>
|
|
<td><span class="description"><?php echo esc_html( $check['description'] ); ?></span></td>
|
|
<td>
|
|
<?php if ( ! empty( $check['tab'] ) ) : ?>
|
|
<a href="<?php echo esc_url( add_query_arg( 'tab', $check['tab'] ) ); ?>" class="button button-small">
|
|
<?php echo $check['status'] ? esc_html__( 'Configure', 'wp-security-pack' ) : esc_html__( 'Enable', 'wp-security-pack' ); ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Quick Info', 'wp-security-pack' ); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th><?php esc_html_e( 'Your IP Address', 'wp-security-pack' ); ?></th>
|
|
<td><code><?php echo esc_html( WPSP_Helper::get_client_ip() ); ?></code></td>
|
|
</tr>
|
|
<tr>
|
|
<th><?php esc_html_e( 'WordPress Version', 'wp-security-pack' ); ?></th>
|
|
<td><?php echo esc_html( get_bloginfo( 'version' ) ); ?></td>
|
|
</tr>
|
|
<tr>
|
|
<th><?php esc_html_e( 'PHP Version', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php echo esc_html( PHP_VERSION ); ?>
|
|
<?php if ( version_compare( PHP_VERSION, '8.0', '<' ) ) : ?>
|
|
<span class="wpsp-status wpsp-status-warning"><?php esc_html_e( 'Update recommended', 'wp-security-pack' ); ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><?php esc_html_e( 'HTTPS', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php if ( is_ssl() ) : ?>
|
|
<span class="wpsp-status wpsp-status-ok"><?php esc_html_e( 'Active', 'wp-security-pack' ); ?></span>
|
|
<?php else : ?>
|
|
<span class="wpsp-status wpsp-status-error"><?php esc_html_e( 'Not active', 'wp-security-pack' ); ?></span>
|
|
<span class="description"><?php esc_html_e( 'HTTPS is strongly recommended', 'wp-security-pack' ); ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Debug Mode', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) : ?>
|
|
<span class="wpsp-status wpsp-status-warning"><?php esc_html_e( 'Enabled', 'wp-security-pack' ); ?></span>
|
|
<span class="description"><?php esc_html_e( 'Disable WP_DEBUG in production', 'wp-security-pack' ); ?></span>
|
|
<?php else : ?>
|
|
<span class="wpsp-status wpsp-status-ok"><?php esc_html_e( 'Disabled', 'wp-security-pack' ); ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Database Prefix', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php global $wpdb; ?>
|
|
<?php if ( 'wp_' === $wpdb->prefix ) : ?>
|
|
<span class="wpsp-status wpsp-status-warning"><code>wp_</code></span>
|
|
<span class="description"><?php esc_html_e( 'Default prefix - consider changing for new installs', 'wp-security-pack' ); ?></span>
|
|
<?php else : ?>
|
|
<span class="wpsp-status wpsp-status-ok"><code><?php echo esc_html( $wpdb->prefix ); ?></code></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Admin Username', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php if ( username_exists( 'admin' ) ) : ?>
|
|
<span class="wpsp-status wpsp-status-warning"><?php esc_html_e( 'Exists', 'wp-security-pack' ); ?></span>
|
|
<span class="description"><?php esc_html_e( '"admin" username is commonly targeted', 'wp-security-pack' ); ?></span>
|
|
<?php else : ?>
|
|
<span class="wpsp-status wpsp-status-ok"><?php esc_html_e( 'Not found', 'wp-security-pack' ); ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
$custom_login = WP_Security_Pack::get_setting( 'custom_login_url', '' );
|
|
if ( ! empty( $custom_login ) ) :
|
|
?>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Custom Login URL', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<code><?php echo esc_url( home_url( '/' . $custom_login . '/' ) ); ?></code>
|
|
<span class="description"><?php esc_html_e( 'Bookmark this!', 'wp-security-pack' ); ?></span>
|
|
</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
</table>
|
|
</div>
|
|
|
|
<?php
|
|
// Check for files that expose WordPress version.
|
|
$readme_exists = file_exists( ABSPATH . 'readme.html' );
|
|
$license_exists = file_exists( ABSPATH . 'license.txt' );
|
|
|
|
if ( $readme_exists || $license_exists ) :
|
|
?>
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Recommended Actions', 'wp-security-pack' ); ?></h2>
|
|
<div class="notice notice-warning inline" style="margin: 0 0 15px;">
|
|
<p><?php esc_html_e( 'These files expose your WordPress version and can be safely removed.', 'wp-security-pack' ); ?></p>
|
|
</div>
|
|
<table class="widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'File', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Status', 'wp-security-pack' ); ?></th>
|
|
<th style="width: 100px;"><?php esc_html_e( 'Action', 'wp-security-pack' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if ( $readme_exists ) : ?>
|
|
<tr>
|
|
<td><code>readme.html</code></td>
|
|
<td><span class="wpsp-status wpsp-status-warning"><?php esc_html_e( 'Exists', 'wp-security-pack' ); ?></span></td>
|
|
<td>
|
|
<button type="button" class="button button-small wpsp-delete-file" data-file="readme.html">
|
|
<?php esc_html_e( 'Delete', 'wp-security-pack' ); ?>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
<?php if ( $license_exists ) : ?>
|
|
<tr>
|
|
<td><code>license.txt</code></td>
|
|
<td><span class="wpsp-status wpsp-status-warning"><?php esc_html_e( 'Exists', 'wp-security-pack' ); ?></span></td>
|
|
<td>
|
|
<button type="button" class="button button-small wpsp-delete-file" data-file="license.txt">
|
|
<?php esc_html_e( 'Delete', 'wp-security-pack' ); ?>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<script>
|
|
jQuery(function($) {
|
|
$('.wpsp-delete-file').on('click', function() {
|
|
var $btn = $(this);
|
|
var file = $btn.data('file');
|
|
|
|
if (!confirm('<?php echo esc_js( __( 'Delete this file?', 'wp-security-pack' ) ); ?>')) {
|
|
return;
|
|
}
|
|
|
|
$btn.prop('disabled', true).text('<?php echo esc_js( __( 'Deleting...', 'wp-security-pack' ) ); ?>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_delete_wp_file',
|
|
file: file,
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$btn.closest('tr').fadeOut();
|
|
} else {
|
|
alert(response.data.message || '<?php echo esc_js( __( 'Failed to delete file.', 'wp-security-pack' ) ); ?>');
|
|
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Delete', 'wp-security-pack' ) ); ?>');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
<?php endif; ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Get security checks for status tab.
|
|
*
|
|
* @return array
|
|
*/
|
|
private function get_security_checks() {
|
|
$checks = array();
|
|
|
|
// Login Protection.
|
|
$checks['login_limit'] = array(
|
|
'name' => __( 'Login Attempt Limiting', 'wp-security-pack' ),
|
|
'description' => __( 'Limits failed login attempts to prevent brute force attacks', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'login_limit_enabled', true ),
|
|
'tab' => 'login',
|
|
);
|
|
|
|
$checks['honeypot'] = array(
|
|
'name' => __( 'Login Honeypot', 'wp-security-pack' ),
|
|
'description' => __( 'Hidden field that traps automated bots', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'honeypot_enabled', true ),
|
|
'tab' => 'login',
|
|
);
|
|
|
|
$checks['hide_login_errors'] = array(
|
|
'name' => __( 'Login Errors Hidden', 'wp-security-pack' ),
|
|
'description' => __( 'Generic error message prevents username enumeration', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'hide_login_errors', true ),
|
|
'tab' => 'login',
|
|
);
|
|
|
|
// Hardening.
|
|
$checks['xmlrpc'] = array(
|
|
'name' => __( 'XML-RPC Disabled', 'wp-security-pack' ),
|
|
'description' => __( 'Blocks XML-RPC endpoint commonly used in attacks', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'disable_xmlrpc', true ),
|
|
'tab' => 'hardening',
|
|
);
|
|
|
|
$checks['file_editing'] = array(
|
|
'name' => __( 'File Editor Disabled', 'wp-security-pack' ),
|
|
'description' => __( 'Prevents editing theme/plugin files from dashboard', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'disable_file_editing', true ),
|
|
'tab' => 'hardening',
|
|
);
|
|
|
|
$checks['rest_api'] = array(
|
|
'name' => __( 'REST API Restricted', 'wp-security-pack' ),
|
|
'description' => __( 'Requires authentication for REST API access', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'restrict_rest_api', true ),
|
|
'tab' => 'hardening',
|
|
);
|
|
|
|
$checks['user_enum'] = array(
|
|
'name' => __( 'User Enumeration Blocked', 'wp-security-pack' ),
|
|
'description' => __( 'Prevents attackers from discovering usernames', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'disable_user_enumeration', true ),
|
|
'tab' => 'hardening',
|
|
);
|
|
|
|
$checks['pingbacks'] = array(
|
|
'name' => __( 'Pingbacks Disabled', 'wp-security-pack' ),
|
|
'description' => __( 'Prevents pingback-based DDoS amplification', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'disable_pingbacks', true ),
|
|
'tab' => 'hardening',
|
|
);
|
|
|
|
$checks['wp_version'] = array(
|
|
'name' => __( 'WP Version Hidden', 'wp-security-pack' ),
|
|
'description' => __( 'Hides WordPress version from public view', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'remove_wp_version', true ),
|
|
'tab' => 'hardening',
|
|
);
|
|
|
|
$checks['security_headers'] = array(
|
|
'name' => __( 'Security Headers', 'wp-security-pack' ),
|
|
'description' => __( 'HTTP headers that enable browser security features', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'add_security_headers', true ),
|
|
'tab' => 'headers',
|
|
);
|
|
|
|
// 2FA - only green if enforced for admins (just "enabled" means users CAN set it up, not that they have).
|
|
$two_fa_enabled = WP_Security_Pack::get_setting( 'two_factor_enabled', false );
|
|
$two_fa_enforced = WP_Security_Pack::get_setting( 'two_factor_enforce_admin', false );
|
|
$checks['two_factor'] = array(
|
|
'name' => __( 'Two-Factor Authentication', 'wp-security-pack' ),
|
|
'description' => $two_fa_enabled && ! $two_fa_enforced
|
|
? __( 'Available but not enforced for admins', 'wp-security-pack' )
|
|
: __( 'Enforced for administrator accounts', 'wp-security-pack' ),
|
|
'status' => $two_fa_enabled && $two_fa_enforced,
|
|
'tab' => '2fa',
|
|
);
|
|
|
|
// Monitoring.
|
|
$checks['file_integrity'] = array(
|
|
'name' => __( 'File Integrity Monitoring', 'wp-security-pack' ),
|
|
'description' => __( 'Detects unauthorized changes to WordPress core', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'file_integrity_enabled', true ),
|
|
'tab' => 'scanner',
|
|
);
|
|
|
|
$checks['malware_scan'] = array(
|
|
'name' => __( 'Malware Scanning', 'wp-security-pack' ),
|
|
'description' => __( 'Scans files for malicious code patterns', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'malware_scan_enabled', true ),
|
|
'tab' => 'scanner',
|
|
);
|
|
|
|
// Optional features (not counted negatively if disabled).
|
|
$checks['custom_login'] = array(
|
|
'name' => __( 'Custom Login URL', 'wp-security-pack' ),
|
|
'description' => __( 'Hides wp-login.php from automated scanners', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'login_rename_enabled', false ),
|
|
'tab' => 'login',
|
|
);
|
|
|
|
$checks['geo_blocking'] = array(
|
|
'name' => __( 'Country Blocking', 'wp-security-pack' ),
|
|
'description' => __( 'Blocks access from specific countries', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'geo_blocking_enabled', false ),
|
|
'tab' => 'ip',
|
|
);
|
|
|
|
$checks['auto_blacklist'] = array(
|
|
'name' => __( 'Auto-Blacklist Repeat Offenders', 'wp-security-pack' ),
|
|
'description' => __( 'Permanently blocks IPs with repeated lockouts', 'wp-security-pack' ),
|
|
'status' => WP_Security_Pack::get_setting( 'auto_blacklist_enabled', false ),
|
|
'tab' => 'ip',
|
|
);
|
|
|
|
// Environment checks (no tab - these are recommendations).
|
|
global $wpdb;
|
|
$checks['ssl'] = array(
|
|
'name' => __( 'SSL/HTTPS', 'wp-security-pack' ),
|
|
'description' => is_ssl()
|
|
? __( 'Site is served over HTTPS', 'wp-security-pack' )
|
|
: __( 'Site should use HTTPS for security', 'wp-security-pack' ),
|
|
'status' => is_ssl(),
|
|
'tab' => '',
|
|
);
|
|
|
|
$checks['debug_mode'] = array(
|
|
'name' => __( 'Debug Mode Disabled', 'wp-security-pack' ),
|
|
'description' => defined( 'WP_DEBUG' ) && WP_DEBUG
|
|
? __( 'WP_DEBUG is enabled - disable in production', 'wp-security-pack' )
|
|
: __( 'Debug mode is properly disabled', 'wp-security-pack' ),
|
|
'status' => ! ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
|
|
'tab' => '',
|
|
);
|
|
|
|
$checks['db_prefix'] = array(
|
|
'name' => __( 'Database Prefix Changed', 'wp-security-pack' ),
|
|
'description' => 'wp_' === $wpdb->prefix
|
|
? __( 'Using default wp_ prefix - consider changing', 'wp-security-pack' )
|
|
: __( 'Using custom database prefix', 'wp-security-pack' ),
|
|
'status' => 'wp_' !== $wpdb->prefix,
|
|
'tab' => '',
|
|
);
|
|
|
|
$admin_user = get_user_by( 'login', 'admin' );
|
|
$checks['admin_username'] = array(
|
|
'name' => __( 'No "admin" Username', 'wp-security-pack' ),
|
|
'description' => $admin_user
|
|
? __( 'Default "admin" username exists - consider renaming', 'wp-security-pack' )
|
|
: __( 'No user with "admin" username', 'wp-security-pack' ),
|
|
'status' => ! $admin_user,
|
|
'tab' => '',
|
|
);
|
|
|
|
$checks['php_version'] = array(
|
|
'name' => __( 'PHP Version', 'wp-security-pack' ),
|
|
'description' => version_compare( PHP_VERSION, '8.0', '<' )
|
|
? sprintf(
|
|
/* translators: %s: PHP version */
|
|
__( 'PHP %s is outdated - update recommended', 'wp-security-pack' ),
|
|
PHP_VERSION
|
|
)
|
|
: sprintf(
|
|
/* translators: %s: PHP version */
|
|
__( 'Running PHP %s', 'wp-security-pack' ),
|
|
PHP_VERSION
|
|
),
|
|
'status' => version_compare( PHP_VERSION, '8.0', '>=' ),
|
|
'tab' => '',
|
|
);
|
|
|
|
// Check if auto-updates are enabled for core.
|
|
$auto_updates_enabled = defined( 'WP_AUTO_UPDATE_CORE' ) && WP_AUTO_UPDATE_CORE;
|
|
// Also check the database option.
|
|
if ( ! $auto_updates_enabled ) {
|
|
$auto_updates_enabled = 'true' === get_site_option( 'auto_update_core_major' ) ||
|
|
'true' === get_site_option( 'auto_update_core_minor' );
|
|
}
|
|
$checks['auto_updates'] = array(
|
|
'name' => __( 'Auto-Updates Enabled', 'wp-security-pack' ),
|
|
'description' => $auto_updates_enabled
|
|
? __( 'WordPress core auto-updates are enabled', 'wp-security-pack' )
|
|
: __( 'Enable auto-updates for security patches', 'wp-security-pack' ),
|
|
'status' => $auto_updates_enabled,
|
|
'tab' => '',
|
|
);
|
|
|
|
return $checks;
|
|
}
|
|
|
|
/**
|
|
* Render login protection tab.
|
|
*/
|
|
private function render_login_tab() {
|
|
?>
|
|
<input type="hidden" name="wpsp_settings[wpsp_current_tab]" value="login" />
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Login Access Restriction', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Restrict access to the WordPress login page by country and/or IP address.', 'wp-security-pack' ); ?><br>
|
|
<strong><?php esc_html_e( 'Note:', 'wp-security-pack' ); ?></strong>
|
|
<?php esc_html_e( 'This is a restrictive list — ONLY IPs/countries listed here can access the login page. This is different from the global IP Whitelist (IP Control tab), which bypasses all security checks including this restriction.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<?php
|
|
$geo_blocking = wpsp()->get_component( 'geo_blocking' );
|
|
$db_exists = $geo_blocking && $geo_blocking->database_exists();
|
|
if ( ! $db_exists ) :
|
|
?>
|
|
<div class="notice notice-warning inline" style="margin: 10px 0;">
|
|
<p>
|
|
<strong><?php esc_html_e( 'GeoIP Database Required', 'wp-security-pack' ); ?></strong><br>
|
|
<?php
|
|
printf(
|
|
/* translators: %s: Link to IP Control tab */
|
|
esc_html__( 'Country detection requires the GeoIP database. Please download it from the %s tab.', 'wp-security-pack' ),
|
|
'<a href="' . esc_url( admin_url( 'admin.php?page=wp-security-pack&tab=ip' ) ) . '"><strong>' . esc_html__( 'IP Control', 'wp-security-pack' ) . '</strong></a>'
|
|
);
|
|
?>
|
|
</p>
|
|
</div>
|
|
<?php else : ?>
|
|
<div class="notice notice-success inline" style="margin: 10px 0;">
|
|
<p><?php esc_html_e( 'GeoIP database is installed. Country detection is available.', 'wp-security-pack' ); ?></p>
|
|
</div>
|
|
<?php endif; ?>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[admin_access_restriction]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'admin_access_restriction', false ) ); ?> />
|
|
<?php esc_html_e( 'Restrict login page access by country/IP', 'wp-security-pack' ); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Allowed Countries', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php
|
|
$countries_with_flags = WPSP_Helper::get_countries_with_flags();
|
|
$admin_countries = WP_Security_Pack::get_setting( 'admin_allowed_countries', array() );
|
|
?>
|
|
<select name="wpsp_settings[admin_allowed_countries][]" multiple="multiple" size="8" class="wpsp-country-select" style="min-width: 300px;">
|
|
<?php foreach ( $countries_with_flags as $code => $name ) : ?>
|
|
<option value="<?php echo esc_attr( $code ); ?>"
|
|
<?php selected( in_array( $code, $admin_countries, true ) ); ?>>
|
|
<?php echo esc_html( $name ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<p class="description"><?php esc_html_e( 'Hold Ctrl/Cmd to select multiple. Leave empty to allow all countries (IP restriction only).', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Allowed IPs', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<textarea name="wpsp_settings[admin_allowed_ips]" rows="4" class="large-text code"><?php
|
|
echo esc_textarea( WP_Security_Pack::get_setting( 'admin_allowed_ips', '' ) );
|
|
?></textarea>
|
|
<p class="description">
|
|
<?php esc_html_e( 'One IP or CIDR range per line. Leave empty to use country restriction only.', 'wp-security-pack' ); ?><br>
|
|
<?php esc_html_e( 'IPs in the global whitelist (IP Control tab) already bypass this restriction — no need to add them here.', 'wp-security-pack' ); ?><br>
|
|
<?php
|
|
/* translators: %s: Current IP address */
|
|
printf( esc_html__( 'Your current IP: %s', 'wp-security-pack' ), '<code>' . esc_html( WPSP_Helper::get_client_ip() ) . '</code>' );
|
|
?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Email Alerts', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Receive email notifications for: failed login attempts, IP lockouts, file integrity changes, and malware scan findings.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[email_alerts_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'email_alerts_enabled', false ) ); ?> />
|
|
<?php esc_html_e( 'Send email alerts for security events', 'wp-security-pack' ); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Email Address', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="email" name="wpsp_settings[email_alerts_address]" id="wpsp-alert-email"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ) ); ?>" class="regular-text" />
|
|
<button type="button" class="button" id="wpsp-test-email">
|
|
<?php esc_html_e( 'Send Test Email', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<span id="wpsp-test-email-result" style="margin-left: 10px;"></span>
|
|
<p class="description"><?php esc_html_e( 'Leave empty to use the site admin email.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Alert Threshold', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="number" name="wpsp_settings[email_alert_threshold]" min="1" max="100"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'email_alert_threshold', 3 ) ); ?>" class="small-text" />
|
|
<p class="description"><?php esc_html_e( 'Send alert after this many failed login attempts from the same IP.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Login Attempt Limiting', 'wp-security-pack' ); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[login_limit_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'login_limit_enabled', true ) ); ?> />
|
|
<?php esc_html_e( 'Limit failed login attempts', 'wp-security-pack' ); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Max Attempts', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="number" name="wpsp_settings[login_max_attempts]" min="1" max="100"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'login_max_attempts', 5 ) ); ?>" class="small-text" />
|
|
<p class="description"><?php esc_html_e( 'Number of failed attempts before lockout.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Lockout Duration', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="number" name="wpsp_settings[login_lockout_duration]" min="1" max="1440"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'login_lockout_duration', 15 ) ); ?>" class="small-text" />
|
|
<?php esc_html_e( 'minutes', 'wp-security-pack' ); ?>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Custom Login URL', 'wp-security-pack' ); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[login_rename_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'login_rename_enabled', false ) ); ?> />
|
|
<?php esc_html_e( 'Use custom login URL', 'wp-security-pack' ); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Custom URL', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<code><?php echo esc_url( home_url( '/' ) ); ?></code>
|
|
<input type="text" name="wpsp_settings[login_custom_url]"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'login_custom_url', '' ) ); ?>" class="regular-text" />
|
|
<p class="description"><?php esc_html_e( 'Letters, numbers, and hyphens only. Example: my-secret-login', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Hide wp-admin', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[hide_wp_admin]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'hide_wp_admin', false ) ); ?> />
|
|
<?php esc_html_e( 'Return 404 for /wp-admin when not logged in', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Direct access to /wp-login.php is also blocked with a 404. Users must visit the custom login URL first, which sets an access cookie allowing WordPress flows (password reset, etc.) to work.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Honeypot Protection', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Adds a hidden field to login and registration forms that is invisible to humans but gets filled by automated bots. When a bot fills this field, it is immediately blocked.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[honeypot_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'honeypot_enabled', true ) ); ?> />
|
|
<?php esc_html_e( 'Enable honeypot bot detection', 'wp-security-pack' ); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Ban Duration', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="number" name="wpsp_settings[honeypot_ban_duration]" min="1" max="1440"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'honeypot_ban_duration', 60 ) ); ?>" class="small-text" />
|
|
<?php esc_html_e( 'minutes', 'wp-security-pack' ); ?>
|
|
<p class="description"><?php esc_html_e( 'How long to block IPs that trigger the honeypot.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Additional Login Security', 'wp-security-pack' ); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Hide Login Errors', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[hide_login_errors]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'hide_login_errors', true ) ); ?> />
|
|
<?php esc_html_e( 'Show generic error message on failed login', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Prevents attackers from discovering valid usernames by showing "Invalid username or password" instead of specific errors.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Admin Login Alerts', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[admin_login_notify]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'admin_login_notify', false ) ); ?> />
|
|
<?php esc_html_e( 'Email notification when admin logs in from new IP', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Sends an email alert when an administrator account logs in from an IP address not seen before.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
$('#wpsp-test-email').on('click', function() {
|
|
var $btn = $(this);
|
|
var $result = $('#wpsp-test-email-result');
|
|
var email = $('#wpsp-alert-email').val() || '<?php echo esc_js( get_option( 'admin_email' ) ); ?>';
|
|
|
|
$btn.prop('disabled', true);
|
|
$result.html('<span style="color: #666;"><?php echo esc_js( __( 'Sending...', 'wp-security-pack' ) ); ?></span>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_test_email',
|
|
email: email,
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
$btn.prop('disabled', false);
|
|
if (response.success) {
|
|
$result.html('<span style="color: #46b450;"><?php echo esc_js( __( 'Test email sent!', 'wp-security-pack' ) ); ?></span>');
|
|
} else {
|
|
$result.html('<span style="color: #dc3232;">' + response.data.message + '</span>');
|
|
}
|
|
}).fail(function() {
|
|
$btn.prop('disabled', false);
|
|
$result.html('<span style="color: #dc3232;"><?php echo esc_js( __( 'Request failed.', 'wp-security-pack' ) ); ?></span>');
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render IP control tab.
|
|
*/
|
|
private function render_ip_tab() {
|
|
$blocked_ips = array();
|
|
$ip_control = wpsp()->get_component( 'ip_control' );
|
|
if ( $ip_control ) {
|
|
$blocked_ips = $ip_control->get_blocked_ips();
|
|
}
|
|
|
|
$geo_blocking = wpsp()->get_component( 'geo_blocking' );
|
|
$db_info = $geo_blocking ? $geo_blocking->get_database_info() : array( 'exists' => false );
|
|
$countries = WPSP_Helper::get_countries_with_flags();
|
|
?>
|
|
<input type="hidden" name="wpsp_settings[wpsp_current_tab]" value="ip" />
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Country Blocking', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description">
|
|
<strong><?php esc_html_e( 'Warning:', 'wp-security-pack' ); ?></strong>
|
|
<?php esc_html_e( 'Blocked countries cannot access the ENTIRE website — not just login. Use this only if you want to completely block visitors from specific countries.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[geo_blocking_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'geo_blocking_enabled', false ) ); ?> />
|
|
<?php esc_html_e( 'Enable country-based blocking', 'wp-security-pack' ); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'GeoIP Database', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php if ( $db_info['exists'] ) : ?>
|
|
<span class="wpsp-status wpsp-status-ok">
|
|
<?php esc_html_e( 'Installed', 'wp-security-pack' ); ?>
|
|
</span>
|
|
<span class="description">
|
|
<?php echo esc_html( $db_info['type'] ); ?> -
|
|
<?php echo esc_html( size_format( $db_info['size'] ) ); ?>
|
|
<?php if ( ! empty( $db_info['modified'] ) ) : ?>
|
|
- <?php printf( esc_html__( 'Updated: %s', 'wp-security-pack' ), esc_html( date_i18n( get_option( 'date_format' ), $db_info['modified'] ) ) ); ?>
|
|
<?php endif; ?>
|
|
</span>
|
|
<br><br>
|
|
<button type="button" class="button" id="wpsp-download-geo-db">
|
|
<?php esc_html_e( 'Re-download Database', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Download the latest version of the IP2Location LITE database.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<?php else : ?>
|
|
<span class="wpsp-status wpsp-status-warning">
|
|
<?php esc_html_e( 'Not installed', 'wp-security-pack' ); ?>
|
|
</span>
|
|
<button type="button" class="button" id="wpsp-download-geo-db">
|
|
<?php esc_html_e( 'Download IP2Location Lite DB', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Free IP2Location LITE database. No account required.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Blocked Countries', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<select name="wpsp_settings[geo_blocked_countries][]" multiple="multiple" size="10" class="wpsp-country-select">
|
|
<?php
|
|
$blocked = WP_Security_Pack::get_setting( 'geo_blocked_countries', array() );
|
|
foreach ( $countries as $code => $name ) :
|
|
?>
|
|
<option value="<?php echo esc_attr( $code ); ?>"
|
|
<?php selected( in_array( $code, $blocked, true ) ); ?>>
|
|
<?php echo esc_html( $name ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Hold Ctrl/Cmd to select multiple. Visitors from these countries will be blocked from the entire site.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'IP Whitelist (Global Bypass)', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description">
|
|
<?php esc_html_e( 'IPs listed here are treated as trusted and bypass ALL security checks: login limits, lockouts, geo-blocking, and login access restrictions.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Whitelisted IPs', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<textarea name="wpsp_settings[ip_whitelist]" rows="6" class="large-text code"><?php
|
|
echo esc_textarea( WP_Security_Pack::get_setting( 'ip_whitelist', '' ) );
|
|
?></textarea>
|
|
<p class="description">
|
|
<?php esc_html_e( 'One IP or CIDR range per line.', 'wp-security-pack' ); ?><br>
|
|
<?php
|
|
/* translators: %s: Current IP address */
|
|
printf( esc_html__( 'Your current IP: %s', 'wp-security-pack' ), '<code>' . esc_html( WPSP_Helper::get_client_ip() ) . '</code>' );
|
|
?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'IP Blacklist', 'wp-security-pack' ); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Blacklisted IPs', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<textarea name="wpsp_settings[ip_blacklist]" rows="6" class="large-text code"><?php
|
|
echo esc_textarea( WP_Security_Pack::get_setting( 'ip_blacklist', '' ) );
|
|
?></textarea>
|
|
<p class="description"><?php esc_html_e( 'One IP or CIDR range per line. Lines starting with # are comments.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Auto-Blacklist', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[auto_blacklist_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'auto_blacklist_enabled', false ) ); ?> />
|
|
<?php esc_html_e( 'Automatically blacklist repeat offenders', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Permanently add IPs to the blacklist after repeated lockouts.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Auto-Blacklist Threshold', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="number" name="wpsp_settings[auto_blacklist_threshold]" min="1" max="20"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'auto_blacklist_threshold', 3 ) ); ?>" class="small-text" />
|
|
<?php esc_html_e( 'lockouts', 'wp-security-pack' ); ?>
|
|
<p class="description"><?php esc_html_e( 'Number of times an IP must be locked out before being permanently blacklisted.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<?php if ( ! empty( $blocked_ips ) ) : ?>
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Currently Locked Out IPs', 'wp-security-pack' ); ?></h2>
|
|
<table class="widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'IP Address', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Failed Attempts', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Locked Until', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Actions', 'wp-security-pack' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ( $blocked_ips as $lockout ) : ?>
|
|
<tr>
|
|
<td><code><?php echo esc_html( $lockout->ip_address ); ?></code></td>
|
|
<td><?php echo esc_html( $lockout->failed_attempts ); ?></td>
|
|
<td><?php echo esc_html( $lockout->lockout_until ); ?></td>
|
|
<td>
|
|
<button type="button" class="button button-small wpsp-unblock-ip"
|
|
data-ip="<?php echo esc_attr( $lockout->ip_address ); ?>">
|
|
<?php esc_html_e( 'Unblock', 'wp-security-pack' ); ?>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
$('.wpsp-unblock-ip').on('click', function() {
|
|
var ip = $(this).data('ip');
|
|
var $row = $(this).closest('tr');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_unblock_ip',
|
|
ip: ip,
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$row.fadeOut();
|
|
}
|
|
});
|
|
});
|
|
|
|
$('#wpsp-download-geo-db').on('click', function() {
|
|
var $btn = $(this);
|
|
$btn.prop('disabled', true).text('<?php echo esc_js( __( 'Downloading...', 'wp-security-pack' ) ); ?>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_download_geo_db',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
location.reload();
|
|
} else {
|
|
alert(response.data.message);
|
|
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Download IP2Location Lite DB', 'wp-security-pack' ) ); ?>');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render hardening tab.
|
|
*/
|
|
private function render_hardening_tab() {
|
|
?>
|
|
<input type="hidden" name="wpsp_settings[wpsp_current_tab]" value="hardening" />
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Security Hardening', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'These options reduce your attack surface by disabling unnecessary WordPress features often exploited by attackers.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Disable XML-RPC', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[disable_xmlrpc]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'disable_xmlrpc', true ) ); ?> />
|
|
<?php esc_html_e( 'Disable XML-RPC endpoint', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'XML-RPC allows remote publishing but is commonly exploited for brute force and DDoS attacks. Disable unless needed for Jetpack, WordPress mobile app, or remote publishing.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Disable File Editing', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[disable_file_editing]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'disable_file_editing', true ) ); ?> />
|
|
<?php esc_html_e( 'Disable plugin and theme file editor', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Removes the ability to edit PHP files from Appearance > Theme Editor and Plugins > Plugin Editor. If an attacker gains admin access, they cannot inject malicious code directly.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Disable Application Passwords', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[disable_application_passwords]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'disable_application_passwords', false ) ); ?> />
|
|
<?php esc_html_e( 'Disable WordPress application passwords', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Application passwords allow external apps to authenticate via REST API. Disable if you don\'t use third-party apps that connect to your site.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Restrict REST API', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[restrict_rest_api]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'restrict_rest_api', true ) ); ?> />
|
|
<?php esc_html_e( 'Require authentication for REST API access', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Blocks anonymous access to REST API endpoints. Prevents user enumeration (/wp-json/wp/v2/users) and other information disclosure. Essential endpoints like oEmbed remain accessible.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Remove WP Version', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[remove_wp_version]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'remove_wp_version', true ) ); ?> />
|
|
<?php esc_html_e( 'Hide WordPress version information', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Removes the WordPress version from the HTML source, RSS feeds, and script/style URLs. Makes it harder for attackers to identify known vulnerabilities for your version.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Remove Feed Links', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[remove_feed_links]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'remove_feed_links', false ) ); ?> />
|
|
<?php esc_html_e( 'Remove RSS feed links from HTML head', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Removes auto-discovery links for RSS/Atom feeds. Only enable if you don\'t use RSS feeds.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Security Headers', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[add_security_headers]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'add_security_headers', true ) ); ?> />
|
|
<?php esc_html_e( 'Add HTTP security headers', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Adds headers like X-Frame-Options, X-Content-Type-Options, etc. Configure specific headers in the Security Headers tab.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Block User Enumeration', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[disable_user_enumeration]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'disable_user_enumeration', true ) ); ?> />
|
|
<?php esc_html_e( 'Prevent username discovery', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Blocks ?author=N scans, hides usernames from REST API and oEmbed responses. Attackers commonly enumerate usernames before brute force attacks.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Disable Pingbacks', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[disable_pingbacks]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'disable_pingbacks', true ) ); ?> />
|
|
<?php esc_html_e( 'Disable pingbacks and trackbacks', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Pingbacks are rarely used legitimately but commonly abused for DDoS amplification attacks and spam.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Always Active', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'The following security measures are automatically applied:', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<ul style="list-style: disc; margin-left: 20px; color: #666;">
|
|
<li><?php esc_html_e( 'RSD (Really Simple Discovery) link removed from HTML head', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'Windows Live Writer manifest link removed', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'Shortlink removed from HTML head', 'wp-security-pack' ); ?></li>
|
|
</ul>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render security headers tab.
|
|
*/
|
|
private function render_headers_tab() {
|
|
$hardening = new WPSP_Hardening();
|
|
$default_headers = $hardening->get_default_headers();
|
|
$headers = WP_Security_Pack::get_setting( 'security_headers', $default_headers );
|
|
?>
|
|
<input type="hidden" name="wpsp_settings[wpsp_current_tab]" value="headers" />
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Security Headers Configuration', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'HTTP security headers instruct browsers to enable security features. Leave a field empty to disable that header.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<div class="notice notice-info inline" style="margin: 10px 0;">
|
|
<p>
|
|
<?php
|
|
printf(
|
|
/* translators: %s: Link to securityheaders.com */
|
|
esc_html__( 'Test your headers at %s after saving.', 'wp-security-pack' ),
|
|
'<a href="https://securityheaders.com/" target="_blank" rel="noopener">securityheaders.com</a>'
|
|
);
|
|
?>
|
|
<button type="button" class="button button-small" id="wpsp-reset-headers" style="margin-left: 15px;">
|
|
<?php esc_html_e( 'Reset to Defaults', 'wp-security-pack' ); ?>
|
|
</button>
|
|
</p>
|
|
</div>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'X-Content-Type-Options', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="text" name="wpsp_settings[security_headers][x_content_type_options]" id="header-x_content_type_options"
|
|
value="<?php echo esc_attr( $headers['x_content_type_options'] ?? 'nosniff' ); ?>" class="regular-text"
|
|
data-default="nosniff" />
|
|
<p class="description"><?php esc_html_e( 'Prevents browsers from MIME-sniffing a response away from the declared content-type. Protects against attacks where an attacker tries to disguise malicious content as a safe file type.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'X-Frame-Options', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<select name="wpsp_settings[security_headers][x_frame_options]" id="header-x_frame_options" data-default="SAMEORIGIN">
|
|
<option value="" <?php selected( empty( $headers['x_frame_options'] ) ); ?>><?php esc_html_e( 'Disabled', 'wp-security-pack' ); ?></option>
|
|
<option value="DENY" <?php selected( $headers['x_frame_options'] ?? '', 'DENY' ); ?>>DENY</option>
|
|
<option value="SAMEORIGIN" <?php selected( $headers['x_frame_options'] ?? '', 'SAMEORIGIN' ); ?>>SAMEORIGIN</option>
|
|
</select>
|
|
<p class="description"><?php esc_html_e( 'Protects against clickjacking attacks by controlling whether your site can be embedded in iframes. DENY blocks all framing, SAMEORIGIN allows framing only from your own domain.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'X-XSS-Protection', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="text" name="wpsp_settings[security_headers][x_xss_protection]" id="header-x_xss_protection"
|
|
value="<?php echo esc_attr( $headers['x_xss_protection'] ?? '1; mode=block' ); ?>" class="regular-text"
|
|
data-default="1; mode=block" />
|
|
<p class="description"><?php esc_html_e( 'Enables the browser\'s built-in XSS filter. Only needed for legacy browsers (Internet Explorer). Modern browsers like Chrome and Firefox have removed this feature in favor of Content-Security-Policy.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Referrer-Policy', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<select name="wpsp_settings[security_headers][referrer_policy]" id="header-referrer_policy" data-default="strict-origin-when-cross-origin">
|
|
<option value="" <?php selected( empty( $headers['referrer_policy'] ) ); ?>><?php esc_html_e( 'Disabled', 'wp-security-pack' ); ?></option>
|
|
<option value="no-referrer" <?php selected( $headers['referrer_policy'] ?? '', 'no-referrer' ); ?>>no-referrer</option>
|
|
<option value="no-referrer-when-downgrade" <?php selected( $headers['referrer_policy'] ?? '', 'no-referrer-when-downgrade' ); ?>>no-referrer-when-downgrade</option>
|
|
<option value="origin" <?php selected( $headers['referrer_policy'] ?? '', 'origin' ); ?>>origin</option>
|
|
<option value="origin-when-cross-origin" <?php selected( $headers['referrer_policy'] ?? '', 'origin-when-cross-origin' ); ?>>origin-when-cross-origin</option>
|
|
<option value="same-origin" <?php selected( $headers['referrer_policy'] ?? '', 'same-origin' ); ?>>same-origin</option>
|
|
<option value="strict-origin" <?php selected( $headers['referrer_policy'] ?? '', 'strict-origin' ); ?>>strict-origin</option>
|
|
<option value="strict-origin-when-cross-origin" <?php selected( $headers['referrer_policy'] ?? '', 'strict-origin-when-cross-origin' ); ?>>strict-origin-when-cross-origin</option>
|
|
</select>
|
|
<p class="description"><?php esc_html_e( 'Controls how much referrer information is sent when navigating to other sites. Protects user privacy and prevents leaking sensitive URL parameters.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Permissions-Policy', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="text" name="wpsp_settings[security_headers][permissions_policy]" id="header-permissions_policy"
|
|
value="<?php echo esc_attr( $headers['permissions_policy'] ?? 'geolocation=(), microphone=(), camera=()' ); ?>" class="large-text"
|
|
data-default="geolocation=(), microphone=(), camera=()" />
|
|
<p class="description"><?php esc_html_e( 'Controls which browser features and APIs can be used. Empty parentheses () disable the feature entirely. Prevents malicious scripts from accessing sensitive device features.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Content-Security-Policy', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<textarea name="wpsp_settings[security_headers][content_security_policy]" id="header-content_security_policy" rows="4" class="large-text code" data-default=""><?php
|
|
echo esc_textarea( $headers['content_security_policy'] ?? '' );
|
|
?></textarea>
|
|
<p style="margin-top: 8px;">
|
|
<button type="button" class="button button-small" id="wpsp-load-example-csp">
|
|
<?php esc_html_e( 'Load Example CSP', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<span class="description" style="margin-left: 10px;"><?php esc_html_e( 'Loads a basic WordPress-compatible CSP as a starting point.', 'wp-security-pack' ); ?></span>
|
|
</p>
|
|
<p class="description">
|
|
<?php esc_html_e( 'An effective measure to protect your site from XSS attacks. By whitelisting sources of approved content, you can prevent the browser from loading malicious assets.', 'wp-security-pack' ); ?>
|
|
<br><strong><?php esc_html_e( 'Warning:', 'wp-security-pack' ); ?></strong> <?php esc_html_e( 'Incorrect CSP can break your site. Test thoroughly and check browser console for violations. Leave empty to disable.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Strict-Transport-Security', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="text" name="wpsp_settings[security_headers][strict_transport_security]" id="header-strict_transport_security"
|
|
value="<?php echo esc_attr( $headers['strict_transport_security'] ?? 'max-age=31536000; includeSubDomains' ); ?>" class="large-text"
|
|
data-default="max-age=31536000; includeSubDomains" />
|
|
<p class="description">
|
|
<?php esc_html_e( 'HSTS tells browsers to only connect via HTTPS, preventing protocol downgrade attacks and cookie hijacking. Only sent over HTTPS connections.', 'wp-security-pack' ); ?>
|
|
<br><strong><?php esc_html_e( 'Note:', 'wp-security-pack' ); ?></strong> <?php esc_html_e( 'max-age is in seconds. 31536000 = 1 year. Use includeSubDomains to protect all subdomains.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
$('#wpsp-reset-headers').on('click', function() {
|
|
if (confirm('<?php echo esc_js( __( 'Reset all security headers to their default values?', 'wp-security-pack' ) ); ?>')) {
|
|
$('[id^="header-"]').each(function() {
|
|
var defaultVal = $(this).data('default');
|
|
if ($(this).is('select')) {
|
|
$(this).val(defaultVal);
|
|
} else if ($(this).is('textarea')) {
|
|
$(this).val(defaultVal);
|
|
} else {
|
|
$(this).val(defaultVal);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$('#wpsp-load-example-csp').on('click', function() {
|
|
var exampleCSP = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';";
|
|
$('#header-content_security_policy').val(exampleCSP);
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render 2FA tab.
|
|
*/
|
|
private function render_2fa_tab() {
|
|
// Count users with 2FA enabled.
|
|
$users_with_2fa = get_users( array(
|
|
'meta_key' => '_wpsp_2fa_enabled',
|
|
'meta_value' => '1',
|
|
'fields' => 'ID',
|
|
) );
|
|
$total_admins = count( get_users( array(
|
|
'role' => 'administrator',
|
|
'fields' => 'ID',
|
|
) ) );
|
|
$admins_with_2fa = 0;
|
|
foreach ( $users_with_2fa as $user_id ) {
|
|
if ( user_can( $user_id, 'manage_options' ) ) {
|
|
$admins_with_2fa++;
|
|
}
|
|
}
|
|
?>
|
|
<input type="hidden" name="wpsp_settings[wpsp_current_tab]" value="2fa" />
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Two-Factor Authentication', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Two-factor authentication adds an extra layer of security by requiring a one-time code from your phone in addition to your password. Even if an attacker steals your password, they cannot log in without access to your authenticator app.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
|
|
<?php if ( count( $users_with_2fa ) > 0 || $total_admins > 0 ) : ?>
|
|
<div class="wpsp-stats" style="margin: 15px 0;">
|
|
<div class="wpsp-stat">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( count( $users_with_2fa ) ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Users with 2FA', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
<div class="wpsp-stat">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $admins_with_2fa . '/' . $total_admins ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Admins with 2FA', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable 2FA', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[two_factor_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'two_factor_enabled', false ) ); ?> />
|
|
<?php esc_html_e( 'Allow users to enable Two-Factor Authentication', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'When enabled, users can set up 2FA from their Profile page.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enforce for Admins', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[two_factor_enforce_admin]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'two_factor_enforce_admin', false ) ); ?> />
|
|
<?php esc_html_e( 'Require 2FA for administrator accounts', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Administrators will be redirected to set up 2FA before they can access the dashboard.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Compatible Authenticator Apps', 'wp-security-pack' ); ?></h2>
|
|
<p class="description"><?php esc_html_e( 'Any TOTP-compatible authenticator app will work. Popular options include:', 'wp-security-pack' ); ?></p>
|
|
<ul style="list-style: disc; margin-left: 20px;">
|
|
<li><strong>Google Authenticator</strong> - <?php esc_html_e( 'iOS & Android', 'wp-security-pack' ); ?></li>
|
|
<li><strong>Authy</strong> - <?php esc_html_e( 'iOS, Android, Desktop (with cloud backup)', 'wp-security-pack' ); ?></li>
|
|
<li><strong>Microsoft Authenticator</strong> - <?php esc_html_e( 'iOS & Android', 'wp-security-pack' ); ?></li>
|
|
<li><strong>1Password</strong> - <?php esc_html_e( 'iOS, Android, Desktop', 'wp-security-pack' ); ?></li>
|
|
<li><strong>Bitwarden</strong> - <?php esc_html_e( 'All platforms (premium feature)', 'wp-security-pack' ); ?></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'How it works', 'wp-security-pack' ); ?></h2>
|
|
<ol>
|
|
<li><?php esc_html_e( 'Enable 2FA above and save settings.', 'wp-security-pack' ); ?></li>
|
|
<li>
|
|
<?php
|
|
printf(
|
|
/* translators: %s: Link to profile page */
|
|
esc_html__( 'Users go to their %s and click "Set Up 2FA".', 'wp-security-pack' ),
|
|
'<a href="' . esc_url( admin_url( 'profile.php' ) ) . '">' . esc_html__( 'Profile page', 'wp-security-pack' ) . '</a>'
|
|
);
|
|
?>
|
|
</li>
|
|
<li><?php esc_html_e( 'Scan the QR code with your authenticator app or enter the secret key manually.', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'Enter the 6-digit verification code to confirm setup.', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'On next login, enter your password as usual, then the 2FA code from your app.', 'wp-security-pack' ); ?></li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Backup Codes', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'When 2FA is set up, 10 backup codes are generated. These are one-time codes for emergency access if the authenticator app is unavailable.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<ul style="list-style: disc; margin-left: 20px; color: #666;">
|
|
<li><?php esc_html_e( 'Codes are shown ONCE at setup - users must save them immediately', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'Codes are stored securely hashed (not viewable later)', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'Users can regenerate codes from their Profile (invalidates old codes)', 'wp-security-pack' ); ?></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Privacy Note', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'QR codes are generated entirely in the browser using JavaScript - no data is sent to external servers. Your site URL and user email are included in the QR code to display properly in the authenticator app. Authentication secrets are generated locally and stored securely in your database.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render scanner tab.
|
|
*/
|
|
private function render_scanner_tab() {
|
|
$file_integrity = new WPSP_File_Integrity();
|
|
$file_results = $file_integrity->get_last_scan_results();
|
|
|
|
$malware_scanner = new WPSP_Malware_Scanner();
|
|
$malware_results = $malware_scanner->get_last_scan_results();
|
|
|
|
$file_changes_count = count( $file_results['changes']['modified'] ?? array() );
|
|
$malware_issues_count = count( $malware_results['results'] ?? array() );
|
|
?>
|
|
<input type="hidden" name="wpsp_settings[wpsp_current_tab]" value="scanner" />
|
|
|
|
<?php if ( $file_results['time'] > 0 || $malware_results['time'] > 0 ) : ?>
|
|
<div class="wpsp-stats" style="margin-bottom: 20px;">
|
|
<div class="wpsp-stat">
|
|
<span class="wpsp-stat-value" style="color: <?php echo $file_changes_count > 0 ? '#dc3232' : '#46b450'; ?>;">
|
|
<?php echo $file_changes_count > 0 ? esc_html( $file_changes_count ) : '✓'; ?>
|
|
</span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Core File Issues', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
<div class="wpsp-stat">
|
|
<span class="wpsp-stat-value" style="color: <?php echo $malware_issues_count > 0 ? '#dc3232' : '#46b450'; ?>;">
|
|
<?php echo $malware_issues_count > 0 ? esc_html( $malware_issues_count ) : '✓'; ?>
|
|
</span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Suspicious Files', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'File Integrity Monitoring', 'wp-security-pack' ); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Monitors WordPress core files (wp-admin, wp-includes, and root PHP files) by comparing them against official checksums from WordPress.org. Detects unauthorized modifications that could indicate a compromise.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[file_integrity_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'file_integrity_enabled', true ) ); ?> />
|
|
<?php esc_html_e( 'Monitor WordPress core files for changes', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Runs automatically once per day.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Last Scan', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php if ( $file_results['time'] > 0 ) : ?>
|
|
<?php echo esc_html( human_time_diff( $file_results['time'] ) . ' ' . __( 'ago', 'wp-security-pack' ) ); ?>
|
|
<?php else : ?>
|
|
<?php esc_html_e( 'Never', 'wp-security-pack' ); ?>
|
|
<?php endif; ?>
|
|
<button type="button" class="button" id="wpsp-run-file-scan">
|
|
<?php esc_html_e( 'Run Scan Now', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<?php if ( $file_results['time'] > 0 ) : ?>
|
|
<button type="button" class="button" id="wpsp-reset-baseline">
|
|
<?php esc_html_e( 'Reset Baseline', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<?php if ( ! empty( $file_results['changes']['modified'] ) ) : ?>
|
|
<h3><?php esc_html_e( 'Modified Core Files', 'wp-security-pack' ); ?></h3>
|
|
<div class="notice notice-warning inline" style="margin: 10px 0;">
|
|
<p>
|
|
<?php esc_html_e( 'These files differ from the official WordPress distribution. This can happen after:', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<ul style="list-style: disc; margin-left: 20px;">
|
|
<li><?php esc_html_e( 'A WordPress update (normal - re-run scan to update baseline)', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'Manual edits to core files (not recommended)', 'wp-security-pack' ); ?></li>
|
|
<li><?php esc_html_e( 'A security compromise (investigate immediately)', 'wp-security-pack' ); ?></li>
|
|
</ul>
|
|
</div>
|
|
<table class="widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'File', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Status', 'wp-security-pack' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ( $file_results['changes']['modified'] as $file ) : ?>
|
|
<tr>
|
|
<td><code><?php echo esc_html( is_array( $file ) ? $file['file'] : $file ); ?></code></td>
|
|
<td><span class="wpsp-status wpsp-status-warning"><?php esc_html_e( 'Modified', 'wp-security-pack' ); ?></span></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<p style="margin-top: 10px;">
|
|
<button type="button" class="button" id="wpsp-dismiss-file-changes">
|
|
<?php esc_html_e( 'Dismiss & Update Baseline', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<span class="description" style="margin-left: 10px;"><?php esc_html_e( 'Use this after verifying the changes are legitimate.', 'wp-security-pack' ); ?></span>
|
|
</p>
|
|
<?php elseif ( $file_results['time'] > 0 ) : ?>
|
|
<div class="notice notice-success inline" style="margin: 10px 0;">
|
|
<p><?php esc_html_e( 'All core files match official WordPress.org checksums.', 'wp-security-pack' ); ?></p>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php
|
|
?>
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Malware Scanner', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description">
|
|
<?php esc_html_e( 'Scans PHP files in plugins, themes, and uploads for malicious code patterns like backdoors, web shells, and obfuscated code.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[malware_scan_enabled]" value="1"
|
|
<?php checked( WP_Security_Pack::get_setting( 'malware_scan_enabled', true ) ); ?> />
|
|
<?php esc_html_e( 'Enable scheduled malware scanning', 'wp-security-pack' ); ?>
|
|
</label>
|
|
<p class="description"><?php esc_html_e( 'Runs automatically once per week. Manual scans can be run anytime.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Last Scan', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php
|
|
$scan_stats = get_option( 'wpsp_malware_scan_stats', array() );
|
|
if ( $malware_results['time'] > 0 ) :
|
|
?>
|
|
<?php echo esc_html( human_time_diff( $malware_results['time'] ) . ' ' . __( 'ago', 'wp-security-pack' ) ); ?>
|
|
<?php if ( ! empty( $scan_stats['files_scanned'] ) ) : ?>
|
|
<span class="description" style="margin-left: 10px;">
|
|
<?php
|
|
printf(
|
|
/* translators: 1: Number of files, 2: Duration in seconds */
|
|
esc_html__( '(%1$s files in %2$ss)', 'wp-security-pack' ),
|
|
esc_html( number_format_i18n( $scan_stats['files_scanned'] ) ),
|
|
esc_html( $scan_stats['duration'] )
|
|
);
|
|
?>
|
|
</span>
|
|
<?php endif; ?>
|
|
<?php else : ?>
|
|
<?php esc_html_e( 'Never', 'wp-security-pack' ); ?>
|
|
<?php endif; ?>
|
|
<button type="button" class="button" id="wpsp-run-malware-scan">
|
|
<?php esc_html_e( 'Run Scan Now', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<span id="wpsp-scan-status"></span>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<?php if ( ! empty( $malware_results['results'] ) ) : ?>
|
|
<h3><?php esc_html_e( 'Suspicious Files Found', 'wp-security-pack' ); ?></h3>
|
|
<div class="notice notice-warning inline" style="margin: 10px 0;">
|
|
<p>
|
|
<?php esc_html_e( 'These files contain suspicious code patterns. Review each finding - some may be false positives from obfuscated commercial plugins or legitimate security tools.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
</div>
|
|
<table class="widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'File', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Finding', 'wp-security-pack' ); ?></th>
|
|
<th style="width: 120px;"><?php esc_html_e( 'Actions', 'wp-security-pack' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ( $malware_results['results'] as $file => $findings ) : ?>
|
|
<tr data-file="<?php echo esc_attr( $file ); ?>">
|
|
<td><code><?php echo esc_html( str_replace( ABSPATH, '', $file ) ); ?></code></td>
|
|
<td>
|
|
<?php foreach ( $findings as $finding ) : ?>
|
|
<span class="wpsp-badge" style="background-color: <?php echo esc_attr( WPSP_Malware_Scanner::get_severity_color( $finding['severity'] ) ); ?>;">
|
|
<?php echo esc_html( strtoupper( $finding['severity'] ) ); ?>
|
|
</span>
|
|
<?php echo esc_html( $finding['name'] ); ?>
|
|
<span class="description" style="display: block; font-size: 12px; color: #666;">
|
|
<?php echo esc_html( $finding['description'] ); ?>
|
|
</span>
|
|
<?php endforeach; ?>
|
|
</td>
|
|
<td>
|
|
<button type="button" class="button button-small wpsp-quarantine-file" data-file="<?php echo esc_attr( $file ); ?>">
|
|
<?php esc_html_e( 'Quarantine', 'wp-security-pack' ); ?>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( ! empty( $malware_results['results'] ) ) : ?>
|
|
<p style="margin-top: 10px;">
|
|
<button type="button" class="button" id="wpsp-clear-malware-results">
|
|
<?php esc_html_e( 'Clear Results', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<span class="description" style="margin-left: 10px;"><?php esc_html_e( 'Clears the current findings. Next scan will re-check all files.', 'wp-security-pack' ); ?></span>
|
|
</p>
|
|
<?php elseif ( $malware_results['time'] > 0 ) : ?>
|
|
<div class="notice notice-success inline" style="margin: 10px 0;">
|
|
<p><?php esc_html_e( 'No malware or suspicious files detected.', 'wp-security-pack' ); ?></p>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php
|
|
$quarantined_files = $malware_scanner->get_quarantined_files();
|
|
if ( ! empty( $quarantined_files ) ) :
|
|
?>
|
|
<h3 style="margin-top: 20px;"><?php esc_html_e( 'Quarantined Files', 'wp-security-pack' ); ?></h3>
|
|
<p class="description"><?php esc_html_e( 'Files that have been isolated. They cannot execute from quarantine.', 'wp-security-pack' ); ?></p>
|
|
<table class="widefat striped" style="margin-top: 10px;">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Original Location', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Quarantined', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Size', 'wp-security-pack' ); ?></th>
|
|
<th style="width: 180px;"><?php esc_html_e( 'Actions', 'wp-security-pack' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ( $quarantined_files as $name => $meta ) : ?>
|
|
<tr data-quarantine-name="<?php echo esc_attr( $name ); ?>">
|
|
<td><code><?php echo esc_html( $meta['relative_path'] ?? $meta['original_name'] ); ?></code></td>
|
|
<td><?php echo esc_html( human_time_diff( $meta['quarantined_at'] ) . ' ' . __( 'ago', 'wp-security-pack' ) ); ?></td>
|
|
<td><?php echo esc_html( size_format( $meta['file_size'] ?? 0 ) ); ?></td>
|
|
<td>
|
|
<button type="button" class="button button-small wpsp-restore-file" data-name="<?php echo esc_attr( $name ); ?>">
|
|
<?php esc_html_e( 'Restore', 'wp-security-pack' ); ?>
|
|
</button>
|
|
<button type="button" class="button button-small wpsp-delete-quarantined" data-name="<?php echo esc_attr( $name ); ?>" style="color: #a00;">
|
|
<?php esc_html_e( 'Delete', 'wp-security-pack' ); ?>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Scan Coverage', 'wp-security-pack' ); ?></h2>
|
|
|
|
<h3><?php esc_html_e( 'Directories Scanned', 'wp-security-pack' ); ?></h3>
|
|
<ul style="list-style: disc; margin-left: 20px; color: #666;">
|
|
<li><strong><?php esc_html_e( 'File Integrity:', 'wp-security-pack' ); ?></strong> <?php esc_html_e( 'wp-admin/, wp-includes/, and root WordPress files', 'wp-security-pack' ); ?></li>
|
|
<li><strong><?php esc_html_e( 'Malware Scanner:', 'wp-security-pack' ); ?></strong> <?php esc_html_e( 'wp-content/plugins/, wp-content/themes/, wp-content/uploads/, wp-content/mu-plugins/', 'wp-security-pack' ); ?></li>
|
|
</ul>
|
|
|
|
<h3><?php esc_html_e( 'Detection Methods', 'wp-security-pack' ); ?></h3>
|
|
<table class="widefat striped" style="max-width: 600px;">
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'Checksum verification', 'wp-security-pack' ); ?></strong></td>
|
|
<td><?php esc_html_e( 'Compares core files against WordPress.org checksums', 'wp-security-pack' ); ?></td>
|
|
<td><span style="color: green;">✓ <?php esc_html_e( '100% accurate', 'wp-security-pack' ); ?></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'Pattern-based', 'wp-security-pack' ); ?></strong></td>
|
|
<td><?php esc_html_e( 'Scans code for suspicious patterns and functions', 'wp-security-pack' ); ?></td>
|
|
<td><span style="color: orange;">⚠ <?php esc_html_e( 'May have false positives', 'wp-security-pack' ); ?></span></td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
$('#wpsp-run-file-scan').on('click', function() {
|
|
var $btn = $(this);
|
|
$btn.prop('disabled', true).text('<?php echo esc_js( __( 'Scanning...', 'wp-security-pack' ) ); ?>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_run_file_scan',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
location.reload();
|
|
});
|
|
});
|
|
|
|
$('#wpsp-reset-baseline, #wpsp-dismiss-file-changes').on('click', function() {
|
|
var $btn = $(this);
|
|
$btn.prop('disabled', true);
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_reset_file_baseline',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
location.reload();
|
|
});
|
|
});
|
|
|
|
$('#wpsp-run-malware-scan').on('click', function() {
|
|
var $btn = $(this);
|
|
$btn.prop('disabled', true);
|
|
$('#wpsp-scan-status').text('<?php echo esc_js( __( 'Scanning... This may take a while.', 'wp-security-pack' ) ); ?>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_run_malware_scan',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
location.reload();
|
|
});
|
|
});
|
|
|
|
$('#wpsp-clear-malware-results').on('click', function() {
|
|
var $btn = $(this);
|
|
$btn.prop('disabled', true);
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_clear_malware_results',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
location.reload();
|
|
});
|
|
});
|
|
|
|
// Quarantine file.
|
|
$('.wpsp-quarantine-file').on('click', function() {
|
|
var $btn = $(this);
|
|
var filePath = $btn.data('file');
|
|
|
|
if (!confirm('<?php echo esc_js( __( 'Quarantine this file? It will be moved to a safe location and cannot execute.', 'wp-security-pack' ) ); ?>')) {
|
|
return;
|
|
}
|
|
|
|
$btn.prop('disabled', true).text('<?php echo esc_js( __( 'Moving...', 'wp-security-pack' ) ); ?>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_quarantine_file',
|
|
file_path: filePath,
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$btn.closest('tr').fadeOut(function() {
|
|
$(this).remove();
|
|
location.reload();
|
|
});
|
|
} else {
|
|
alert(response.data.message || '<?php echo esc_js( __( 'Failed to quarantine file.', 'wp-security-pack' ) ); ?>');
|
|
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Quarantine', 'wp-security-pack' ) ); ?>');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Restore file from quarantine.
|
|
$('.wpsp-restore-file').on('click', function() {
|
|
var $btn = $(this);
|
|
var name = $btn.data('name');
|
|
|
|
if (!confirm('<?php echo esc_js( __( 'Restore this file to its original location? Make sure it is safe before restoring.', 'wp-security-pack' ) ); ?>')) {
|
|
return;
|
|
}
|
|
|
|
$btn.prop('disabled', true).text('<?php echo esc_js( __( 'Restoring...', 'wp-security-pack' ) ); ?>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_restore_file',
|
|
quarantine_name: name,
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
location.reload();
|
|
} else {
|
|
alert(response.data.message || '<?php echo esc_js( __( 'Failed to restore file.', 'wp-security-pack' ) ); ?>');
|
|
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Restore', 'wp-security-pack' ) ); ?>');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Delete quarantined file permanently.
|
|
$('.wpsp-delete-quarantined').on('click', function() {
|
|
var $btn = $(this);
|
|
var name = $btn.data('name');
|
|
|
|
if (!confirm('<?php echo esc_js( __( 'Permanently delete this file? This cannot be undone.', 'wp-security-pack' ) ); ?>')) {
|
|
return;
|
|
}
|
|
|
|
$btn.prop('disabled', true).text('<?php echo esc_js( __( 'Deleting...', 'wp-security-pack' ) ); ?>');
|
|
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_delete_quarantined',
|
|
quarantine_name: name,
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$btn.closest('tr').fadeOut(function() {
|
|
$(this).remove();
|
|
});
|
|
} else {
|
|
alert(response.data.message || '<?php echo esc_js( __( 'Failed to delete file.', 'wp-security-pack' ) ); ?>');
|
|
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Delete', 'wp-security-pack' ) ); ?>');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render logs tab.
|
|
*/
|
|
private function render_logs_tab() {
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$page = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1;
|
|
$per_page = 50;
|
|
$offset = ( $page - 1 ) * $per_page;
|
|
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$filter_type = isset( $_GET['event_type'] ) ? sanitize_text_field( wp_unslash( $_GET['event_type'] ) ) : '';
|
|
|
|
$logs = WPSP_Activity_Log::get_logs( array(
|
|
'limit' => $per_page,
|
|
'offset' => $offset,
|
|
'event_type' => $filter_type,
|
|
) );
|
|
|
|
$total = WPSP_Activity_Log::get_log_count( array( 'event_type' => $filter_type ) );
|
|
$pages = ceil( $total / $per_page );
|
|
|
|
$stats = WPSP_Activity_Log::get_stats( 30 );
|
|
?>
|
|
<input type="hidden" name="wpsp_settings[wpsp_current_tab]" value="logs" />
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Activity Log', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description"><?php esc_html_e( 'Monitor all security-related events including login attempts, lockouts, and blocked access. Use this log to identify potential threats and review user activity.', 'wp-security-pack' ); ?></p>
|
|
|
|
<h3><?php esc_html_e( 'Statistics (Last 30 Days)', 'wp-security-pack' ); ?></h3>
|
|
<div class="wpsp-stats">
|
|
<div class="wpsp-stat wpsp-stat-success">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $stats['login_success'] ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Successful Logins', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
<div class="wpsp-stat wpsp-stat-warning">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $stats['login_failed'] ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Failed Logins', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
<div class="wpsp-stat wpsp-stat-danger">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $stats['lockout'] ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Lockouts', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
<div class="wpsp-stat wpsp-stat-info">
|
|
<span class="wpsp-stat-value"><?php echo esc_html( $stats['ip_blocked'] + $stats['geo_blocked'] ); ?></span>
|
|
<span class="wpsp-stat-label"><?php esc_html_e( 'Blocks', 'wp-security-pack' ); ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<h3><?php esc_html_e( 'Log Events', 'wp-security-pack' ); ?></h3>
|
|
|
|
<div class="tablenav top">
|
|
<div class="alignleft actions">
|
|
<select id="wpsp-filter-type">
|
|
<option value=""><?php esc_html_e( 'All Events', 'wp-security-pack' ); ?></option>
|
|
<option value="login_success" <?php selected( $filter_type, 'login_success' ); ?>><?php esc_html_e( 'Login Success', 'wp-security-pack' ); ?></option>
|
|
<option value="login_failed" <?php selected( $filter_type, 'login_failed' ); ?>><?php esc_html_e( 'Login Failed', 'wp-security-pack' ); ?></option>
|
|
<option value="lockout" <?php selected( $filter_type, 'lockout' ); ?>><?php esc_html_e( 'Lockout', 'wp-security-pack' ); ?></option>
|
|
<option value="ip_blocked" <?php selected( $filter_type, 'ip_blocked' ); ?>><?php esc_html_e( 'IP Blocked', 'wp-security-pack' ); ?></option>
|
|
<option value="geo_blocked" <?php selected( $filter_type, 'geo_blocked' ); ?>><?php esc_html_e( 'Geo Blocked', 'wp-security-pack' ); ?></option>
|
|
</select>
|
|
<button type="button" class="button" id="wpsp-filter-logs"><?php esc_html_e( 'Filter', 'wp-security-pack' ); ?></button>
|
|
</div>
|
|
<div class="alignright">
|
|
<button type="button" class="button" id="wpsp-export-logs"><?php esc_html_e( 'Export CSV', 'wp-security-pack' ); ?></button>
|
|
<button type="button" class="button" id="wpsp-clear-logs"><?php esc_html_e( 'Clear All Logs', 'wp-security-pack' ); ?></button>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Time', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Event', 'wp-security-pack' ); ?></th>
|
|
<th style="width: 50px; text-align: center;"><?php esc_html_e( 'Country', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'IP Address', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Username', 'wp-security-pack' ); ?></th>
|
|
<th><?php esc_html_e( 'Details', 'wp-security-pack' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if ( empty( $logs ) ) : ?>
|
|
<tr>
|
|
<td colspan="6"><?php esc_html_e( 'No activity logged yet.', 'wp-security-pack' ); ?></td>
|
|
</tr>
|
|
<?php else : ?>
|
|
<?php foreach ( $logs as $log ) : ?>
|
|
<tr>
|
|
<td><?php echo esc_html( $log->created_at ); ?></td>
|
|
<td>
|
|
<span class="wpsp-event-<?php echo esc_attr( $log->event_type ); ?>">
|
|
<?php echo esc_html( WPSP_Activity_Log::get_event_label( $log->event_type ) ); ?>
|
|
</span>
|
|
</td>
|
|
<td style="text-align: center;" title="<?php echo esc_attr( $log->country_code ? $log->country_code : '' ); ?>">
|
|
<?php if ( $log->country_code ) : ?>
|
|
<?php echo esc_html( WPSP_Helper::get_country_flag( $log->country_code ) ); ?>
|
|
<?php else : ?>
|
|
-
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><code><?php echo esc_html( $log->ip_address ); ?></code></td>
|
|
<td><?php echo esc_html( $log->username ? $log->username : '-' ); ?></td>
|
|
<td><?php echo esc_html( $log->details ? $log->details : '-' ); ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
|
|
<?php if ( $pages > 1 ) : ?>
|
|
<div class="tablenav bottom">
|
|
<div class="tablenav-pages">
|
|
<?php
|
|
echo wp_kses_post( paginate_links( array(
|
|
'base' => add_query_arg( 'paged', '%#%' ),
|
|
'format' => '',
|
|
'current' => $page,
|
|
'total' => $pages,
|
|
) ) );
|
|
?>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Email Alerts', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description"><?php esc_html_e( 'Receive email notifications when suspicious activity is detected. Alerts are sent when multiple failed login attempts occur from the same IP.', 'wp-security-pack' ); ?></p>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Enable Email Alerts', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="wpsp_settings[email_alerts_enabled]" value="1" <?php checked( WP_Security_Pack::get_setting( 'email_alerts_enabled', false ) ); ?> />
|
|
<?php esc_html_e( 'Send email notifications for security events', 'wp-security-pack' ); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Alert Email Address', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="email" name="wpsp_settings[email_alerts_address]"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ) ); ?>" class="regular-text" />
|
|
<p class="description"><?php esc_html_e( 'Leave empty to use the site admin email.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Alert Threshold', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="number" name="wpsp_settings[email_alert_threshold]" min="1" max="50"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'email_alert_threshold', 3 ) ); ?>" class="small-text" />
|
|
<?php esc_html_e( 'failed login attempts', 'wp-security-pack' ); ?>
|
|
<p class="description"><?php esc_html_e( 'Send an alert after this many failed login attempts from the same IP.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Log Settings', 'wp-security-pack' ); ?></h2>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Log Retention', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="number" name="wpsp_settings[log_retention_days]" min="1" max="365"
|
|
value="<?php echo esc_attr( WP_Security_Pack::get_setting( 'log_retention_days', 30 ) ); ?>" class="small-text" />
|
|
<?php esc_html_e( 'days', 'wp-security-pack' ); ?>
|
|
<p class="description"><?php esc_html_e( 'Logs older than this will be automatically deleted via daily cron job.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<?php submit_button(); ?>
|
|
</div>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
$('#wpsp-filter-logs').on('click', function() {
|
|
var type = $('#wpsp-filter-type').val();
|
|
var url = new URL(window.location.href);
|
|
if (type) {
|
|
url.searchParams.set('event_type', type);
|
|
} else {
|
|
url.searchParams.delete('event_type');
|
|
}
|
|
url.searchParams.delete('paged');
|
|
window.location.href = url.toString();
|
|
});
|
|
|
|
$('#wpsp-export-logs').on('click', function() {
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_export_logs',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
var blob = new Blob([response.data.csv], {type: 'text/csv'});
|
|
var url = URL.createObjectURL(blob);
|
|
var a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'wp-security-pack-logs-' + new Date().toISOString().split('T')[0] + '.csv';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
});
|
|
});
|
|
|
|
$('#wpsp-clear-logs').on('click', function() {
|
|
if (confirm('<?php echo esc_js( __( 'Are you sure you want to clear all logs? This cannot be undone.', 'wp-security-pack' ) ); ?>')) {
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_clear_logs',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
location.reload();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render tools tab.
|
|
*/
|
|
private function render_tools_tab() {
|
|
$current_ip = WPSP_Helper::get_client_ip();
|
|
$geo_blocking = wpsp()->get_component( 'geo_blocking' );
|
|
$geo_info = $geo_blocking ? $geo_blocking->get_database_info() : array( 'exists' => false );
|
|
$country_code = $geo_blocking && $current_ip ? $geo_blocking->get_country_code( $current_ip ) : null;
|
|
?>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Your Current IP', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description"><?php esc_html_e( 'Make sure your IP is whitelisted before enabling strict security features to avoid locking yourself out.', 'wp-security-pack' ); ?></p>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Your IP Address', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<code style="font-size: 14px; padding: 5px 10px;"><?php echo esc_html( $current_ip ); ?></code>
|
|
<?php if ( $country_code ) : ?>
|
|
<?php echo esc_html( WPSP_Helper::get_country_flag( $country_code ) . ' ' . $country_code ); ?>
|
|
<?php endif; ?>
|
|
<?php
|
|
$ip_control = wpsp()->get_component( 'ip_control' );
|
|
$is_whitelisted = $ip_control && $ip_control->is_whitelisted( $current_ip );
|
|
?>
|
|
<?php if ( $is_whitelisted ) : ?>
|
|
<span style="color: green; margin-left: 10px;">✓ <?php esc_html_e( 'Whitelisted', 'wp-security-pack' ); ?></span>
|
|
<?php else : ?>
|
|
<span style="color: orange; margin-left: 10px;">○ <?php esc_html_e( 'Not whitelisted', 'wp-security-pack' ); ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Add to Global Whitelist', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php if ( ! $is_whitelisted ) : ?>
|
|
<button type="button" class="button button-primary" id="wpsp-whitelist-my-ip"><?php esc_html_e( 'Whitelist My IP', 'wp-security-pack' ); ?></button>
|
|
<span id="wpsp-whitelist-status"></span>
|
|
<p class="description">
|
|
<?php esc_html_e( 'Adds your IP to the global whitelist (IP Control → IP Whitelist).', 'wp-security-pack' ); ?><br>
|
|
<?php esc_html_e( 'Whitelisted IPs bypass all security checks: login limits, lockouts, geo-blocking, and login access restrictions.', 'wp-security-pack' ); ?>
|
|
</p>
|
|
<?php else : ?>
|
|
<span style="color: green;">✓ <?php esc_html_e( 'Your IP is already in the global whitelist.', 'wp-security-pack' ); ?></span>
|
|
<p class="description"><?php esc_html_e( 'You bypass all security restrictions and cannot be locked out.', 'wp-security-pack' ); ?></p>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Lockout Recovery', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description"><?php esc_html_e( 'If you get locked out, use one of these methods to regain access.', 'wp-security-pack' ); ?></p>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Method 1: Wait', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php
|
|
$lockout_duration = WP_Security_Pack::get_setting( 'login_lockout_duration', 15 );
|
|
printf(
|
|
/* translators: %d: lockout duration in minutes */
|
|
esc_html__( 'Lockouts automatically expire after %d minutes.', 'wp-security-pack' ),
|
|
$lockout_duration
|
|
);
|
|
?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Method 2: FTP/File Manager', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php esc_html_e( 'Rename or delete the plugin folder via FTP:', 'wp-security-pack' ); ?>
|
|
<code>wp-content/plugins/wp-security-pack</code>
|
|
<p class="description"><?php esc_html_e( 'This disables the plugin and removes all blocks. Re-enable after logging in.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Method 3: Database', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<?php esc_html_e( 'Run this SQL query in phpMyAdmin to clear all lockouts:', 'wp-security-pack' ); ?>
|
|
<br><code>TRUNCATE TABLE <?php echo esc_html( $GLOBALS['wpdb']->prefix ); ?>wpsp_lockouts;</code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Manage Lockouts', 'wp-security-pack' ); ?></h2>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Clear All Lockouts', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<button type="button" class="button" id="wpsp-clear-lockouts"><?php esc_html_e( 'Clear All Lockouts', 'wp-security-pack' ); ?></button>
|
|
<span id="wpsp-lockout-status"></span>
|
|
<p class="description"><?php esc_html_e( 'Removes all temporary IP lockouts from the database.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Session Management', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description"><?php esc_html_e( 'Manage user sessions across your site.', 'wp-security-pack' ); ?></p>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Force Logout All Users', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<button type="button" class="button" id="wpsp-force-logout-all"><?php esc_html_e( 'Logout All Users', 'wp-security-pack' ); ?></button>
|
|
<span id="wpsp-logout-status"></span>
|
|
<p class="description"><?php esc_html_e( 'Terminates all user sessions immediately. All users (including you) will need to log in again.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Import / Export Settings', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description"><?php esc_html_e( 'Backup your settings or transfer them to another site.', 'wp-security-pack' ); ?></p>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Export Settings', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<button type="button" class="button" id="wpsp-export-settings"><?php esc_html_e( 'Export Settings', 'wp-security-pack' ); ?></button>
|
|
<p class="description"><?php esc_html_e( 'Download your current settings as a JSON file.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Import Settings', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<input type="file" id="wpsp-import-file" accept=".json" />
|
|
<button type="button" class="button" id="wpsp-import-settings"><?php esc_html_e( 'Import Settings', 'wp-security-pack' ); ?></button>
|
|
<span id="wpsp-import-status"></span>
|
|
<p class="description"><?php esc_html_e( 'Upload a previously exported settings file.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'System Information', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description"><?php esc_html_e( 'Technical details about your WordPress installation and plugin status.', 'wp-security-pack' ); ?></p>
|
|
|
|
<table class="widefat striped">
|
|
<tr>
|
|
<td style="width: 200px;"><strong><?php esc_html_e( 'WordPress Version', 'wp-security-pack' ); ?></strong></td>
|
|
<td><?php echo esc_html( get_bloginfo( 'version' ) ); ?></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'PHP Version', 'wp-security-pack' ); ?></strong></td>
|
|
<td><?php echo esc_html( PHP_VERSION ); ?></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'Plugin Version', 'wp-security-pack' ); ?></strong></td>
|
|
<td><?php echo esc_html( WPSP_VERSION ); ?></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'Server IP Address', 'wp-security-pack' ); ?></strong></td>
|
|
<td>
|
|
<?php
|
|
$server_ip = WPSP_Helper::get_server_ip();
|
|
if ( $server_ip ) :
|
|
?>
|
|
<code><?php echo esc_html( $server_ip ); ?></code>
|
|
<?php else : ?>
|
|
<em><?php esc_html_e( 'Unable to detect', 'wp-security-pack' ); ?></em>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'HTTPS', 'wp-security-pack' ); ?></strong></td>
|
|
<td>
|
|
<?php if ( is_ssl() ) : ?>
|
|
<span style="color: green;">✓</span> <?php esc_html_e( 'Yes', 'wp-security-pack' ); ?>
|
|
<?php else : ?>
|
|
<span style="color: red;">✗</span> <?php esc_html_e( 'No', 'wp-security-pack' ); ?>
|
|
<em>(<?php esc_html_e( 'HTTPS is recommended', 'wp-security-pack' ); ?>)</em>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'Geo Database', 'wp-security-pack' ); ?></strong></td>
|
|
<td>
|
|
<?php if ( $geo_info['exists'] ) : ?>
|
|
<span style="color: green;">✓</span> <?php esc_html_e( 'Installed', 'wp-security-pack' ); ?>
|
|
(<?php echo esc_html( size_format( $geo_info['size'] ) ); ?>)
|
|
<?php else : ?>
|
|
<span style="color: orange;">○</span> <?php esc_html_e( 'Not installed', 'wp-security-pack' ); ?>
|
|
<em>(<?php esc_html_e( 'Required for geo-blocking', 'wp-security-pack' ); ?>)</em>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'Custom Login URL', 'wp-security-pack' ); ?></strong></td>
|
|
<td>
|
|
<?php
|
|
$custom_url = WP_Security_Pack::get_setting( 'login_custom_url', '' );
|
|
if ( WP_Security_Pack::get_setting( 'login_rename_enabled', false ) && ! empty( $custom_url ) ) :
|
|
?>
|
|
<code><?php echo esc_html( home_url( '/' . $custom_url ) ); ?></code>
|
|
<?php else : ?>
|
|
<?php esc_html_e( 'Default (wp-login.php)', 'wp-security-pack' ); ?>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong><?php esc_html_e( 'Server Software', 'wp-security-pack' ); ?></strong></td>
|
|
<td><?php echo esc_html( isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : __( 'Unknown', 'wp-security-pack' ) ); ?></td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wpsp-section">
|
|
<h2><?php esc_html_e( 'Danger Zone', 'wp-security-pack' ); ?></h2>
|
|
<p class="wpsp-description" style="color: #d63638;"><?php esc_html_e( 'These actions are destructive and cannot be undone.', 'wp-security-pack' ); ?></p>
|
|
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e( 'Reset All Settings', 'wp-security-pack' ); ?></th>
|
|
<td>
|
|
<button type="button" class="button" id="wpsp-reset-settings" style="border-color: #d63638; color: #d63638;"><?php esc_html_e( 'Reset to Defaults', 'wp-security-pack' ); ?></button>
|
|
<p class="description"><?php esc_html_e( 'This will reset all plugin settings to their default values.', 'wp-security-pack' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
$('#wpsp-whitelist-my-ip').on('click', function() {
|
|
var $btn = $(this);
|
|
$btn.prop('disabled', true);
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_whitelist_ip',
|
|
ip: '<?php echo esc_js( $current_ip ); ?>',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$('#wpsp-whitelist-status').text('<?php echo esc_js( __( 'IP added to whitelist!', 'wp-security-pack' ) ); ?>').css('color', 'green');
|
|
setTimeout(function() { location.reload(); }, 1000);
|
|
} else {
|
|
$('#wpsp-whitelist-status').text(response.data.message || '<?php echo esc_js( __( 'Error', 'wp-security-pack' ) ); ?>').css('color', 'red');
|
|
$btn.prop('disabled', false);
|
|
}
|
|
});
|
|
});
|
|
|
|
$('#wpsp-clear-lockouts').on('click', function() {
|
|
if (confirm('<?php echo esc_js( __( 'Are you sure you want to clear all lockouts?', 'wp-security-pack' ) ); ?>')) {
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_clear_lockouts',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$('#wpsp-lockout-status').text('<?php echo esc_js( __( 'All lockouts cleared.', 'wp-security-pack' ) ); ?>').css('color', 'green');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$('#wpsp-force-logout-all').on('click', function() {
|
|
if (confirm('<?php echo esc_js( __( 'Are you sure? ALL users (including you) will be logged out immediately.', 'wp-security-pack' ) ); ?>')) {
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_force_logout_all',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$('#wpsp-logout-status').text('<?php echo esc_js( __( 'All sessions terminated. Redirecting to login...', 'wp-security-pack' ) ); ?>').css('color', 'green');
|
|
setTimeout(function() {
|
|
window.location.href = '<?php echo esc_js( wp_login_url() ); ?>';
|
|
}, 1500);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$('#wpsp-export-settings').on('click', function() {
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_export_settings',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
var blob = new Blob([JSON.stringify(response.data, null, 2)], {type: 'application/json'});
|
|
var url = URL.createObjectURL(blob);
|
|
var a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'wp-security-pack-settings.json';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
});
|
|
});
|
|
|
|
$('#wpsp-import-settings').on('click', function() {
|
|
var file = $('#wpsp-import-file')[0].files[0];
|
|
if (!file) {
|
|
alert('<?php echo esc_js( __( 'Please select a file to import.', 'wp-security-pack' ) ); ?>');
|
|
return;
|
|
}
|
|
|
|
var reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_import_settings',
|
|
settings: e.target.result,
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
$('#wpsp-import-status').text('<?php echo esc_js( __( 'Settings imported successfully!', 'wp-security-pack' ) ); ?>').css('color', 'green');
|
|
setTimeout(function() { location.reload(); }, 1000);
|
|
} else {
|
|
$('#wpsp-import-status').text(response.data.message).css('color', 'red');
|
|
}
|
|
});
|
|
};
|
|
reader.readAsText(file);
|
|
});
|
|
|
|
$('#wpsp-reset-settings').on('click', function() {
|
|
if (confirm('<?php echo esc_js( __( 'Are you sure you want to reset all settings to defaults? This cannot be undone.', 'wp-security-pack' ) ); ?>')) {
|
|
$.post(ajaxurl, {
|
|
action: 'wpsp_reset_settings',
|
|
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_admin' ) ); ?>'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
location.reload();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* AJAX: Clear logs.
|
|
*/
|
|
public function ajax_clear_logs() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
WPSP_Activity_Log::clear_all_logs();
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Whitelist IP.
|
|
*
|
|
* Adds an IP to the whitelist setting.
|
|
*/
|
|
public function ajax_whitelist_ip() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$ip = isset( $_POST['ip'] ) ? sanitize_text_field( wp_unslash( $_POST['ip'] ) ) : '';
|
|
|
|
if ( empty( $ip ) || ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Invalid IP address.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
// Get current whitelist.
|
|
$whitelist = WP_Security_Pack::get_setting( 'ip_whitelist', '' );
|
|
|
|
// Check if IP is already in whitelist.
|
|
$whitelist_array = array_filter( array_map( 'trim', explode( "\n", $whitelist ) ) );
|
|
if ( in_array( $ip, $whitelist_array, true ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'IP is already whitelisted.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
// Add IP to whitelist.
|
|
$whitelist_array[] = $ip;
|
|
$new_whitelist = implode( "\n", $whitelist_array );
|
|
|
|
WP_Security_Pack::update_setting( 'ip_whitelist', $new_whitelist );
|
|
|
|
// Also remove from lockouts if present.
|
|
$ip_control = wpsp()->get_component( 'ip_control' );
|
|
if ( $ip_control ) {
|
|
$ip_control->unblock_ip( $ip );
|
|
}
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Run file scan.
|
|
*/
|
|
public function ajax_run_file_scan() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$file_integrity = new WPSP_File_Integrity();
|
|
$results = $file_integrity->scan_core_files( true );
|
|
|
|
wp_send_json_success( $results );
|
|
}
|
|
|
|
/**
|
|
* AJAX: Run malware scan.
|
|
*/
|
|
public function ajax_run_malware_scan() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
// Increase time limit.
|
|
set_time_limit( 300 );
|
|
|
|
$scanner = new WPSP_Malware_Scanner();
|
|
$results = $scanner->scan_files();
|
|
|
|
update_option( WPSP_Malware_Scanner::RESULTS_OPTION, $results );
|
|
update_option( WPSP_Malware_Scanner::LAST_SCAN_OPTION, time() );
|
|
|
|
wp_send_json_success( $results );
|
|
}
|
|
|
|
/**
|
|
* AJAX: Reset file integrity baseline.
|
|
*/
|
|
public function ajax_reset_file_baseline() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$file_integrity = new WPSP_File_Integrity();
|
|
$file_integrity->reset_baseline();
|
|
|
|
// Run a fresh scan to establish new baseline.
|
|
$file_integrity->scan_core_files( true );
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Clear malware scan results.
|
|
*/
|
|
public function ajax_clear_malware_results() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$scanner = new WPSP_Malware_Scanner();
|
|
$scanner->clear_results();
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Download GeoIP database.
|
|
*/
|
|
public function ajax_download_geo_db() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$geo_blocking = wpsp()->get_component( 'geo_blocking' );
|
|
if ( ! $geo_blocking ) {
|
|
wp_send_json_error( array( 'message' => __( 'Geo blocking not available.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$result = $geo_blocking->download_database();
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
|
|
}
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Send test email.
|
|
*/
|
|
public function ajax_test_email() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
|
|
|
|
if ( empty( $email ) ) {
|
|
$email = get_option( 'admin_email' );
|
|
}
|
|
|
|
if ( ! is_email( $email ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Invalid email address.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$site_name = get_bloginfo( 'name' );
|
|
$subject = sprintf(
|
|
/* translators: %s: Site name */
|
|
__( '[%s] WP Security Pack - Test Email', 'wp-security-pack' ),
|
|
$site_name
|
|
);
|
|
|
|
$message = sprintf(
|
|
/* translators: 1: Site name, 2: Site URL */
|
|
__( "This is a test email from WP Security Pack.\n\nIf you received this email, your email alerts are configured correctly.\n\nSite: %1\$s\nURL: %2\$s", 'wp-security-pack' ),
|
|
$site_name,
|
|
home_url()
|
|
);
|
|
|
|
$sent = wp_mail( $email, $subject, $message );
|
|
|
|
if ( $sent ) {
|
|
wp_send_json_success();
|
|
} else {
|
|
wp_send_json_error( array( 'message' => __( 'Failed to send email. Check your server mail configuration.', 'wp-security-pack' ) ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* AJAX: Force logout all users.
|
|
*/
|
|
public function ajax_force_logout_all() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
// Get all users and destroy their sessions.
|
|
$users = get_users( array( 'fields' => 'ID' ) );
|
|
foreach ( $users as $user_id ) {
|
|
$sessions = WP_Session_Tokens::get_instance( $user_id );
|
|
$sessions->destroy_all();
|
|
}
|
|
|
|
// Log the action.
|
|
WPSP_Activity_Log::log( 'force_logout', WPSP_Helper::get_client_ip(), wp_get_current_user()->user_login, __( 'All user sessions terminated', 'wp-security-pack' ) );
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Export settings.
|
|
*/
|
|
public function ajax_export_settings() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$settings = get_option( 'wpsp_settings', array() );
|
|
|
|
wp_send_json_success( $settings );
|
|
}
|
|
|
|
/**
|
|
* AJAX: Import settings.
|
|
*/
|
|
public function ajax_import_settings() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$settings_json = isset( $_POST['settings'] ) ? wp_unslash( $_POST['settings'] ) : '';
|
|
|
|
if ( empty( $settings_json ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'No settings data provided.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$settings = json_decode( $settings_json, true );
|
|
|
|
if ( null === $settings ) {
|
|
wp_send_json_error( array( 'message' => __( 'Invalid JSON format.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
// Sanitize the imported settings.
|
|
$sanitized = $this->sanitize_settings( $settings );
|
|
|
|
update_option( 'wpsp_settings', $sanitized );
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Export logs as CSV.
|
|
*/
|
|
public function ajax_export_logs() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$logs = WPSP_Activity_Log::get_logs( array( 'limit' => 10000 ) );
|
|
|
|
$csv_lines = array();
|
|
$csv_lines[] = 'Time,Event,IP Address,Country,Username,Details,User Agent';
|
|
|
|
foreach ( $logs as $log ) {
|
|
$csv_lines[] = sprintf(
|
|
'"%s","%s","%s","%s","%s","%s","%s"',
|
|
str_replace( '"', '""', $log->created_at ),
|
|
str_replace( '"', '""', WPSP_Activity_Log::get_event_label( $log->event_type ) ),
|
|
str_replace( '"', '""', $log->ip_address ),
|
|
str_replace( '"', '""', $log->country_code ? $log->country_code : '' ),
|
|
str_replace( '"', '""', $log->username ? $log->username : '' ),
|
|
str_replace( '"', '""', $log->details ? $log->details : '' ),
|
|
str_replace( '"', '""', $log->user_agent ? $log->user_agent : '' )
|
|
);
|
|
}
|
|
|
|
wp_send_json_success( array( 'csv' => implode( "\n", $csv_lines ) ) );
|
|
}
|
|
|
|
/**
|
|
* AJAX: Clear all lockouts.
|
|
*/
|
|
public function ajax_clear_lockouts() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
global $wpdb;
|
|
$table = WPSP_DB::get_lockout_table();
|
|
|
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
|
$wpdb->query( "TRUNCATE TABLE {$table}" );
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Reset all settings to defaults.
|
|
*/
|
|
public function ajax_reset_settings() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$defaults = WP_Security_Pack::get_default_settings();
|
|
update_option( 'wpsp_settings', $defaults );
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* AJAX: Quarantine a suspicious file.
|
|
*/
|
|
public function ajax_quarantine_file() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$file_path = isset( $_POST['file_path'] ) ? sanitize_text_field( wp_unslash( $_POST['file_path'] ) ) : '';
|
|
|
|
if ( empty( $file_path ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'No file specified.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$scanner = new WPSP_Malware_Scanner();
|
|
$result = $scanner->quarantine_file( $file_path );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
|
|
}
|
|
|
|
wp_send_json_success( $result );
|
|
}
|
|
|
|
/**
|
|
* AJAX: Restore a file from quarantine.
|
|
*/
|
|
public function ajax_restore_file() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$quarantine_name = isset( $_POST['quarantine_name'] ) ? sanitize_file_name( wp_unslash( $_POST['quarantine_name'] ) ) : '';
|
|
|
|
if ( empty( $quarantine_name ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'No file specified.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$scanner = new WPSP_Malware_Scanner();
|
|
$result = $scanner->restore_file( $quarantine_name );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
|
|
}
|
|
|
|
wp_send_json_success( $result );
|
|
}
|
|
|
|
/**
|
|
* AJAX: Delete a quarantined file permanently.
|
|
*/
|
|
public function ajax_delete_quarantined() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$quarantine_name = isset( $_POST['quarantine_name'] ) ? sanitize_file_name( wp_unslash( $_POST['quarantine_name'] ) ) : '';
|
|
|
|
if ( empty( $quarantine_name ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'No file specified.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$scanner = new WPSP_Malware_Scanner();
|
|
$result = $scanner->delete_quarantined_file( $quarantine_name );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
|
|
}
|
|
|
|
wp_send_json_success( $result );
|
|
}
|
|
|
|
/**
|
|
* AJAX: Delete WordPress info files (readme.html, license.txt).
|
|
*/
|
|
public function ajax_delete_wp_file() {
|
|
check_ajax_referer( 'wpsp_admin' );
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$file = isset( $_POST['file'] ) ? sanitize_file_name( wp_unslash( $_POST['file'] ) ) : '';
|
|
|
|
// Only allow specific safe files to be deleted.
|
|
$allowed_files = array( 'readme.html', 'license.txt' );
|
|
|
|
if ( ! in_array( $file, $allowed_files, true ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Invalid file.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
$file_path = ABSPATH . $file;
|
|
|
|
if ( ! file_exists( $file_path ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'File not found.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
|
|
if ( ! unlink( $file_path ) ) {
|
|
wp_send_json_error( array( 'message' => __( 'Failed to delete file. Check file permissions.', 'wp-security-pack' ) ) );
|
|
}
|
|
|
|
wp_send_json_success();
|
|
}
|
|
}
|