plugin = $plugin_instance; } /** * Performs a complete scan of the local environment. * @return array Scan results. */ public function run_local_scan() { $results = []; if (($this->plugin->options['scan_check_php'] ?? '1') !== '0') { $results['php'] = $this->check_php_version(); } else { $results['php'] = [ 'status' => 'skipped', 'message' => __('Skipped by settings.', 'advanced-ip-blocker'), 'current' => phpversion() ]; } if (($this->plugin->options['scan_check_wp'] ?? '1') !== '0') { $results['wordpress'] = $this->check_wp_version(); } else { $results['wordpress'] = [ 'status' => 'skipped', 'message' => __('Skipped by settings.', 'advanced-ip-blocker'), 'current' => get_bloginfo('version') ]; } $results['database'] = $this->check_db_version(); if (($this->plugin->options['scan_check_debug'] ?? '1') !== '0') { $results['debug_mode'] = $this->check_debug_mode(); } else { $results['debug_mode'] = [ 'status' => 'skipped', 'message' => __('Skipped by settings.', 'advanced-ip-blocker') ]; } if (($this->plugin->options['scan_check_updates'] ?? '1') !== '0') { $results['updates'] = $this->check_pending_updates(); } else { $results['updates'] = [ 'plugins' => 0, 'themes' => 0, 'status' => 'skipped', 'plugin_details' => [], 'theme_details' => [], 'details' => [] ]; } if (($this->plugin->options['scan_check_ssl'] ?? '1') !== '0') { $results['ssl'] = $this->check_ssl(); } else { $results['ssl'] = [ 'status' => 'skipped', 'message' => __('Skipped by settings.', 'advanced-ip-blocker') ]; } $results['themes_list'] = $this->get_themes_status_list(); return apply_filters('advaipbl_site_scanner_results', $results); } /** * Get detailed status list of all installed themes. * @return array */ private function get_themes_status_list() { $all_themes = wp_get_themes(); $update_data = get_site_transient('update_themes'); $active_theme = wp_get_theme(); $parent_theme = $active_theme->parent(); $themes_list = []; foreach ($all_themes as $slug => $theme) { $is_active = ($active_theme->get_stylesheet() === $slug); $is_parent = ($parent_theme && $parent_theme->get_stylesheet() === $slug); $is_child = (bool) $theme->parent(); $parent_name = $is_child ? $theme->parent()->get('Name') : ''; $parent_version = $is_child ? $theme->parent()->get('Version') : ''; $has_update = false; $update_version = ''; if (isset($update_data->response[$slug])) { $has_update = true; $update_info = $update_data->response[$slug]; if (is_array($update_info)) { $update_version = $update_info['new_version'] ?? ''; } elseif (is_object($update_info)) { $update_version = $update_info->new_version ?? ''; } } $themes_list[$slug] = [ 'name' => $theme->get('Name'), 'version' => $theme->get('Version'), 'is_active' => $is_active, 'is_parent' => $is_parent, 'is_child' => $is_child, 'parent_name' => $parent_name, 'parent_version' => $parent_version, 'has_update' => $has_update, 'new_version' => $update_version, 'author' => $theme->get('Author'), 'screenshot' => $theme->get_screenshot(), ]; } return $themes_list; } private function check_php_version() { $version = phpversion(); $status = 'critical'; $message = __('End of Life (EOL). Obsolete and without official security support. Update immediately.', 'advanced-ip-blocker'); if (version_compare($version, '8.4', '>=')) { $status = 'good'; $message = __('Actively supported and secure.', 'advanced-ip-blocker'); } elseif (version_compare($version, '8.3', '>=')) { $status = 'good'; $message = __('Supported and secure (Security patches until Dec 2027).', 'advanced-ip-blocker'); } elseif (version_compare($version, '8.2', '>=')) { $status = 'warning'; $message = __('Security updates only (until Dec 2026). Plan upgrading to PHP 8.3+ soon.', 'advanced-ip-blocker'); } return ['current' => $version, 'status' => $status, 'message' => $message]; } private function check_wp_version() { global $wp_version; $core_updates = get_site_transient('update_core'); $status = 'good'; $message = __('You are running the latest version.', 'advanced-ip-blocker'); if (isset($core_updates->updates) && !empty($core_updates->updates)) { foreach ($core_updates->updates as $update) { if ($update->response === 'upgrade') { $status = 'critical'; /* translators: %s is a placeholder */ $message = sprintf(__('Outdated. New version %s is available.', 'advanced-ip-blocker'), $update->current); break; } } } return ['current' => $wp_version, 'status' => $status, 'message' => $message]; } private function check_db_version() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $version = $wpdb->get_var("SELECT VERSION()"); return ['current' => $version, 'status' => 'info', 'message' => __('Database engine version.', 'advanced-ip-blocker')]; } private function check_debug_mode() { if (defined('WP_DEBUG') && WP_DEBUG) { if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG && defined('WP_DEBUG_DISPLAY') && !WP_DEBUG_DISPLAY) { return ['status' => 'warning', 'message' => __('Enabled (Log Only). Good for dev, risky for prod.', 'advanced-ip-blocker')]; } return ['status' => 'critical', 'message' => __('Enabled and displaying errors. Security risk!', 'advanced-ip-blocker')]; } return ['status' => 'good', 'message' => __('Disabled. (Secure)', 'advanced-ip-blocker')]; } private function check_pending_updates() { $plugin_updates = get_site_transient('update_plugins'); $theme_updates = get_site_transient('update_themes'); $p_count = isset($plugin_updates->response) ? count($plugin_updates->response) : 0; $t_count = isset($theme_updates->response) ? count($theme_updates->response) : 0; $status = ($p_count + $t_count === 0) ? 'good' : 'warning'; if ($p_count + $t_count > 5) { $status = 'critical'; } $details = []; if (isset($plugin_updates->response) && is_array($plugin_updates->response)) { $details = array_merge($details, $plugin_updates->response); } if (isset($theme_updates->response) && is_array($theme_updates->response)) { $details = array_merge($details, $theme_updates->response); } return [ 'plugins' => $p_count, 'themes' => $t_count, 'status' => $status, 'plugin_details' => $plugin_updates->response ?? [], 'theme_details' => $theme_updates->response ?? [], 'details' => $details ]; } private function check_ssl() { $site_url = get_site_url(); $is_configured_ssl = (stripos($site_url, 'https://') === 0); return (is_ssl() || $is_configured_ssl) ? ['status' => 'good', 'message' => __('Active.', 'advanced-ip-blocker')] : ['status' => 'critical', 'message' => __('Inactive. Your site is not using HTTPS.', 'advanced-ip-blocker')]; } /** * Scans installed plugins against the remote AIB API. * @return array */ public function check_vulnerabilities_via_api() { if (isset($this->plugin->options['scan_check_vulnerabilities']) && $this->plugin->options['scan_check_vulnerabilities'] === '0') { return ['status' => 'skipped', 'count' => 0, 'details' => [], 'message' => __('Skipped by settings.', 'advanced-ip-blocker')]; } if (! function_exists('get_plugins')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $payload = []; $all_plugins = get_plugins(); foreach ($all_plugins as $path => $data) { $slug = dirname($path); if ($slug === '.') { $slug = basename($path, '.php'); } $payload[$slug] = $data['Version']; } $all_themes = wp_get_themes(); foreach ($all_themes as $slug => $theme) { if ($theme->parent()) { $parent_slug = $theme->get_template(); $parent_version = $theme->parent()->get('Version'); if (!empty($parent_slug) && !empty($parent_version)) { $payload[$parent_slug] = $parent_version; } continue; } $payload[$slug] = $theme->get('Version'); } $api_url = 'https://advaipbl.com/wp-json/aib-scanner/v2/check?v=' . ADVAIPBL_VERSION; $site_hash = hash('sha256', get_site_url()); $headers = [ 'Content-Type' => 'application/json', 'X-AIB-Site-Hash' => $site_hash ]; if (!empty($this->plugin->options['api_token_v3'])) { $api_url = 'https://advaipbl.com/wp-json/aib-api/v3/scanner/check?v=' . ADVAIPBL_VERSION; $headers['Authorization'] = 'Bearer ' . $this->plugin->options['api_token_v3']; $headers['X-AIB-Auth'] = 'Bearer ' . $this->plugin->options['api_token_v3']; } $response = wp_remote_post($api_url, [ 'body' => wp_json_encode($payload), 'headers' => $headers, 'timeout' => 15 ]); if (is_wp_error($response)) { return ['status' => 'error', 'message' => 'API Error']; } $body = wp_remote_retrieve_body($response); $vulnerabilities = json_decode($body, true); if (empty($vulnerabilities) || !is_array($vulnerabilities)) { return ['status' => 'clean', 'count' => 0, 'details' => []]; } // Failsafe filter: Validate reported vulnerabilities against locally installed versions // to eliminate false positives (e.g. "Flatsome <= 3.20.5" when 3.20.11 is installed). foreach ($vulnerabilities as $slug => $vuln_item) { $installed_ver = $payload[$slug] ?? null; if (empty($installed_ver)) { continue; } $vuln_data = is_array($vuln_item) ? (isset($vuln_item[0]) ? $vuln_item[0] : $vuln_item) : null; if (!$vuln_data) { continue; } // 1. Check if patched version is specified and installed version is already >= patched $patched_in = $vuln_data['software'][0]['patched_versions'][0] ?? null; if (empty($patched_in) && !empty($vuln_data['fix']) && preg_match('/Update to\s+([0-9]+(?:\.[0-9]+)+)/i', $vuln_data['fix'], $m_fix)) { $patched_in = $m_fix[1]; } if (!empty($patched_in) && $patched_in !== 'Unknown' && version_compare($installed_ver, $patched_in, '>=')) { unset($vulnerabilities[$slug]); continue; } // 2. Check if vulnerability title specifies affected upper bound (e.g. "<= 3.20.5" or "< 3.20.5") if (!empty($vuln_data['title'])) { if (preg_match('/<=\s*([0-9]+(?:\.[0-9]+)+)/', $vuln_data['title'], $m_lte)) { if (version_compare($installed_ver, $m_lte[1], '>')) { unset($vulnerabilities[$slug]); continue; } } elseif (preg_match('/<\s*([0-9]+(?:\.[0-9]+)+)/', $vuln_data['title'], $m_lt)) { if (version_compare($installed_ver, $m_lt[1], '>=')) { unset($vulnerabilities[$slug]); continue; } } } } if (empty($vulnerabilities)) { return ['status' => 'clean', 'count' => 0, 'details' => []]; } return [ 'status' => 'vulnerable', 'count' => count($vulnerabilities), 'details' => $vulnerabilities ]; } /** * Checks the server IP reputation against configured blocklists. * @return array Check results. */ public function check_server_reputation() { $server_ip = $this->plugin->get_server_ip(); if (! $server_ip) { return [ 'status' => 'error', 'message' => __('Could not determine server IP address.', 'advanced-ip-blocker') ]; } $results = [ 'ip' => $server_ip, 'status' => 'clean', 'checks' => [] ]; $aib_status = 'clean'; $aib_detail = ''; $api_check_url = 'https://advaipbl.com/wp-json/aib-scanner/v2/check-ip?ip=' . $server_ip . '&v=' . ADVAIPBL_VERSION; $site_hash = hash('sha256', get_site_url()); $headers = [ 'X-AIB-Site-Hash' => $site_hash ]; if (!empty($this->plugin->options['api_token_v3'])) { $api_check_url = 'https://advaipbl.com/wp-json/aib-api/v3/scanner/check-ip?ip=' . $server_ip . '&v=' . ADVAIPBL_VERSION; $headers['Authorization'] = 'Bearer ' . $this->plugin->options['api_token_v3']; $headers['X-AIB-Auth'] = 'Bearer ' . $this->plugin->options['api_token_v3']; } $response = wp_remote_get($api_check_url, [ 'timeout' => 5, 'headers' => $headers ]); if (is_wp_error($response)) { $aib_status = 'error'; $aib_detail = __('API Error', 'advanced-ip-blocker'); } else { $data = json_decode(wp_remote_retrieve_body($response), true); if (isset($data['listed']) && $data['listed'] === true) { $aib_status = 'listed'; $results['status'] = 'blacklisted'; } } $results['checks']['aib'] = [ 'label' => __('AIB Community Network', 'advanced-ip-blocker'), 'status' => $aib_status, 'detail' => $aib_detail ]; $local_status = 'clean'; if (isset($this->plugin->community_manager) && $this->plugin->community_manager->is_ip_blocked($server_ip)) { $local_status = 'listed'; } $results['checks']['local_db'] = [ 'label' => __('Local Firewall Database', 'advanced-ip-blocker'), 'status' => $local_status, 'detail' => $local_status === 'listed' ? __('Blocked locally (Aggregated)', 'advanced-ip-blocker') : '' ]; $spamhaus_status = 'skipped'; if (! empty($this->plugin->options['enable_spamhaus_asn']) && isset($this->plugin->asn_manager)) { $location = $this->plugin->geolocation_manager->fetch_location($server_ip); $asn = $this->plugin->asn_manager->extract_asn_from_data($location); if ($asn) { $spamhaus_list = get_option('advaipbl_spamhaus_asn_list', []); if (in_array($asn, $spamhaus_list, true)) { $spamhaus_status = 'listed'; $results['status'] = 'blacklisted'; } else { $spamhaus_status = 'clean'; } } else { $spamhaus_status = 'unknown'; } } $results['checks']['spamhaus'] = [ 'label' => __('Spamhaus ASN DROP', 'advanced-ip-blocker'), 'status' => $spamhaus_status ]; $abuse_status = 'skipped'; if (! empty($this->plugin->options['enable_abuseipdb']) && ! empty($this->plugin->options['abuseipdb_api_key'])) { $check = $this->plugin->abuseipdb_manager->check_ip($server_ip); if ($check && isset($check['score'])) { if ($check['score'] > 0) { $abuse_status = ($check['score'] > 20) ? 'listed' : 'warning'; if ($check['score'] > 20) { $results['status'] = 'blacklisted'; } } else { $abuse_status = 'clean'; } $results['checks']['abuseipdb'] = [ 'label' => __('AbuseIPDB', 'advanced-ip-blocker'), 'status' => $abuse_status, /* translators: %s is a placeholder */ 'detail' => sprintf(__('Score: %d%%', 'advanced-ip-blocker'), $check['score']) ]; } } else { $results['checks']['abuseipdb'] = [ 'label' => __('AbuseIPDB', 'advanced-ip-blocker'), 'status' => 'skipped', 'detail' => __('API Key not configured', 'advanced-ip-blocker') ]; } return $results; } /** * Executes a full scan (Local + Server Reputation + Vulnerabilities) and sends an HTML report via email. * @param string $to_email Destination email address. * @param bool $is_manual Whether the scan was triggered manually. */ public function run_full_scan_and_email($to_email, $is_manual = false) { if (empty($to_email)) { $configured_email = $this->plugin->options['scan_notification_email'] ?? ''; $to_email = !empty($configured_email) ? $configured_email : get_option('admin_email'); } $local_scan = $this->run_local_scan(); $server_rep = $this->check_server_reputation(); $vulns = $this->check_vulnerabilities_via_api(); if (! $is_manual) { $trigger = $this->plugin->options['scan_email_trigger'] ?? 'any_issue'; $has_vuln_or_critical = false; $has_any_issue = false; if ($vulns['status'] === 'vulnerable' || $vulns['status'] === 'error') { $has_vuln_or_critical = true; } if ($server_rep['status'] === 'blacklisted' || $server_rep['status'] === 'error') { $has_vuln_or_critical = true; } // Evaluate Environment Criticals (also severe enough for agency alert) if ($local_scan['php']['status'] === 'critical' || $local_scan['wordpress']['status'] === 'critical' || $local_scan['debug_mode']['status'] === 'critical' || $local_scan['ssl']['status'] === 'critical') { $has_vuln_or_critical = true; } if ($has_vuln_or_critical) { $has_any_issue = true; } else { if ($local_scan['updates']['status'] !== 'good' && $local_scan['updates']['status'] !== 'skipped') { $has_any_issue = true; } if ($local_scan['php']['status'] !== 'good' && $local_scan['php']['status'] !== 'skipped') { $has_any_issue = true; } if ($local_scan['wordpress']['status'] !== 'good' && $local_scan['wordpress']['status'] !== 'skipped') { $has_any_issue = true; } if ($local_scan['ssl']['status'] !== 'good' && $local_scan['ssl']['status'] !== 'skipped') { $has_any_issue = true; } if ($local_scan['debug_mode']['status'] !== 'good' && $local_scan['debug_mode']['status'] !== 'skipped') { $has_any_issue = true; } } if ($trigger === 'vulnerabilities_only' && !$has_vuln_or_critical) { return; // Agency mode: silent unless strictly critical } if ($trigger === 'any_issue' && !$has_any_issue) { return; } } $site_url = get_site_url(); $date = date_i18n(get_option('date_format') . ' ' . get_option('time_format')); /* translators: %s is a placeholder */ $subject = sprintf(__('[%1$s] DeepScan Security Report - %2$s', 'advanced-ip-blocker'), get_bloginfo('name'), $date); $template_title = __('Security Scan Report', 'advanced-ip-blocker'); ob_start(); ?>

' . esc_html($site_url) . ''); ?>

0 || $local_scan['updates']['themes'] > 0): ?>

PHP Version
WordPress
SSL
Debug Mode

(' . esc_html($check['detail']) . ')'; } ?>

$info) : $v = is_array($info) ? $info : []; $title = $v['title'] ?? __('Unknown Vulnerability', 'advanced-ip-blocker'); $severity = $v['severity'] ?? 'Unknown'; $cve = $v['cve'] ?? ''; $cve_link = $v['cve_link'] ?? ''; $description = $v['description'] ?? ''; $fix = $v['fix'] ?? __('No fix details available.', 'advanced-ip-blocker'); $severity_color = '#dba617'; if (stripos($severity, 'High') !== false || stripos($severity, 'Critical') !== false) { $severity_color = '#d63638'; } ?>

 

plugin->notification_manager)) { $body = $this->plugin->notification_manager->get_html_email_template($template_title, $content_html); add_filter('wp_mail_content_type', [$this->plugin->notification_manager, 'set_html_mail_content_type']); wp_mail($to_email, $subject, $body); remove_filter('wp_mail_content_type', [$this->plugin->notification_manager, 'set_html_mail_content_type']); } else { $body = $content_html; wp_mail($to_email, $subject, $body); } } }