init_hooks(); } /** * Initialize hooks * * @return void */ private function init_hooks() { add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_menu', array($this, 'force_admin_menu_label'), 999); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); add_action('admin_init', array($this, 'register_settings')); add_action('wp_ajax_dwarpal_scan_website', array($this, 'ajax_scan_website')); add_action('wp_ajax_dwarpal_backup_website', array($this, 'ajax_backup_website')); add_action('wp_ajax_dwarpal_suggest_fix', array($this, 'ajax_suggest_fix')); add_action('wp_ajax_dwarpal_save_scan_report', array($this, 'ajax_save_scan_report')); } /** * Add admin menu * * @return void */ public function add_admin_menu() { // Use dashicons-shield as base icon (CSS will override with emoji) $icon_url = 'dashicons-shield'; // Left sidebar label must stay short: "Dwarpal" only (not the plugin slug). $this->page_hooks[] = add_menu_page( 'Dwarpal', 'Dwarpal', 'manage_options', 'dwarpal', array($this, 'render_dashboard_page'), $icon_url, 30 ); // Replace the auto-generated first submenu (which would repeat the parent title). $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Dashboard', 'dwarpal'), __('Dashboard', 'dwarpal'), 'manage_options', 'dwarpal', array($this, 'render_dashboard_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Firewall', 'dwarpal'), __('Firewall', 'dwarpal'), 'manage_options', 'dwarpal-firewall', array($this, 'render_firewall_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Login Security', 'dwarpal'), __('Login Security', 'dwarpal'), 'manage_options', 'dwarpal-login', array($this, 'render_login_security_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Security Logs', 'dwarpal'), __('Security Logs', 'dwarpal'), 'manage_options', 'dwarpal-logs', array($this, 'render_logs_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Malware Scanner', 'dwarpal'), __('Malware Scanner', 'dwarpal'), 'manage_options', 'dwarpal-malware', array($this, 'render_malware_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('File Integrity', 'dwarpal'), __('File Integrity', 'dwarpal'), 'manage_options', 'dwarpal-integrity', array($this, 'render_integrity_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Security Hardening', 'dwarpal'), __('Hardening', 'dwarpal'), 'manage_options', 'dwarpal-hardening', array($this, 'render_hardening_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Two-Factor Auth', 'dwarpal'), __('2FA', 'dwarpal'), 'manage_options', 'dwarpal-2fa', array($this, 'render_2fa_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Spam Prevention', 'dwarpal'), __('Spam Prevention', 'dwarpal'), 'manage_options', 'dwarpal-spam', array($this, 'render_spam_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('IP Blocking', 'dwarpal'), __('IP Blocking', 'dwarpal'), 'manage_options', 'dwarpal-ip-blocking', array($this, 'render_ip_blocking_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Security Headers', 'dwarpal'), __('Security Headers', 'dwarpal'), 'manage_options', 'dwarpal-headers', array($this, 'render_headers_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Post-Hack Recovery', 'dwarpal'), __('Post-Hack', 'dwarpal'), 'manage_options', 'dwarpal-post-hack', array($this, 'render_post_hack_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('File Permissions', 'dwarpal'), __('File Permissions', 'dwarpal'), 'manage_options', 'dwarpal-permissions', array($this, 'render_permissions_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Database Security', 'dwarpal'), __('Database', 'dwarpal'), 'manage_options', 'dwarpal-database', array($this, 'render_database_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Vulnerability Scanner', 'dwarpal'), __('Vulnerabilities', 'dwarpal'), 'manage_options', 'dwarpal-vulnerabilities', array($this, 'render_vulnerabilities_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Blocklist Monitor', 'dwarpal'), __('Blocklist', 'dwarpal'), 'manage_options', 'dwarpal-blocklist', array($this, 'render_blocklist_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Live Traffic', 'dwarpal'), __('Live Traffic', 'dwarpal'), 'manage_options', 'dwarpal-traffic', array($this, 'render_traffic_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('HTTP Error Logs', 'dwarpal'), __('HTTP Errors', 'dwarpal'), 'manage_options', 'dwarpal-http-errors', array($this, 'render_http_errors_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Maintenance Mode', 'dwarpal'), __('Maintenance', 'dwarpal'), 'manage_options', 'dwarpal-maintenance', array($this, 'render_maintenance_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('System Info', 'dwarpal'), __('System Info', 'dwarpal'), 'manage_options', 'dwarpal-system-info', array($this, 'render_system_info_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('SEO Protection', 'dwarpal'), __('SEO Protection', 'dwarpal'), 'manage_options', 'dwarpal-seo-protection', array($this, 'render_seo_protection_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Critical Options', 'dwarpal'), __('Critical Options', 'dwarpal'), 'manage_options', 'dwarpal-critical-options', array($this, 'render_critical_options_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Outbound Monitor', 'dwarpal'), __('Outbound Monitor', 'dwarpal'), 'manage_options', 'dwarpal-outbound-monitor', array($this, 'render_outbound_monitor_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Plugin Scanner', 'dwarpal'), __('Plugin Scanner', 'dwarpal'), 'manage_options', 'dwarpal-plugin-scanner', array($this, 'render_plugin_scanner_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Threat Defense', 'dwarpal'), __('Threat Defense', 'dwarpal'), 'manage_options', 'dwarpal-threat-defense', array($this, 'render_threat_defense_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('OWASP Coverage', 'dwarpal'), __('OWASP Coverage', 'dwarpal'), 'manage_options', 'dwarpal-owasp', array($this, 'render_owasp_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Security Report', 'dwarpal'), __('Security Report', 'dwarpal'), 'manage_options', 'dwarpal-scan-report', array($this, 'render_scan_report_page') ); $this->page_hooks[] = add_submenu_page( 'dwarpal', __('Settings', 'dwarpal'), __('Settings', 'dwarpal'), 'manage_options', 'dwarpal-settings', array($this, 'render_settings_page') ); } /** * Ensure the left admin menu always shows "Dwarpal" (never the slug). * * @return void */ public function force_admin_menu_label() { global $menu; if (!is_array($menu)) { return; } foreach ($menu as $index => $item) { if (isset($item[2]) && 'dwarpal' === $item[2]) { $menu[ $index ][0] = 'Dwarpal'; break; } } } /** * Enqueue admin assets * * @param string $hook Current admin page hook * @return void */ public function enqueue_admin_assets($hook) { if (!in_array($hook, array_filter($this->page_hooks), true)) { return; } wp_enqueue_style( 'dwarpal-admin', DWARPAL_PLUGIN_URL . 'admin/css/dwarpal-admin.css', array(), DWARPAL_VERSION ); wp_enqueue_script( 'dwarpal-admin', DWARPAL_PLUGIN_URL . 'js/admin.js', array('jquery'), DWARPAL_VERSION, true ); // Localize script for AJAX wp_localize_script('dwarpal-admin', 'dwarpalAdmin', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('dwarpal_admin_nonce'), 'reportUrl' => admin_url('admin.php?page=dwarpal-scan-report'), 'featureUrls' => array( 'firewall' => admin_url('admin.php?page=dwarpal-firewall'), 'login_security' => admin_url('admin.php?page=dwarpal-login'), 'security_logs' => admin_url('admin.php?page=dwarpal-logs'), 'malware' => admin_url('admin.php?page=dwarpal-malware'), 'vulnerability' => admin_url('admin.php?page=dwarpal-vulnerability'), 'file_integrity' => admin_url('admin.php?page=dwarpal-integrity'), 'permissions' => admin_url('admin.php?page=dwarpal-permissions'), 'hardening' => admin_url('admin.php?page=dwarpal-hardening'), 'two_factor' => admin_url('admin.php?page=dwarpal-2fa'), 'spam_prevention' => admin_url('admin.php?page=dwarpal-spam'), 'ip_blocking' => admin_url('admin.php?page=dwarpal-ip-blocking'), 'security_headers' => admin_url('admin.php?page=dwarpal-headers'), 'post_hack' => admin_url('admin.php?page=dwarpal-post-hack'), 'database' => admin_url('admin.php?page=dwarpal-database'), 'blocklist' => admin_url('admin.php?page=dwarpal-blocklist'), 'live_traffic' => admin_url('admin.php?page=dwarpal-live-traffic'), 'http_errors' => admin_url('admin.php?page=dwarpal-http-errors'), 'maintenance' => admin_url('admin.php?page=dwarpal-maintenance'), 'system_info' => admin_url('admin.php?page=dwarpal-system-info'), 'plugin_scanner' => admin_url('admin.php?page=dwarpal-plugin-scanner'), 'seo_protection' => admin_url('admin.php?page=dwarpal-seo-protection'), ), )); // Enqueue page-specific inline scripts and styles $this->enqueue_page_specific_assets($hook); } /** * Enqueue page-specific inline scripts and styles * * @param string $hook Current admin page hook * @return void */ private function enqueue_page_specific_assets($hook) { // Integrity page if (strpos($hook, 'dwarpal-integrity') !== false) { $integrity_script = "jQuery(document).ready(function($) { $('#dwarpal-check-integrity').on('click', function() { var button = $(this); button.prop('disabled', true).text('" . esc_js(__('Checking...', 'dwarpal')) . "'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'dwarpal_check_integrity', nonce: '" . esc_js(wp_create_nonce('dwarpal_integrity')) . "' }, success: function(response) { if (response.success) { location.reload(); } else { alert(response.data.message || '" . esc_js(__('Check failed.', 'dwarpal')) . "'); } button.prop('disabled', false).text('" . esc_js(__('Check Integrity', 'dwarpal')) . "'); } }); }); });"; wp_add_inline_script('dwarpal-admin', $integrity_script); } // Plugin Scanner page if (strpos($hook, 'dwarpal-plugin-scanner') !== false) { $plugin_scanner_script = "jQuery(document).ready(function($) { $('#dwarpal-scan-plugins').on('click', function() { var \$button = $(this); var \$status = $('#dwarpal-scan-status'); \$button.prop('disabled', true).text('" . esc_js(__('Scanning...', 'dwarpal')) . "'); \$status.show(); $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_scan_plugins', nonce: dwarpalAdmin.nonce }, success: function(response) { if (response.success) { location.reload(); } else { alert('" . esc_js(__('Scan failed:', 'dwarpal')) . " ' + (response.data.message || 'Unknown error')); \$button.prop('disabled', false).text('" . esc_js(__('Scan All Plugins', 'dwarpal')) . "'); \$status.hide(); } }, error: function() { alert('" . esc_js(__('An error occurred during the scan.', 'dwarpal')) . "'); \$button.prop('disabled', false).text('" . esc_js(__('Scan All Plugins', 'dwarpal')) . "'); \$status.hide(); } }); }); $('#dwarpal-protect-backups').on('click', function() { if (!confirm('" . esc_js(__('This will enable firewall rules to block direct access to common backup file paths. Continue?', 'dwarpal')) . "')) { return; } var \$button = $(this); \$button.prop('disabled', true).text('" . esc_js(__('Protecting...', 'dwarpal')) . "'); $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_protect_backup_files', nonce: dwarpalAdmin.nonce }, success: function(response) { if (response.success) { alert('" . esc_js(__('Backup files protected successfully!', 'dwarpal')) . "'); location.reload(); } else { alert('" . esc_js(__('Failed to protect backup files:', 'dwarpal')) . " ' + (response.data.message || 'Unknown error')); \$button.prop('disabled', false).text('" . esc_js(__('Protect Backup Files', 'dwarpal')) . "'); } }, error: function() { alert('" . esc_js(__('An error occurred.', 'dwarpal')) . "'); \$button.prop('disabled', false).text('" . esc_js(__('Protect Backup Files', 'dwarpal')) . "'); } }); }); $('.dwarpal-disable-plugin').on('click', function() { var pluginFile = $(this).data('plugin-file'); var pluginName = $(this).data('plugin-name'); if (!confirm('" . esc_js(__('Are you sure you want to disable', 'dwarpal')) . " \"' + pluginName + '\"?')) { return; } var \$button = $(this); \$button.prop('disabled', true).text('" . esc_js(__('Disabling...', 'dwarpal')) . "'); $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_disable_plugin', nonce: dwarpalAdmin.nonce, plugin_file: pluginFile }, success: function(response) { if (response.success) { alert('" . esc_js(__('Plugin disabled successfully.', 'dwarpal')) . "'); location.reload(); } else { alert('" . esc_js(__('Failed to disable plugin:', 'dwarpal')) . " ' + (response.data.message || 'Unknown error')); \$button.prop('disabled', false).text('" . esc_js(__('Disable', 'dwarpal')) . "'); } }, error: function() { alert('" . esc_js(__('An error occurred.', 'dwarpal')) . "'); \$button.prop('disabled', false).text('" . esc_js(__('Disable', 'dwarpal')) . "'); } }); }); $('.dwarpal-delete-backup-file').on('click', function() { var filePath = $(this).data('file-path'); var fileName = $(this).data('file-name'); if (!confirm('" . esc_js(__('Are you sure you want to delete', 'dwarpal')) . " \"' + fileName + '\"?\\n\\n" . esc_js(__('This action cannot be undone!', 'dwarpal')) . "')) { return; } var \$button = $(this); \$button.prop('disabled', true).text('" . esc_js(__('Deleting...', 'dwarpal')) . "'); $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_fix_plugin_issue', nonce: dwarpalAdmin.nonce, action_type: 'delete_backup_file', file_path: filePath }, success: function(response) { if (response.success) { alert('" . esc_js(__('Backup file deleted successfully.', 'dwarpal')) . "'); location.reload(); } else { alert('" . esc_js(__('Failed to delete file:', 'dwarpal')) . " ' + (response.data.message || 'Unknown error')); \$button.prop('disabled', false).text('" . esc_js(__('Delete', 'dwarpal')) . "'); } }, error: function() { alert('" . esc_js(__('An error occurred.', 'dwarpal')) . "'); \$button.prop('disabled', false).text('" . esc_js(__('Delete', 'dwarpal')) . "'); } }); }); });"; wp_add_inline_script('dwarpal-admin', $plugin_scanner_script); } // Permissions page if (strpos($hook, 'dwarpal-permissions') !== false) { $permissions_script = "jQuery(document).ready(function($) { $('#dwarpal-scan-permissions').on('click', function() { var button = $(this); button.prop('disabled', true).text('" . esc_js(__('Scanning...', 'dwarpal')) . "'); $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_scan_permissions', nonce: dwarpalAdmin.nonce }, success: function(response) { if (response.success) { var html = '

" . esc_js(__('Scan Results', 'dwarpal')) . "

'; if (response.data.length > 0) { html += '

" . esc_js(__('Found', 'dwarpal')) . " ' + response.data.length + ' " . esc_js(__('permission issues.', 'dwarpal')) . "

'; html += ''; $.each(response.data, function(i, item) { html += ''; }); html += '
" . esc_js(__('File', 'dwarpal')) . "" . esc_js(__('Type', 'dwarpal')) . "" . esc_js(__('Permissions', 'dwarpal')) . "" . esc_js(__('Issue', 'dwarpal')) . "
' + item.path + '' + item.type + '' + item.permissions + '' + item.issue + '
'; } else { html += '

" . esc_js(__('No permission issues found.', 'dwarpal')) . "

'; } $('#dwarpal-permissions-results').html(html); } button.prop('disabled', false).text('" . esc_js(__('Scan Permissions', 'dwarpal')) . "'); } }); }); });"; wp_add_inline_script('dwarpal-admin', $permissions_script); } // Database page if (strpos($hook, 'dwarpal-database') !== false) { $database_script = "jQuery(document).ready(function($) { $('#dwarpal-change-prefix-form').on('submit', function(e) { e.preventDefault(); if (!confirm('" . esc_js(__('Are you sure? This will rename all database tables.', 'dwarpal')) . "')) { return; } $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_change_db_prefix', nonce: dwarpalAdmin.nonce, new_prefix: $('#new_prefix').val() }, success: function(response) { if (response.success) { alert(response.data.message); location.reload(); } else { alert(response.data.message); } } }); }); $('#dwarpal-backup-database').on('click', function() { var button = $(this); button.prop('disabled', true).text('" . esc_js(__('Creating backup...', 'dwarpal')) . "'); $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_backup_database', nonce: dwarpalAdmin.nonce }, success: function(response) { if (response.success) { alert(response.data.message); } else { alert(response.data.message); } button.prop('disabled', false).text('" . esc_js(__('Create Backup', 'dwarpal')) . "'); } }); }); });"; wp_add_inline_script('dwarpal-admin', $database_script); } // Logs page if (strpos($hook, 'dwarpal-logs') !== false) { $logs_style = ".dwarpal-critical-log { background: #fef0f1 !important; } .dwarpal-critical-log:hover { background: #fee2e2 !important; }"; wp_add_inline_style('dwarpal-admin', $logs_style); } // Post-Hack page if (strpos($hook, 'dwarpal-post-hack') !== false) { $post_hack_script = "jQuery(document).ready(function($) {\n\t$('#dwarpal-update-keys, #dwarpal-reset-passwords, #dwarpal-reset-plugins').on('click', function() {\n\t\tvar button = $(this);\n\t\tvar actionName = button.attr('id').replace('dwarpal-', '').replace('-', '_');\n\n\t\tif (!confirm('" . esc_js(__('Are you sure you want to perform this action?', 'dwarpal')) . "')) {\n\t\t\treturn;\n\t\t}\n\n\t\tbutton.prop('disabled', true);\n\n\t\t$.ajax({\n\t\t\turl: ajaxurl,\n\t\t\ttype: 'POST',\n\t\t\tdata: {\n\t\t\t\taction: 'dwarpal_' + actionName,\n\t\t\t\tnonce: '" . esc_js(wp_create_nonce('dwarpal_post_hack')) . "'\n\t\t\t},\n\t\t\tsuccess: function(response) {\n\t\t\t\tif (response.success) {\n\t\t\t\t\talert(response.data.message);\n\t\t\t\t} else {\n\t\t\t\t\talert(response.data.message || '" . esc_js(__('Action failed.', 'dwarpal')) . "');\n\t\t\t\t}\n\t\t\t\tbutton.prop('disabled', false);\n\t\t\t}\n\t\t});\n\t});\n});"; wp_add_inline_script('dwarpal-admin', $post_hack_script); } // Malware page if (strpos($hook, 'dwarpal-malware') !== false) { $malware_script = "jQuery(document).ready(function($) {\n\t$('#dwarpal-scan-now').on('click', function() {\n\t\tvar button = $(this);\n\t\tbutton.prop('disabled', true).text('" . esc_js(__('Scanning...', 'dwarpal')) . "');\n\n\t\t$.ajax({\n\t\t\turl: ajaxurl,\n\t\t\ttype: 'POST',\n\t\t\tdata: {\n\t\t\t\taction: 'dwarpal_scan_now',\n\t\t\t\tnonce: '" . esc_js(wp_create_nonce('dwarpal_scan')) . "'\n\t\t\t},\n\t\t\tsuccess: function(response) {\n\t\t\t\tif (response.success) {\n\t\t\t\t\tlocation.reload();\n\t\t\t\t} else {\n\t\t\t\t\talert((response.data && response.data.message) ? response.data.message : '" . esc_js(__('Scan failed.', 'dwarpal')) . "');\n\t\t\t\t}\n\t\t\t\tbutton.prop('disabled', false).text('" . esc_js(__('Scan Now', 'dwarpal')) . "');\n\t\t\t}\n\t\t});\n\t});\n});"; wp_add_inline_script('dwarpal-admin', $malware_script); } // SEO Protection — Bot View Scanner (no Google scraping). if (strpos($hook, 'dwarpal-seo-protection') !== false) { $cloaking_nonce = wp_create_nonce('dwarpal_cloaking_scan'); $seo_script = "jQuery(document).ready(function($) { $('#dwarpal-run-cloaking-scan').on('click', function() { var btn = $(this); var status = $('#dwarpal-cloaking-scan-status'); btn.prop('disabled', true); status.text('" . esc_js(__('Scanning…', 'dwarpal')) . "'); $.ajax({ url: dwarpalAdmin.ajaxurl, type: 'POST', data: { action: 'dwarpal_run_cloaking_scan', nonce: '" . esc_js($cloaking_nonce) . "' }, success: function(response) { btn.prop('disabled', false); if (response.success) { status.text('" . esc_js(__('Scan complete. Reloading…', 'dwarpal')) . "'); location.reload(); } else { var msg = (response.data && response.data.message) ? response.data.message : '" . esc_js(__('Scan failed.', 'dwarpal')) . "'; status.text(msg); } }, error: function() { btn.prop('disabled', false); status.text('" . esc_js(__('Request failed.', 'dwarpal')) . "'); } }); }); });"; wp_add_inline_script('dwarpal-admin', $seo_script); } // Threat Defense page if (strpos($hook, 'dwarpal-threat-defense') !== false) { $threat_script = "jQuery(function($){\n" . "\t$('#dwarpal-sync-signatures').on('click', function(){\n" . "\t\tvar \$s = $('#dwarpal-sync-signatures-status');\n" . "\t\t\$s.text('" . esc_js(__('Syncing…', 'dwarpal')) . "');\n" . "\t\t$.post(ajaxurl, { action: 'dwarpal_sync_signatures', nonce: dwarpalAdmin.nonce }, function(resp){\n" . "\t\t\t\$s.text(resp && resp.success ? (resp.data.message || 'OK') : ((resp && resp.data && resp.data.message) ? resp.data.message : 'Error'));\n" . "\t\t}).fail(function(){ \$s.text('Error'); });\n" . "\t});\n" . "\t$('#dwarpal-send-digest').on('click', function(){\n" . "\t\tvar \$s = $('#dwarpal-send-digest-status');\n" . "\t\t\$s.text('" . esc_js(__('Sending…', 'dwarpal')) . "');\n" . "\t\t$.post(ajaxurl, { action: 'dwarpal_send_alert_digest', nonce: dwarpalAdmin.nonce }, function(resp){\n" . "\t\t\t\$s.text(resp && resp.success ? (resp.data.message || 'OK') : ((resp && resp.data && resp.data.message) ? resp.data.message : 'Error'));\n" . "\t\t}).fail(function(){ \$s.text('Error'); });\n" . "\t});\n" . "});"; wp_add_inline_script('dwarpal-admin', $threat_script); } } /** * Register settings * * @return void */ public function register_settings() { register_setting('dwarpal_settings', 'dwarpal_firewall_enabled', array('sanitize_callback' => 'rest_sanitize_boolean')); register_setting('dwarpal_settings', 'dwarpal_login_security_enabled', array('sanitize_callback' => 'rest_sanitize_boolean')); register_setting('dwarpal_settings', 'dwarpal_login_attempts_limit', array('sanitize_callback' => 'absint')); register_setting('dwarpal_settings', 'dwarpal_login_lockout_duration', array('sanitize_callback' => 'absint')); register_setting('dwarpal_settings', 'dwarpal_audit_log_enabled', array('sanitize_callback' => 'rest_sanitize_boolean')); } /** * Render dashboard page * * @return void */ public function render_dashboard_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/dashboard.php'; } /** * Render Security Report (full page) from last scan * * @return void */ public function render_scan_report_page() { $report = get_option('dwarpal_last_scan_report', array()); $report_time = get_option('dwarpal_last_scan_report_time', ''); include DWARPAL_PLUGIN_DIR . 'admin/views/scan-report.php'; } /** * Render Threat Defense page. * * @return void */ public function render_threat_defense_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/threat-defense.php'; } /** * Render OWASP coverage page. * * @return void */ public function render_owasp_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/owasp-report.php'; } /** * AJAX: save scan report after scan completes * * @return void */ public function ajax_save_scan_report() { if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('Permission denied.', 'dwarpal'))); } $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''; if (!wp_verify_nonce($nonce, 'dwarpal_admin_nonce')) { wp_send_json_error(array('message' => __('Security check failed.', 'dwarpal'))); } $raw = isset($_POST['report_data']) ? wp_unslash($_POST['report_data']) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- decoded and sanitized below $data = json_decode($raw, true); if (!is_array($data)) { wp_send_json_error(array('message' => __('Invalid report data.', 'dwarpal'))); } // Normalize: each key may have inner feature data (e.g. data.firewall) or be the feature itself $report = array(); $allowed_keys = array('firewall', 'login_security', 'security_logs', 'seo_protection', 'malware', 'file_integrity', 'hardening', 'two_factor', 'spam_prevention', 'ip_blocking', 'security_headers', 'post_hack', 'permissions', 'database', 'vulnerability', 'blocklist', 'live_traffic', 'http_errors', 'maintenance', 'system_info', 'plugin_scanner', 'security_recommendations'); foreach ($data as $key => $value) { if (!in_array($key, $allowed_keys, true)) { continue; } if (!is_array($value)) { continue; } // If this step's data contains a key with same name, use that as the feature payload if (isset($value[ $key ]) && is_array($value[ $key ])) { $report[ $key ] = $this->dwarpal_sanitize_report_payload($value[ $key ]); } else { $report[ $key ] = $this->dwarpal_sanitize_report_payload($value); } } update_option('dwarpal_last_scan_report', $report); update_option('dwarpal_last_scan_report_time', current_time('mysql')); wp_send_json_success(array('report_url' => admin_url('admin.php?page=dwarpal-scan-report'))); } /** * Sanitize scan report payload before persisting to the database. * * @param mixed $value Untrusted decoded JSON value. * @return mixed Sanitized value safe for update_option(). */ private function dwarpal_sanitize_report_payload($value) { if (is_array($value)) { $sanitized = array(); foreach ($value as $k => $v) { // Preserve numeric keys, sanitize string keys. $key = is_int($k) ? $k : sanitize_key((string) $k); $sanitized[ $key ] = $this->dwarpal_sanitize_report_payload($v); } return $sanitized; } if (is_bool($value) || is_null($value)) { return $value; } if (is_int($value)) { return $value; } if (is_float($value)) { return (float) $value; } // Everything else becomes plain text. return sanitize_text_field((string) $value); } /** * Render firewall page * * @return void */ public function render_firewall_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/firewall.php'; } /** * Render login security page * * @return void */ public function render_login_security_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/login-security.php'; } /** * Render logs page * * @return void */ public function render_logs_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/logs.php'; } /** * Render settings page * * @return void */ public function render_settings_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/settings.php'; } /** * Render malware scanner page * * @return void */ public function render_malware_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/malware.php'; } /** * Render file integrity page * * @return void */ public function render_integrity_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/integrity.php'; } /** * Render hardening page * * @return void */ public function render_hardening_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/hardening.php'; } /** * Render 2FA page * * @return void */ public function render_2fa_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/2fa.php'; } /** * Render spam prevention page * * @return void */ public function render_spam_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/spam.php'; } /** * Render IP blocking page * * @return void */ public function render_ip_blocking_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/ip-blocking.php'; } /** * Render security headers page * * @return void */ public function render_headers_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/headers.php'; } /** * Render post-hack page * * @return void */ public function render_post_hack_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/post-hack.php'; } /** * Render file permissions page * * @return void */ public function render_permissions_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/permissions.php'; } /** * Render database security page * * @return void */ public function render_database_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/database.php'; } /** * Render vulnerabilities page * * @return void */ public function render_vulnerabilities_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/vulnerabilities.php'; } /** * Render blocklist page * * @return void */ public function render_blocklist_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/blocklist.php'; } /** * Render live traffic page * * @return void */ public function render_traffic_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/traffic.php'; } /** * Render HTTP errors page * * @return void */ public function render_http_errors_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/http-errors.php'; } /** * Render maintenance mode page * * @return void */ public function render_maintenance_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/maintenance.php'; } /** * Render system info page * * @return void */ public function render_system_info_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/system-info.php'; } /** * Render SEO protection page * * @return void */ public function render_seo_protection_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/seo-protection.php'; } /** * Render Critical Options Monitor page * * @return void */ public function render_critical_options_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/critical-options.php'; } /** * Render Outbound Monitor page * * @return void */ public function render_outbound_monitor_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/outbound-monitor.php'; } /** * Render plugin scanner page * * @return void */ public function render_plugin_scanner_page() { include DWARPAL_PLUGIN_DIR . 'admin/views/plugin-scanner.php'; } /** * AJAX handler for comprehensive website scan * * @return void */ public function ajax_scan_website() { // Ensure clean JSON output (no whitespace or notices) if (ob_get_level()) { ob_clean(); } header('Content-Type: application/json; charset=utf-8'); // Heavy scan steps (malware, integrity) can take several minutes on large sites. if (function_exists('wp_raise_memory_limit')) { wp_raise_memory_limit('admin'); } // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- long-running security scan needs extended execution time. @set_time_limit(120); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- long-running security scan needs extended execution time. @ini_set('max_execution_time', '120'); $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''; if (!wp_verify_nonce($nonce, 'dwarpal_admin_nonce')) { wp_send_json_error(array('message' => __('Security check failed. Please refresh the page and try again.', 'dwarpal'))); return; } if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('Permission denied.', 'dwarpal'))); return; } $scan_step = isset($_POST['scan_step']) ? sanitize_text_field(wp_unslash($_POST['scan_step'])) : 'start'; $results = array(); try { // Define all scan steps with progress percentages $scan_steps = array( 'start' => 2, 'firewall' => 5, 'login_security' => 8, 'security_logs' => 10, 'seo_protection' => 12, 'malware' => 15, 'file_integrity' => 20, 'hardening' => 25, 'two_factor' => 28, 'spam_prevention' => 30, 'ip_blocking' => 32, 'security_headers' => 35, 'post_hack' => 37, 'permissions' => 40, 'database' => 45, 'vulnerability' => 50, 'blocklist' => 55, 'live_traffic' => 58, 'http_errors' => 60, 'maintenance' => 62, 'system_info' => 65, 'plugin_scanner' => 72, 'security_recommendations' => 90, 'complete' => 100 ); switch ($scan_step) { case 'start': $results = array( 'step' => 'firewall', 'status' => 'scanning', 'message' => __('Checking Firewall status...', 'dwarpal'), 'progress' => $scan_steps['firewall'] ); break; case 'firewall': $firewall_enabled = absint(get_option('dwarpal_firewall_enabled', 1)); $results = array( 'step' => 'login_security', 'status' => 'scanning', 'message' => __('Checking Login Security...', 'dwarpal'), 'progress' => $scan_steps['login_security'], 'firewall' => array( 'enabled' => $firewall_enabled, 'status' => $firewall_enabled ? 'enabled' : 'disabled', 'recommendation' => $firewall_enabled ? '' : 'Enable Firewall for better protection' ) ); break; case 'login_security': $login_security_enabled = absint(get_option('dwarpal_login_security_enabled', 1)); $login_attempts_limit = get_option('dwarpal_login_attempts_limit', 5); $results = array( 'step' => 'security_logs', 'status' => 'scanning', 'message' => __('Checking Security Logs...', 'dwarpal'), 'progress' => $scan_steps['security_logs'], 'login_security' => array( 'enabled' => $login_security_enabled, 'status' => $login_security_enabled ? 'enabled' : 'disabled', 'login_attempts_limit' => $login_attempts_limit, 'recommendation' => $login_security_enabled ? '' : 'Enable Login Security to prevent brute force attacks' ) ); break; case 'security_logs': $audit_log_enabled = absint(get_option('dwarpal_audit_log_enabled', 1)); global $wpdb; $logs_table = $wpdb->prefix . 'dwarpal_logs'; $total_logs = Dwarpal_Install::estimated_rows($logs_table); $results = array( 'step' => 'seo_protection', 'status' => 'scanning', 'message' => __('Checking SEO Protection...', 'dwarpal'), 'progress' => $scan_steps['seo_protection'], 'security_logs' => array( 'enabled' => $audit_log_enabled, 'status' => $audit_log_enabled ? 'enabled' : 'disabled', 'total_logs' => $total_logs, 'recommendation' => $audit_log_enabled ? '' : 'Enable Security Logs to track security events' ) ); break; case 'seo_protection': $seo_enabled = (bool) get_option('dwarpal_seo_protection_enabled', true); $last_scan = class_exists('Dwarpal_Cloaking_Scanner') ? Dwarpal_Cloaking_Scanner::get_last_scan() : null; $scan_time = (is_array($last_scan) && !empty($last_scan['scan_time'])) ? sanitize_text_field((string) $last_scan['scan_time']) : ''; $status = $seo_enabled ? 'enabled' : 'disabled'; $issue_count = (is_array($last_scan) && isset($last_scan['issue_count'])) ? (int) $last_scan['issue_count'] : 0; $scan_status = (is_array($last_scan) && !empty($last_scan['status'])) ? sanitize_text_field((string) $last_scan['status']) : ''; $results = array( 'step' => 'malware', 'status' => 'scanning', 'message' => __('Running quick malware scan...', 'dwarpal'), 'progress' => $scan_steps['malware'], 'seo_protection' => array( 'enabled' => $seo_enabled, 'status' => $status, 'last_scan_time' => $scan_time, 'last_scan_status' => $scan_status, 'issues' => $issue_count, 'recommendation' => $seo_enabled ? '' : 'Enable SEO Protection to detect SEO spam and cloaking', ), ); break; case 'malware': $malware_scanner = new Dwarpal_Malware_Scanner(); delete_transient('dwarpal_dashboard_malware_partial'); $malware_results = $malware_scanner->perform_dashboard_quick_scan(); $malware_results = $malware_scanner->trim_threats_for_response($malware_results); $results = array( 'step' => 'vulnerability', 'status' => 'scanning', /* translators: %d: Number of files scanned */ 'message' => sprintf(__('Quick malware scan completed (%d files). Checking updates...', 'dwarpal'), $malware_results['files_scanned']), 'progress' => $scan_steps['vulnerability'], 'malware' => $malware_results, 'quick_scan' => true, ); break; case 'malware_themes': case 'malware_content': // Legacy step names from older clients — continue with quick flow. $malware_scanner = new Dwarpal_Malware_Scanner(); $malware_results = $malware_scanner->trim_threats_for_response( $malware_scanner->perform_dashboard_quick_scan() ); $results = array( 'step' => 'vulnerability', 'status' => 'scanning', 'message' => __('Checking for available updates...', 'dwarpal'), 'progress' => $scan_steps['vulnerability'], 'malware' => $malware_results, 'quick_scan' => true, ); break; case 'vulnerability': if (class_exists('Dwarpal_Vulnerability_Scanner')) { $vuln_scanner = new Dwarpal_Vulnerability_Scanner(); $vuln_results = $vuln_scanner->perform_quick_vulnerability_scan(); $results = array( 'step' => 'file_integrity', 'status' => 'scanning', 'message' => __('Checking critical file integrity...', 'dwarpal'), 'progress' => $scan_steps['file_integrity'], 'vulnerability' => array('success' => true, 'vulnerabilities' => $vuln_results, 'count' => count($vuln_results), 'quick_scan' => true) ); } else { $results = array( 'step' => 'file_integrity', 'status' => 'scanning', 'message' => __('Checking file integrity...', 'dwarpal'), 'progress' => 50, 'vulnerability' => array('success' => false, 'message' => __('Scanner not available', 'dwarpal')) ); } break; case 'file_integrity': if (class_exists('Dwarpal_File_Integrity')) { $integrity = new Dwarpal_File_Integrity(); $integrity_results = $integrity->perform_quick_integrity_check(); $results = array( 'step' => 'permissions', 'status' => 'scanning', 'message' => __('Checking file permissions...', 'dwarpal'), 'progress' => $scan_steps['permissions'], 'file_integrity' => array('success' => true, 'results' => $integrity_results, 'quick_scan' => true) ); } else { $results = array( 'step' => 'permissions', 'status' => 'scanning', 'message' => __('Checking file permissions...', 'dwarpal'), 'progress' => 70, 'file_integrity' => array('success' => false, 'message' => __('Scanner not available', 'dwarpal')) ); } break; case 'permissions': if (class_exists('Dwarpal_File_Permissions')) { $permissions = new Dwarpal_File_Permissions(); $perm_results = $permissions->scan_permissions_quick(); $results = array( 'step' => 'hardening', 'status' => 'scanning', 'message' => __('Checking Hardening...', 'dwarpal'), 'progress' => $scan_steps['hardening'], 'permissions' => array('success' => true, 'issues' => $perm_results, 'count' => count($perm_results), 'quick_scan' => true) ); } else { $results = array( 'step' => 'hardening', 'status' => 'scanning', 'message' => __('Checking Hardening...', 'dwarpal'), 'progress' => $scan_steps['hardening'], 'permissions' => array('success' => false, 'message' => __('Scanner not available', 'dwarpal')) ); } break; case 'hardening': $hardening_status = $this->check_hardening_status(); $results = array( 'step' => 'two_factor', 'status' => 'scanning', 'message' => __('Checking 2FA status...', 'dwarpal'), 'progress' => $scan_steps['two_factor'], 'hardening' => $hardening_status ); break; case 'two_factor': // Check if 2FA is actually implemented and enabled $two_factor_enabled = get_option('dwarpal_2fa_enabled', false); $two_factor_implemented = class_exists('Dwarpal_Two_Factor'); // Check if any users have 2FA enabled $users_with_2fa = 0; if ($two_factor_implemented) { $all_users = get_users(array('fields' => 'ID')); foreach ($all_users as $user_id) { $user_2fa = get_user_meta($user_id, 'dwarpal_2fa_enabled', true); if ($user_2fa) { $users_with_2fa++; } } } $results = array( 'step' => 'spam_prevention', 'status' => 'scanning', 'message' => __('Checking Spam Prevention...', 'dwarpal'), 'progress' => $scan_steps['spam_prevention'], 'two_factor' => array( 'enabled' => $two_factor_enabled, 'implemented' => $two_factor_implemented, 'users_with_2fa' => $users_with_2fa, 'status' => $two_factor_enabled && $users_with_2fa > 0 ? 'enabled' : 'disabled', 'recommendation' => ($two_factor_enabled && $users_with_2fa > 0) ? '' : 'Enable Two-Factor Authentication for enhanced login security. Configure 2FA for admin users to prevent unauthorized access.' ) ); break; case 'spam_prevention': $spam_prevention_enabled = get_option('dwarpal_spam_prevention_enabled', true); $results = array( 'step' => 'ip_blocking', 'status' => 'scanning', 'message' => __('Checking IP Blocking...', 'dwarpal'), 'progress' => $scan_steps['ip_blocking'], 'spam_prevention' => array( 'enabled' => $spam_prevention_enabled, 'status' => $spam_prevention_enabled ? 'enabled' : 'disabled', 'recommendation' => $spam_prevention_enabled ? '' : 'Enable Spam Prevention to block spam comments and registrations' ) ); break; case 'ip_blocking': $ip_blocking_enabled = get_option('dwarpal_ip_blocking_enabled', true); $wpadmin_ip_restriction = get_option('dwarpal_wpadmin_ip_restriction', false); $blocked_list = get_option('dwarpal_permanently_blocked_ips', array()); $blocked_ips = is_array($blocked_list) ? count($blocked_list) : 0; $results = array( 'step' => 'security_headers', 'status' => 'scanning', 'message' => __('Checking Security Headers...', 'dwarpal'), 'progress' => $scan_steps['security_headers'], 'ip_blocking' => array( 'enabled' => $ip_blocking_enabled, 'status' => $ip_blocking_enabled ? 'enabled' : 'disabled', 'blocked_ips' => $blocked_ips, 'wpadmin_ip_restriction' => $wpadmin_ip_restriction, 'recommendation' => $ip_blocking_enabled ? '' : 'Enable IP Blocking to block malicious IP addresses' ), 'wpadmin_ip_restriction' => array( 'enabled' => $wpadmin_ip_restriction, 'status' => $wpadmin_ip_restriction ? 'enabled' : 'disabled', 'recommendation' => $wpadmin_ip_restriction ? '' : 'Enable IP restriction for wp-admin folder to prevent unauthorized admin access' ) ); break; case 'security_headers': $security_headers_enabled = get_option('dwarpal_security_headers_enabled', true); $headers_status = $this->check_security_headers_status(); $results = array( 'step' => 'post_hack', 'status' => 'scanning', 'message' => __('Checking Post-Hack Recovery...', 'dwarpal'), 'progress' => $scan_steps['post_hack'], 'security_headers' => array_merge($headers_status, array( 'enabled' => $security_headers_enabled, 'status' => $security_headers_enabled ? 'enabled' : 'disabled' )) ); break; case 'post_hack': $post_hack_enabled = get_option('dwarpal_post_hack_enabled', true); $results = array( 'step' => 'database', 'status' => 'scanning', 'message' => __('Checking Database Security...', 'dwarpal'), 'progress' => $scan_steps['database'], 'post_hack' => array( 'enabled' => $post_hack_enabled, 'status' => $post_hack_enabled ? 'enabled' : 'disabled', 'recommendation' => $post_hack_enabled ? '' : 'Enable Post-Hack Recovery tools for quick recovery after security incidents' ) ); break; case 'database': $database_security = $this->check_database_security(); $results = array( 'step' => 'blocklist', 'status' => 'scanning', 'message' => __('Checking Blocklist...', 'dwarpal'), 'progress' => $scan_steps['blocklist'], 'database' => $database_security ); break; case 'blocklist': $blocklist_enabled = (bool) get_option('dwarpal_blocklist_monitoring_enabled', false); if ($blocklist_enabled && class_exists('Dwarpal_Blocklist_Monitor')) { $blocklist_monitor = new Dwarpal_Blocklist_Monitor(); $blocklist_status = $blocklist_monitor->check_blocklist_status(); $results = array( 'step' => 'live_traffic', 'status' => 'scanning', 'message' => __('Checking Live Traffic...', 'dwarpal'), 'progress' => $scan_steps['live_traffic'], 'blocklist' => array( 'enabled' => $blocklist_enabled, 'status' => $blocklist_status, 'blocked' => count(array_filter($blocklist_status, function($v) { return $v === 'blocked'; })) ) ); } else { $results = array( 'step' => 'live_traffic', 'status' => 'scanning', 'message' => __('Checking Live Traffic...', 'dwarpal'), 'progress' => $scan_steps['live_traffic'], 'blocklist' => array( 'enabled' => $blocklist_enabled, 'status' => $blocklist_enabled ? 'not_available' : 'disabled' ) ); } break; case 'live_traffic': $live_traffic_enabled = get_option('dwarpal_live_traffic_enabled', true); global $wpdb; $traffic_table = $wpdb->prefix . 'dwarpal_traffic'; $recent_traffic = Dwarpal_Install::estimated_rows($traffic_table); $results = array( 'step' => 'http_errors', 'status' => 'scanning', 'message' => __('Checking HTTP Errors...', 'dwarpal'), 'progress' => $scan_steps['http_errors'], 'live_traffic' => array( 'enabled' => $live_traffic_enabled, 'status' => $live_traffic_enabled ? 'enabled' : 'disabled', 'recent_traffic' => $recent_traffic, 'recommendation' => $live_traffic_enabled ? '' : 'Enable Live Traffic monitoring to track real-time website activity' ) ); break; case 'http_errors': $http_errors_enabled = get_option('dwarpal_http_error_logging_enabled', true); global $wpdb; $http_errors_table = $wpdb->prefix . 'dwarpal_http_errors'; $recent_errors = Dwarpal_Install::estimated_rows($http_errors_table); $results = array( 'step' => 'maintenance', 'status' => 'scanning', 'message' => __('Checking Maintenance Mode...', 'dwarpal'), 'progress' => $scan_steps['maintenance'], 'http_errors' => array( 'enabled' => $http_errors_enabled, 'status' => $http_errors_enabled ? 'enabled' : 'disabled', 'recent_errors' => $recent_errors, 'recommendation' => $http_errors_enabled ? '' : 'Enable HTTP Error Logging to monitor 404 and other errors' ) ); break; case 'maintenance': $maintenance_enabled = get_option('dwarpal_maintenance_mode_enabled', false); $results = array( 'step' => 'system_info', 'status' => 'scanning', 'message' => __('Checking System Info...', 'dwarpal'), 'progress' => $scan_steps['system_info'], 'maintenance' => array( 'enabled' => $maintenance_enabled, 'status' => $maintenance_enabled ? 'enabled' : 'disabled', 'recommendation' => $maintenance_enabled ? 'Maintenance mode is active' : 'Maintenance mode is available for site updates' ) ); break; case 'system_info': $system_info = $this->check_system_info(); $results = array( 'step' => 'plugin_scanner', 'status' => 'scanning', 'message' => __('Scanning Plugins...', 'dwarpal'), 'progress' => $scan_steps['plugin_scanner'], 'system_info' => $system_info ); break; case 'plugin_scanner': if (class_exists('Dwarpal_Plugin_Scanner')) { $plugin_scanner = new Dwarpal_Plugin_Scanner(); $plugin_results = $plugin_scanner->scan_plugins_quick(); $results = array( 'step' => 'security_recommendations', 'status' => 'scanning', 'message' => __('Generating Security Recommendations...', 'dwarpal'), 'progress' => $scan_steps['security_recommendations'], 'plugin_scanner' => $plugin_results ); } else { $results = array( 'step' => 'security_recommendations', 'status' => 'scanning', 'message' => __('Generating Security Recommendations...', 'dwarpal'), 'progress' => $scan_steps['security_recommendations'], 'plugin_scanner' => array('success' => false, 'message' => __('Scanner not available', 'dwarpal')) ); } break; case 'security_recommendations': // Generate comprehensive security recommendations $recommendations = $this->generate_security_recommendations(); $results = array( 'step' => 'complete', 'status' => 'complete', 'message' => __('Quick scan complete! Open Malware Scanner or File Integrity for a deep scan.', 'dwarpal'), 'progress' => $scan_steps['complete'], 'security_recommendations' => $recommendations, 'quick_scan' => true, ); break; default: wp_send_json_error(array('message' => __('Invalid scan step.', 'dwarpal'))); return; } } catch (Exception $e) { wp_send_json_error(array('message' => __('Scan error: ', 'dwarpal') . $e->getMessage())); return; } catch (Error $e) { wp_send_json_error(array('message' => __('Scan error: ', 'dwarpal') . $e->getMessage())); return; } wp_send_json_success($results); } /** * Check hardening status * * @return array */ private function check_hardening_status() { $hardening_features = array( 'block_php_uploads' => get_option('dwarpal_block_php_uploads', true), 'disable_file_editing' => get_option('dwarpal_disable_file_editing', true), 'disable_xmlrpc' => get_option('dwarpal_disable_xmlrpc', true), 'hide_wp_version' => get_option('dwarpal_hide_wp_version', true) ); $enabled_count = count(array_filter($hardening_features)); $total_count = count($hardening_features); return array( 'enabled' => $enabled_count > 0, 'status' => $enabled_count == $total_count ? 'fully_secured' : 'partially_secured', 'enabled_features' => $enabled_count, 'total_features' => $total_count, 'features' => $hardening_features, 'recommendation' => $enabled_count == $total_count ? '' : 'Enable all hardening features for maximum security' ); } /** * Check security headers status * * @return array */ private function check_security_headers_status() { $headers = array( 'x_frame_options' => get_option('dwarpal_header_x_frame_options', true), 'x_content_type' => get_option('dwarpal_header_x_content_type', true), 'x_xss_protection' => get_option('dwarpal_header_x_xss_protection', true), 'referrer_policy' => get_option('dwarpal_header_referrer_policy', true), 'hsts' => get_option('dwarpal_header_hsts', false), 'csp' => get_option('dwarpal_header_csp', false) ); $enabled_count = count(array_filter($headers)); $total_count = count($headers); return array( 'enabled_count' => $enabled_count, 'total_count' => $total_count, 'headers' => $headers, 'recommendation' => $enabled_count == $total_count ? '' : 'Enable all security headers for better protection' ); } /** * Check database security * * @return array */ private function check_database_security() { global $wpdb; $table_prefix = $wpdb->prefix; $default_prefix = 'wp_'; $is_default = ($table_prefix === $default_prefix); return array( 'table_prefix' => $table_prefix, 'is_default_prefix' => $is_default, 'enabled' => !$is_default, 'status' => $is_default ? 'needs_attention' : 'secure', 'recommendation' => $is_default ? 'Change database table prefix from default "wp_" for better security' : 'Database prefix is secure' ); } /** * Check system info * * @return array */ private function check_system_info() { $wp_version = get_bloginfo('version'); $php_version = phpversion(); $mysql_version = $GLOBALS['wpdb']->db_version(); return array( 'wordpress_version' => $wp_version, 'php_version' => $php_version, 'mysql_version' => $mysql_version, 'php_version_ok' => version_compare($php_version, '7.4', '>='), 'recommendation' => version_compare($php_version, '7.4', '<') ? 'Upgrade PHP to version 7.4 or higher' : 'System versions are adequate' ); } /** * Generate comprehensive security recommendations * * @return array */ private function generate_security_recommendations() { $recommendations = array(); // Check all features and generate recommendations if (!absint(get_option('dwarpal_firewall_enabled', 1))) { $recommendations[] = array( 'priority' => 'high', 'feature' => 'Firewall', 'issue' => 'Firewall is disabled', 'recommendation' => 'Enable Firewall to block malicious requests automatically', 'action' => admin_url('admin.php?page=dwarpal-firewall') ); } if (!absint(get_option('dwarpal_login_security_enabled', 1))) { $recommendations[] = array( 'priority' => 'high', 'feature' => 'Login Security', 'issue' => 'Login Security is disabled', 'recommendation' => 'Enable Login Security to prevent brute force attacks', 'action' => admin_url('admin.php?page=dwarpal-login') ); } // Check 2FA status $two_factor_enabled = get_option('dwarpal_2fa_enabled', false); $two_factor_implemented = class_exists('Dwarpal_Two_Factor'); $users_with_2fa = 0; if ($two_factor_implemented) { $all_users = get_users(array('fields' => 'ID', 'role' => 'administrator')); foreach ($all_users as $user_id) { $user_2fa = get_user_meta($user_id, 'dwarpal_2fa_enabled', true); if ($user_2fa) { $users_with_2fa++; } } } if (!$two_factor_enabled || $users_with_2fa === 0) { $recommendations[] = array( 'priority' => 'high', 'feature' => 'Two-Factor Authentication', 'issue' => '2FA is not enabled or no admin users have 2FA configured', 'recommendation' => 'Enable Two-Factor Authentication for all administrator accounts to prevent unauthorized login attempts', 'action' => admin_url('admin.php?page=dwarpal-2fa') ); } // Check wp-admin IP restriction $wpadmin_ip_restriction = get_option('dwarpal_wpadmin_ip_restriction', false); if (!$wpadmin_ip_restriction) { $recommendations[] = array( 'priority' => 'high', 'feature' => 'WP-Admin IP Restriction', 'issue' => 'WP-Admin folder is not IP restricted', 'recommendation' => 'Restrict wp-admin access to specific IP addresses to prevent unauthorized admin access. This adds an extra layer of security.', 'action' => admin_url('admin.php?page=dwarpal-ip-blocking') ); } $hardening = $this->check_hardening_status(); if ($hardening['enabled_features'] < $hardening['total_features']) { $recommendations[] = array( 'priority' => 'medium', 'feature' => 'Hardening', 'issue' => 'Not all hardening features are enabled', 'recommendation' => 'Enable all hardening features for maximum WordPress security', 'action' => admin_url('admin.php?page=dwarpal-hardening') ); } global $wpdb; $table_prefix = $wpdb->prefix; if ($table_prefix === 'wp_') { $recommendations[] = array( 'priority' => 'medium', 'feature' => 'Database Security', 'issue' => 'Using default database prefix', 'recommendation' => 'Change database table prefix from default "wp_" to a custom prefix', 'action' => admin_url('admin.php?page=dwarpal-database') ); } $headers = $this->check_security_headers_status(); if ($headers['enabled_count'] < $headers['total_count']) { $recommendations[] = array( 'priority' => 'low', 'feature' => 'Security Headers', 'issue' => 'Not all security headers are enabled', 'recommendation' => 'Enable all security headers for better browser protection', 'action' => admin_url('admin.php?page=dwarpal-headers') ); } return array( 'total' => count($recommendations), 'high_priority' => count(array_filter($recommendations, function($r) { return $r['priority'] === 'high'; })), 'medium_priority' => count(array_filter($recommendations, function($r) { return $r['priority'] === 'medium'; })), 'low_priority' => count(array_filter($recommendations, function($r) { return $r['priority'] === 'low'; })), 'recommendations' => $recommendations ); } /** * AJAX handler for website backup * * @return void */ public function ajax_backup_website() { check_ajax_referer('dwarpal_admin_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('Permission denied.', 'dwarpal'))); } $backup_step = isset($_POST['backup_step']) ? sanitize_text_field(wp_unslash($_POST['backup_step'])) : 'start'; $results = array(); switch ($backup_step) { case 'start': $results = array( 'step' => 'database', 'status' => 'backing_up', 'message' => __('Starting database backup...', 'dwarpal'), 'progress' => 10 ); break; case 'database': // Create database backup $db_security = new Dwarpal_Database_Security(); $db_backup = $db_security->backup_database(); if (!$db_backup['success']) { wp_send_json_error(array('message' => __('Failed to create database backup.', 'dwarpal'))); } $results = array( 'step' => 'files', 'status' => 'backing_up', 'message' => __('Database backup completed. Starting file backup...', 'dwarpal'), 'progress' => 50, 'database' => $db_backup, 'db_backup_filename' => isset($db_backup['filename']) ? $db_backup['filename'] : '', ); break; case 'files': $files_backup = $this->backup_files(); $db_filename = isset($_POST['db_backup_filename']) ? sanitize_file_name(wp_unslash($_POST['db_backup_filename'])) : ''; $backup_info = array( 'database' => array( 'success' => true, 'filename' => $db_filename, 'relative_path' => $db_filename ? Dwarpal_Storage::get_display_path('secure-backups', $db_filename) : '', ), 'files' => $files_backup, 'timestamp' => current_time('mysql'), ); // Save backup info update_option('dwarpal_last_website_backup', $backup_info); $security = new Dwarpal_Security(); $security->log_event('website_backup', 'Complete website backup created'); $results = array( 'step' => 'complete', 'status' => 'complete', 'message' => __('Backup completed successfully!', 'dwarpal'), 'progress' => 100, 'backup_info' => $backup_info ); break; default: wp_send_json_error(array('message' => __('Invalid backup step.', 'dwarpal'))); } wp_send_json_success($results); } /** * Backup website files (metadata only — no wp-config copy). * * @return array */ private function backup_files() { $backup_dir = Dwarpal_Storage::get_directory('backups'); if (!$backup_dir) { return array( 'success' => false, 'message' => __('Failed to create secure backup directory.', 'dwarpal'), ); } return array( 'success' => true, 'message' => __('Backup directory ready. Use your hosting tools for full file archives.', 'dwarpal'), 'backup_dir_relative' => 'uploads/dwarpal/backups/', 'location' => __('Uploads (protected directory)', 'dwarpal'), 'security_note' => __('This plugin stores database exports only. It does not copy wp-config.php or write .htaccess outside uploads/dwarpal/.', 'dwarpal'), 'files_inventory' => array( 'wp-config.php' => Dwarpal_Paths::wp_config_file() ? 'present' : 'missing', 'uploads' => is_dir(Dwarpal_Paths::uploads_dir()) ? 'present' : 'missing', 'plugins' => is_dir(Dwarpal_Paths::plugins_dir()) ? 'present' : 'missing', 'themes' => is_dir(Dwarpal_Paths::themes_dir()) ? 'present' : 'missing', ), ); } /** * AJAX handler for suggesting and applying fixes * * @return void */ public function ajax_suggest_fix() { check_ajax_referer('dwarpal_admin_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('Permission denied.', 'dwarpal'))); } $fix_type = isset($_POST['fix_type']) ? sanitize_text_field(wp_unslash($_POST['fix_type'])) : ''; $backup_created = isset($_POST['backup_created']) ? filter_var(wp_unslash($_POST['backup_created']), FILTER_VALIDATE_BOOLEAN) : false; if (!$backup_created) { wp_send_json_error(array( 'message' => __('Please create a backup first before applying fixes.', 'dwarpal'), 'requires_backup' => true )); } $result = array('success' => false, 'message' => __('Fix type not specified.', 'dwarpal')); // Apply appropriate fix based on type switch ($fix_type) { case 'malware': // Suggest malware removal actions $result = array( 'success' => true, 'message' => __('Malware fix suggestions generated. Review the threats and apply fixes manually.', 'dwarpal'), 'actions' => array( 'Delete suspicious files', 'Restore from clean backup', 'Update all plugins and themes', 'Change all passwords' ) ); break; case 'vulnerability': // Suggest vulnerability fixes $result = array( 'success' => true, 'message' => __('Update vulnerable plugins and themes to latest versions.', 'dwarpal'), 'actions' => array( 'Update WordPress core', 'Update all plugins', 'Update all themes', 'Remove unused plugins' ) ); break; case 'permissions': // Fix file permissions if (class_exists('Dwarpal_File_Permissions')) { $permissions = new Dwarpal_File_Permissions(); $scan_results = $permissions->scan_permissions(); if (!empty($scan_results)) { $paths = array_column($scan_results, 'path'); $fix_result = $permissions->fix_permissions($paths); $result = array( 'success' => true, 'message' => __('File permissions fixed.', 'dwarpal'), 'fixed' => count($paths) ); } else { $result = array( 'success' => true, 'message' => __('No permission issues found.', 'dwarpal') ); } } break; default: $result = array( 'success' => true, 'message' => __('General security fixes applied. Review security status.', 'dwarpal') ); } $security = new Dwarpal_Security(); $security->log_event('fix_applied', sprintf('Fix applied: %s', $fix_type)); wp_send_json_success($result); } }