__( 'Status', 'security-pack' ),
'login' => __( 'Login Protection', 'security-pack' ),
'ip' => __( 'IP Control', 'security-pack' ),
'hardening' => __( 'Hardening', 'security-pack' ),
'headers' => __( 'Security Headers', 'security-pack' ),
'2fa' => __( '2FA', 'security-pack' ),
'scanner' => __( 'Scanner', 'security-pack' ),
'logs' => __( 'Activity Log', 'security-pack' ),
'tools' => __( 'Tools', 'security-pack' ),
);
foreach ( $submenus as $slug => $title ) {
add_submenu_page(
'security-pack',
$title . ' - ' . __( 'Security Pack', 'security-pack' ),
$title,
'manage_options',
'status' === $slug ? 'security-pack' : '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 ( '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 'security-pack';
}
return '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(
'%s',
admin_url( 'admin.php?page=security-pack' ),
__( 'Settings', '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(
'%s',
'https://arkhost.com/products-menu.php',
__( 'Get Hosting', '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', '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 );
?>
__( 'Status', 'security-pack' ),
'login' => __( 'Login Protection', 'security-pack' ),
'ip' => __( 'IP Control', 'security-pack' ),
'hardening' => __( 'Hardening', 'security-pack' ),
'headers' => __( 'Security Headers', 'security-pack' ),
'2fa' => __( '2FA', 'security-pack' ),
'scanner' => __( 'Scanner', 'security-pack' ),
'logs' => __( 'Activity Log', 'security-pack' ),
'tools' => __( 'Tools', 'security-pack' ),
);
?>
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;
?>
__( 'Login Attempt Limiting', 'security-pack' ),
'description' => __( 'Limits failed login attempts to prevent brute force attacks', 'security-pack' ),
'status' => Security_Pack::get_setting( 'login_limit_enabled', true ),
'tab' => 'login',
);
$checks['honeypot'] = array(
'name' => __( 'Login Honeypot', 'security-pack' ),
'description' => __( 'Hidden field that traps automated bots', 'security-pack' ),
'status' => Security_Pack::get_setting( 'honeypot_enabled', true ),
'tab' => 'login',
);
$checks['hide_login_errors'] = array(
'name' => __( 'Login Errors Hidden', 'security-pack' ),
'description' => __( 'Generic error message prevents username enumeration', 'security-pack' ),
'status' => Security_Pack::get_setting( 'hide_login_errors', true ),
'tab' => 'login',
);
// Hardening.
$checks['xmlrpc'] = array(
'name' => __( 'XML-RPC Disabled', 'security-pack' ),
'description' => __( 'Blocks XML-RPC endpoint commonly used in attacks', 'security-pack' ),
'status' => Security_Pack::get_setting( 'disable_xmlrpc', true ),
'tab' => 'hardening',
);
$checks['file_editing'] = array(
'name' => __( 'File Editor Disabled', 'security-pack' ),
'description' => __( 'Prevents editing theme/plugin files from dashboard', 'security-pack' ),
'status' => Security_Pack::get_setting( 'disable_file_editing', true ),
'tab' => 'hardening',
);
$checks['rest_api'] = array(
'name' => __( 'REST API Restricted', 'security-pack' ),
'description' => __( 'Requires authentication for REST API access', 'security-pack' ),
'status' => Security_Pack::get_setting( 'restrict_rest_api', true ),
'tab' => 'hardening',
);
$checks['user_enum'] = array(
'name' => __( 'User Enumeration Blocked', 'security-pack' ),
'description' => __( 'Prevents attackers from discovering usernames', 'security-pack' ),
'status' => Security_Pack::get_setting( 'disable_user_enumeration', true ),
'tab' => 'hardening',
);
$checks['pingbacks'] = array(
'name' => __( 'Pingbacks Disabled', 'security-pack' ),
'description' => __( 'Prevents pingback-based DDoS amplification', 'security-pack' ),
'status' => Security_Pack::get_setting( 'disable_pingbacks', true ),
'tab' => 'hardening',
);
$checks['wp_version'] = array(
'name' => __( 'WP Version Hidden', 'security-pack' ),
'description' => __( 'Hides WordPress version from public view', 'security-pack' ),
'status' => Security_Pack::get_setting( 'remove_wp_version', true ),
'tab' => 'hardening',
);
$checks['security_headers'] = array(
'name' => __( 'Security Headers', 'security-pack' ),
'description' => __( 'HTTP headers that enable browser security features', 'security-pack' ),
'status' => 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 = Security_Pack::get_setting( 'two_factor_enabled', false );
$two_fa_enforced = Security_Pack::get_setting( 'two_factor_enforce_admin', false );
$checks['two_factor'] = array(
'name' => __( 'Two-Factor Authentication', 'security-pack' ),
'description' => $two_fa_enabled && ! $two_fa_enforced
? __( 'Available but not enforced for admins', 'security-pack' )
: __( 'Enforced for administrator accounts', 'security-pack' ),
'status' => $two_fa_enabled && $two_fa_enforced,
'tab' => '2fa',
);
// Monitoring.
$checks['file_integrity'] = array(
'name' => __( 'File Integrity Monitoring', 'security-pack' ),
'description' => __( 'Detects unauthorized changes to WordPress core', 'security-pack' ),
'status' => Security_Pack::get_setting( 'file_integrity_enabled', true ),
'tab' => 'scanner',
);
$checks['malware_scan'] = array(
'name' => __( 'Malware Scanning', 'security-pack' ),
'description' => __( 'Scans files for malicious code patterns', 'security-pack' ),
'status' => 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', 'security-pack' ),
'description' => __( 'Hides wp-login.php from automated scanners', 'security-pack' ),
'status' => Security_Pack::get_setting( 'login_rename_enabled', false ),
'tab' => 'login',
);
$checks['geo_blocking'] = array(
'name' => __( 'Country Blocking', 'security-pack' ),
'description' => __( 'Blocks access from specific countries', 'security-pack' ),
'status' => Security_Pack::get_setting( 'geo_blocking_enabled', false ),
'tab' => 'ip',
);
$checks['auto_blacklist'] = array(
'name' => __( 'Auto-Blacklist Repeat Offenders', 'security-pack' ),
'description' => __( 'Permanently blocks IPs with repeated lockouts', 'security-pack' ),
'status' => 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', 'security-pack' ),
'description' => is_ssl()
? __( 'Site is served over HTTPS', 'security-pack' )
: __( 'Site should use HTTPS for security', 'security-pack' ),
'status' => is_ssl(),
'tab' => '',
);
$checks['debug_mode'] = array(
'name' => __( 'Debug Mode Disabled', 'security-pack' ),
'description' => defined( 'WP_DEBUG' ) && WP_DEBUG
? __( 'WP_DEBUG is enabled - disable in production', 'security-pack' )
: __( 'Debug mode is properly disabled', 'security-pack' ),
'status' => ! ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
'tab' => '',
);
$checks['db_prefix'] = array(
'name' => __( 'Database Prefix Changed', 'security-pack' ),
'description' => 'wp_' === $wpdb->prefix
? __( 'Using default wp_ prefix - consider changing', 'security-pack' )
: __( 'Using custom database prefix', 'security-pack' ),
'status' => 'wp_' !== $wpdb->prefix,
'tab' => '',
);
$admin_user = get_user_by( 'login', 'admin' );
$checks['admin_username'] = array(
'name' => __( 'No "admin" Username', 'security-pack' ),
'description' => $admin_user
? __( 'Default "admin" username exists - consider renaming', 'security-pack' )
: __( 'No user with "admin" username', 'security-pack' ),
'status' => ! $admin_user,
'tab' => '',
);
$checks['php_version'] = array(
'name' => __( 'PHP Version', 'security-pack' ),
'description' => version_compare( PHP_VERSION, '8.0', '<' )
? sprintf(
/* translators: %s: PHP version */
__( 'PHP %s is outdated - update recommended', 'security-pack' ),
PHP_VERSION
)
: sprintf(
/* translators: %s: PHP version */
__( 'Running PHP %s', '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', 'security-pack' ),
'description' => $auto_updates_enabled
? __( 'WordPress core auto-updates are enabled', 'security-pack' )
: __( 'Enable auto-updates for security patches', 'security-pack' ),
'status' => $auto_updates_enabled,
'tab' => '',
);
return $checks;
}
/**
* Render login protection tab.
*/
private function render_login_tab() {
?>
get_component( 'geo_blocking' );
$db_exists = $geo_blocking && $geo_blocking->database_exists();
if ( ! $db_exists ) :
?>
' . esc_html__( 'IP Control', 'security-pack' ) . ''
);
?>
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();
?>
|
|
|
|
ip_address ); ?> |
failed_attempts ); ?> |
lockout_until ); ?> |
|
get_default_headers();
$headers = Security_Pack::get_setting( 'security_headers', $default_headers );
?>
securityheaders.com'
);
?>
'_wpsp_2fa_enabled', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Required for 2FA user lookup.
'meta_value' => '1', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Required for 2FA user lookup.
'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++;
}
}
?>
0 || $total_admins > 0 ) : ?>
- Google Authenticator -
- Authy -
- Microsoft Authenticator -
- 1Password -
- Bitwarden -
-
' . esc_html__( 'Profile page', 'security-pack' ) . ''
);
?>
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() );
?>
0 || $malware_results['time'] > 0 ) : ?>
0 ? esc_html( $file_changes_count ) : '✓'; ?>
0 ? esc_html( $malware_issues_count ) : '✓'; ?>
0 ) : ?>
get_quarantined_files();
if ( ! empty( $quarantined_files ) ) :
?>
$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 );
?>
|
|
|
|
|
|
|
| created_at ); ?> |
event_type ) ); ?>
|
country_code ) : ?>
country_code ) ); ?>
-
|
ip_address ); ?> |
username ? $log->username : '-' ); ?> |
details ? $log->details : '-' ); ?> |
1 ) : ?>
add_query_arg( 'paged', '%#%' ),
'format' => '',
'current' => $page,
'total' => $pages,
) ) );
?>
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;
?>
__( 'Permission denied.', '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.', '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.', 'security-pack' ) ) );
}
// Get current whitelist.
$whitelist = 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.', 'security-pack' ) ) );
}
// Add IP to whitelist.
$whitelist_array[] = $ip;
$new_whitelist = implode( "\n", $whitelist_array );
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.', '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.', 'security-pack' ) ) );
}
// Increase time limit for potentially long scans.
// phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for comprehensive malware scanning.
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.', '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.', '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.', 'security-pack' ) ) );
}
$geo_blocking = wpsp()->get_component( 'geo_blocking' );
if ( ! $geo_blocking ) {
wp_send_json_error( array( 'message' => __( 'Geo blocking not available.', '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.', '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.', 'security-pack' ) ) );
}
$site_name = get_bloginfo( 'name' );
$subject = sprintf(
/* translators: %s: Site name */
__( '[%s] Security Pack - Test Email', 'security-pack' ),
$site_name
);
$message = sprintf(
/* translators: 1: Site name, 2: Site URL */
__( "This is a test email from Security Pack.\n\nIf you received this email, your email alerts are configured correctly.\n\nSite: %1\$s\nURL: %2\$s", '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.', '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.', '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', '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.', '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.', 'security-pack' ) ) );
}
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON is decoded then sanitized via sanitize_settings().
$settings_json = isset( $_POST['settings'] ) ? wp_unslash( $_POST['settings'] ) : '';
if ( empty( $settings_json ) ) {
wp_send_json_error( array( 'message' => __( 'No settings data provided.', 'security-pack' ) ) );
}
$settings = json_decode( $settings_json, true );
if ( null === $settings ) {
wp_send_json_error( array( 'message' => __( 'Invalid JSON format.', 'security-pack' ) ) );
}
// Sanitize the imported settings (individual values are sanitized here).
$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.', '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: Unblock a single IP.
*/
public function ajax_unblock_ip() {
check_ajax_referer( 'wpsp_admin' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', '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.', 'security-pack' ) ) );
}
$ip_control = wpsp()->get_component( 'ip_control' );
if ( $ip_control ) {
$ip_control->unblock_ip( $ip );
}
wp_send_json_success();
}
/**
* 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.', 'security-pack' ) ) );
}
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Admin action to clear lockouts.
$wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}wpsp_lockouts" );
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.', 'security-pack' ) ) );
}
$defaults = 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.', '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.', '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.', '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.', '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.', '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.', '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.', '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.', 'security-pack' ) ) );
}
$file_path = ABSPATH . $file;
if ( ! file_exists( $file_path ) ) {
wp_send_json_error( array( 'message' => __( 'File not found.', '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.', 'security-pack' ) ) );
}
wp_send_json_success();
}
}