From 6b719f4facb825369f0aee838a4e72020002257d Mon Sep 17 00:00:00 2001 From: Yuri Karamian Date: Wed, 4 Feb 2026 20:30:32 +0100 Subject: [PATCH] v1.0 --- .../arkhost-security-pack.php | 88 +- arkhost-security-pack/assets/css/admin.css | 575 +++ .../assets/css}/index.php | 2 +- .../assets}/index.php | 2 +- .../assets/js/2fa-profile.js | 89 + arkhost-security-pack/assets/js/admin.js | 400 +++ .../assets/js/index.php | 0 .../assets/js/qrcode.min.js | 0 .../includes/class-arksp-activity-log.php | 48 +- .../includes/class-arksp-admin.php | 2805 +++++++++++++++ .../includes/class-arksp-db.php | 50 +- .../includes/class-arksp-file-integrity.php | 44 +- .../includes/class-arksp-geo-blocking.php | 50 +- .../includes/class-arksp-hardening.php | 28 +- .../includes/class-arksp-helper.php | 555 +++ .../includes/class-arksp-ip-control.php | 48 +- .../includes/class-arksp-ip2location.php | 4 +- .../includes/class-arksp-login-protection.php | 254 +- .../includes/class-arksp-malware-scanner.php | 92 +- .../includes/class-arksp-two-factor.php | 266 +- .../includes}/index.php | 2 +- .../index.php | 2 +- arkhost-security-pack/languages/index.php | 6 + .../readme.txt | 30 +- arkhost-security-pack/uninstall.php | 55 + .../assets => icon-images}/icon-128x128.png | Bin .../assets => icon-images}/icon-256x256.png | Bin .../assets => icon-images}/screenshot-1.png | Bin .../assets => icon-images}/screenshot-2.png | Bin .../assets => icon-images}/screenshot-3.png | Bin .../assets => icon-images}/screenshot-4.png | Bin .../assets => icon-images}/screenshot-5.png | Bin .../assets => icon-images}/screenshot-6.png | Bin .../assets => icon-images}/screenshot-7.png | Bin .../assets => icon-images}/screenshot-8.png | Bin .../assets => icon-images}/screenshot-9.png | Bin .../security-pack-logo.png | Bin security-pack/README.md | 103 - security-pack/assets/css/admin.css | 524 --- security-pack/includes/class-wpsp-admin.php | 3200 ----------------- security-pack/includes/class-wpsp-helper.php | 555 --- security-pack/index.php | 6 - security-pack/languages/index.php | 6 - security-pack/uninstall.php | 33 - 44 files changed, 4974 insertions(+), 4948 deletions(-) rename security-pack/security-pack.php => arkhost-security-pack/arkhost-security-pack.php (64%) create mode 100644 arkhost-security-pack/assets/css/admin.css rename {security-pack/data => arkhost-security-pack/assets/css}/index.php (53%) rename {security-pack/includes => arkhost-security-pack/assets}/index.php (53%) create mode 100644 arkhost-security-pack/assets/js/2fa-profile.js create mode 100644 arkhost-security-pack/assets/js/admin.js rename {security-pack => arkhost-security-pack}/assets/js/index.php (100%) rename {security-pack => arkhost-security-pack}/assets/js/qrcode.min.js (100%) rename security-pack/includes/class-wpsp-activity-log.php => arkhost-security-pack/includes/class-arksp-activity-log.php (78%) create mode 100644 arkhost-security-pack/includes/class-arksp-admin.php rename security-pack/includes/class-wpsp-db.php => arkhost-security-pack/includes/class-arksp-db.php (73%) rename security-pack/includes/class-wpsp-file-integrity.php => arkhost-security-pack/includes/class-arksp-file-integrity.php (82%) rename security-pack/includes/class-wpsp-geo-blocking.php => arkhost-security-pack/includes/class-arksp-geo-blocking.php (81%) rename security-pack/includes/class-wpsp-hardening.php => arkhost-security-pack/includes/class-arksp-hardening.php (89%) create mode 100644 arkhost-security-pack/includes/class-arksp-helper.php rename security-pack/includes/class-wpsp-ip-control.php => arkhost-security-pack/includes/class-arksp-ip-control.php (81%) rename security-pack/includes/class-wpsp-ip2location.php => arkhost-security-pack/includes/class-arksp-ip2location.php (99%) rename security-pack/includes/class-wpsp-login-protection.php => arkhost-security-pack/includes/class-arksp-login-protection.php (78%) rename security-pack/includes/class-wpsp-malware-scanner.php => arkhost-security-pack/includes/class-arksp-malware-scanner.php (90%) rename security-pack/includes/class-wpsp-two-factor.php => arkhost-security-pack/includes/class-arksp-two-factor.php (64%) rename {security-pack/assets/css => arkhost-security-pack/includes}/index.php (53%) rename {security-pack/assets => arkhost-security-pack}/index.php (53%) create mode 100644 arkhost-security-pack/languages/index.php rename {security-pack => arkhost-security-pack}/readme.txt (68%) create mode 100644 arkhost-security-pack/uninstall.php rename {security-pack/assets => icon-images}/icon-128x128.png (100%) rename {security-pack/assets => icon-images}/icon-256x256.png (100%) rename {security-pack/assets => icon-images}/screenshot-1.png (100%) rename {security-pack/assets => icon-images}/screenshot-2.png (100%) rename {security-pack/assets => icon-images}/screenshot-3.png (100%) rename {security-pack/assets => icon-images}/screenshot-4.png (100%) rename {security-pack/assets => icon-images}/screenshot-5.png (100%) rename {security-pack/assets => icon-images}/screenshot-6.png (100%) rename {security-pack/assets => icon-images}/screenshot-7.png (100%) rename {security-pack/assets => icon-images}/screenshot-8.png (100%) rename {security-pack/assets => icon-images}/screenshot-9.png (100%) rename {security-pack/assets => icon-images}/security-pack-logo.png (100%) delete mode 100644 security-pack/README.md delete mode 100644 security-pack/assets/css/admin.css delete mode 100644 security-pack/includes/class-wpsp-admin.php delete mode 100644 security-pack/includes/class-wpsp-helper.php delete mode 100644 security-pack/index.php delete mode 100644 security-pack/languages/index.php delete mode 100644 security-pack/uninstall.php diff --git a/security-pack/security-pack.php b/arkhost-security-pack/arkhost-security-pack.php similarity index 64% rename from security-pack/security-pack.php rename to arkhost-security-pack/arkhost-security-pack.php index 65ce578..6f0a9d9 100644 --- a/security-pack/security-pack.php +++ b/arkhost-security-pack/arkhost-security-pack.php @@ -1,6 +1,6 @@ components['activity_log'] = new WPSP_Activity_Log(); - $this->components['ip_control'] = new WPSP_IP_Control(); - $this->components['hardening'] = new WPSP_Hardening(); - $this->components['geo_blocking'] = new WPSP_Geo_Blocking(); - $this->components['two_factor'] = new WPSP_Two_Factor(); - $this->components['file_integrity'] = new WPSP_File_Integrity(); - $this->components['malware_scanner'] = new WPSP_Malware_Scanner(); + $this->components['activity_log'] = new ARKSP_Activity_Log(); + $this->components['ip_control'] = new ARKSP_IP_Control(); + $this->components['hardening'] = new ARKSP_Hardening(); + $this->components['geo_blocking'] = new ARKSP_Geo_Blocking(); + $this->components['two_factor'] = new ARKSP_Two_Factor(); + $this->components['file_integrity'] = new ARKSP_File_Integrity(); + $this->components['malware_scanner'] = new ARKSP_Malware_Scanner(); // Login protection must be last - custom login URL handling may exit early. - $this->components['login_protection'] = new WPSP_Login_Protection(); + $this->components['login_protection'] = new ARKSP_Login_Protection(); if ( is_admin() ) { - $this->components['admin'] = new WPSP_Admin(); + $this->components['admin'] = new ARKSP_Admin(); } } @@ -212,7 +212,7 @@ final class Security_Pack { * @return mixed */ public static function get_setting( $key, $default = null ) { - $settings = get_option( 'wpsp_settings', array() ); + $settings = get_option( 'arksp_settings', array() ); $defaults = self::get_default_settings(); if ( isset( $settings[ $key ] ) ) { @@ -234,9 +234,9 @@ final class Security_Pack { * @return bool */ public static function update_setting( $key, $value ) { - $settings = get_option( 'wpsp_settings', array() ); + $settings = get_option( 'arksp_settings', array() ); $settings[ $key ] = $value; - return update_option( 'wpsp_settings', $settings ); + return update_option( 'arksp_settings', $settings ); } /** @@ -255,11 +255,11 @@ final class Security_Pack { /** * Get plugin instance. * - * @return Security_Pack + * @return ARKSP_Plugin */ -function wpsp() { - return Security_Pack::instance(); +function arksp() { + return ARKSP_Plugin::instance(); } // Initialize plugin. -wpsp(); +arksp(); diff --git a/arkhost-security-pack/assets/css/admin.css b/arkhost-security-pack/assets/css/admin.css new file mode 100644 index 0000000..7292ef8 --- /dev/null +++ b/arkhost-security-pack/assets/css/admin.css @@ -0,0 +1,575 @@ +/** + * ArkHost Security Pack Admin Styles + * + * Clean, modern design - no scary dashboards or fake threat counters. + */ + +/* CSS Custom Properties */ +:root { + --arksp-primary: #2271b1; + --arksp-primary-hover: #135e96; + --arksp-success: #00a32a; + --arksp-success-bg: #edfaef; + --arksp-warning: #dba617; + --arksp-warning-bg: #fcf9e8; + --arksp-danger: #d63638; + --arksp-danger-bg: #fcf0f1; + --arksp-info: #72aee6; + --arksp-info-bg: #f0f6fc; + --arksp-border: #e0e0e0; + --arksp-border-light: #f0f0f1; + --arksp-text: #1d2327; + --arksp-text-light: #646970; + --arksp-bg: #fff; + --arksp-bg-alt: #f6f7f7; + --arksp-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); + --arksp-shadow-hover: 0 2px 8px rgba(0, 0, 0, 0.08); + --arksp-radius: 8px; + --arksp-radius-sm: 4px; + --arksp-transition: 0.15s ease; +} + +/* Main wrapper */ +.arksp-wrap { + max-width: 1200px; +} + +.arksp-wrap h1 { + font-size: 24px; + font-weight: 600; + margin-bottom: 1.5em; + padding: 0; + color: var(--arksp-text); +} + +/* Tabs */ +.arksp-tabs { + margin-bottom: 24px; + border-bottom: 1px solid var(--arksp-border); + display: flex; + flex-wrap: wrap; + gap: 2px; +} + +.arksp-tabs .nav-tab { + margin-left: 0; + margin-right: 0; + border-radius: var(--arksp-radius-sm) var(--arksp-radius-sm) 0 0; + transition: background var(--arksp-transition), color var(--arksp-transition); + padding: 8px 16px; + font-weight: 500; +} + +.arksp-tabs .nav-tab:hover { + background: var(--arksp-bg-alt); +} + +.arksp-tabs .nav-tab-active { + background: var(--arksp-bg); + border-bottom-color: var(--arksp-bg); +} + +/* Sections */ +.arksp-section { + background: var(--arksp-bg); + border: 1px solid var(--arksp-border); + border-radius: var(--arksp-radius); + padding: 24px; + margin-bottom: 20px; + box-shadow: var(--arksp-shadow); + transition: box-shadow var(--arksp-transition); +} + +.arksp-section:hover { + box-shadow: var(--arksp-shadow-hover); +} + +.arksp-section h2 { + margin: 0 0 16px 0; + padding: 0 0 12px 0; + font-size: 1.2em; + font-weight: 600; + border-bottom: 1px solid var(--arksp-border-light); + color: var(--arksp-text); +} + +.arksp-section h3 { + margin-top: 1.5em; + font-size: 1em; + font-weight: 600; + color: var(--arksp-text); +} + +/* Form tables */ +.arksp-section .form-table { + margin-top: 0; +} + +.arksp-section .form-table th { + width: 220px; + padding-left: 0; + font-weight: 500; + color: var(--arksp-text); +} + +.arksp-section .form-table td { + padding-top: 12px; + padding-bottom: 12px; +} + +/* Status indicators */ +.arksp-status { + display: inline-flex; + align-items: center; + padding: 4px 10px; + border-radius: 50px; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +.arksp-status-ok { + background: var(--arksp-success-bg); + color: var(--arksp-success); +} + +.arksp-status-warning { + background: var(--arksp-warning-bg); + color: #8a6d00; +} + +.arksp-status-error { + background: var(--arksp-danger-bg); + color: var(--arksp-danger); +} + +/* Stats */ +.arksp-stats { + display: flex; + gap: 16px; + margin-bottom: 24px; + flex-wrap: wrap; +} + +.arksp-stat { + background: var(--arksp-bg); + border: 1px solid var(--arksp-border); + padding: 20px 28px; + border-radius: var(--arksp-radius); + text-align: center; + min-width: 130px; + transition: transform var(--arksp-transition), box-shadow var(--arksp-transition); +} + +.arksp-stat:hover { + transform: translateY(-2px); + box-shadow: var(--arksp-shadow-hover); +} + +.arksp-stat-value { + display: block; + font-size: 32px; + font-weight: 700; + color: var(--arksp-text); + line-height: 1.2; +} + +.arksp-stat-label { + display: block; + font-size: 11px; + font-weight: 500; + color: var(--arksp-text-light); + margin-top: 6px; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* Stat color variations */ +.arksp-stat-success { + border-left: 4px solid var(--arksp-success); +} + +.arksp-stat-success .arksp-stat-value { + color: var(--arksp-success); +} + +.arksp-stat-warning { + border-left: 4px solid var(--arksp-warning); +} + +.arksp-stat-warning .arksp-stat-value { + color: #8a6d00; +} + +.arksp-stat-danger { + border-left: 4px solid var(--arksp-danger); +} + +.arksp-stat-danger .arksp-stat-value { + color: var(--arksp-danger); +} + +.arksp-stat-info { + border-left: 4px solid var(--arksp-primary); +} + +.arksp-stat-info .arksp-stat-value { + color: var(--arksp-primary); +} + +/* Tables */ +.arksp-section .widefat { + margin-top: 16px; + border-radius: var(--arksp-radius-sm); + overflow: hidden; +} + +.arksp-section .widefat td, +.arksp-section .widefat th { + padding: 12px 14px; +} + +.arksp-section .widefat thead th { + font-weight: 600; + background: var(--arksp-bg-alt); +} + +.arksp-section .widefat tbody tr { + transition: background var(--arksp-transition); +} + +.arksp-section .widefat tbody tr:hover { + background: var(--arksp-bg-alt); +} + +/* Country select */ +.arksp-country-select { + min-width: 300px; + height: 200px !important; + border-radius: var(--arksp-radius-sm); +} + +/* Event type colors */ +.arksp-event-login_success { + color: var(--arksp-success); +} + +.arksp-event-login_failed { + color: #8a6d00; +} + +.arksp-event-lockout, +.arksp-event-ip_blocked, +.arksp-event-geo_blocked { + color: var(--arksp-danger); +} + +/* Country badge */ +.arksp-country { + display: inline-block; + background: var(--arksp-bg-alt); + padding: 2px 8px; + border-radius: 50px; + font-size: 11px; + font-weight: 500; + margin-left: 6px; +} + +/* Severity badges */ +.arksp-badge { + display: inline-block; + padding: 3px 10px; + border-radius: 50px; + font-size: 10px; + font-weight: 700; + color: #fff; + margin-right: 6px; + text-transform: uppercase; + letter-spacing: 0.03em; +} + +/* Footer */ +.arksp-footer { + margin-top: 32px; + padding-top: 20px; + border-top: 1px solid var(--arksp-border); + color: var(--arksp-text-light); + font-size: 13px; +} + +.arksp-footer a { + color: var(--arksp-primary); + text-decoration: none; + transition: color var(--arksp-transition); +} + +.arksp-footer a:hover { + color: var(--arksp-primary-hover); + text-decoration: underline; +} + +/* 2FA status */ +.arksp-2fa-status { + display: inline-flex; + align-items: center; + padding: 6px 14px; + border-radius: 50px; + font-weight: 600; + font-size: 13px; +} + +.arksp-2fa-enabled { + background: var(--arksp-success-bg); + color: var(--arksp-success); +} + +.arksp-2fa-disabled { + background: var(--arksp-danger-bg); + color: var(--arksp-danger); +} + +/* Responsive */ +@media screen and (max-width: 782px) { + .arksp-stats { + flex-direction: column; + } + + .arksp-stat { + min-width: auto; + } + + .arksp-section .form-table th { + width: auto; + } + + .arksp-country-select { + min-width: 100%; + } + + .arksp-tabs { + gap: 4px; + } + + .arksp-tabs .nav-tab { + padding: 6px 12px; + font-size: 13px; + } +} + +/* Code blocks */ +.arksp-section code { + background: var(--arksp-bg-alt); + padding: 3px 8px; + border-radius: var(--arksp-radius-sm); + font-size: 13px; +} + +/* Button spacing & styling */ +.arksp-section .button { + transition: all var(--arksp-transition); +} + +.arksp-section .button:hover { + transform: translateY(-1px); +} + +.arksp-section .button + .button { + margin-left: 8px; +} + +/* Tablenav */ +.arksp-section .tablenav { + margin: 16px 0; +} + +.arksp-section .tablenav .actions { + padding: 0; +} + +/* Alert boxes */ +.arksp-alert { + padding: 14px 18px; + border-radius: var(--arksp-radius); + margin-bottom: 16px; + border-left: 4px solid; +} + +.arksp-alert-info { + background: var(--arksp-info-bg); + border-left-color: var(--arksp-info); + color: #0a4b78; +} + +.arksp-alert-warning { + background: var(--arksp-warning-bg); + border-left-color: var(--arksp-warning); + color: #6e5600; +} + +.arksp-alert-error { + background: var(--arksp-danger-bg); + border-left-color: var(--arksp-danger); + color: #8a1f1f; +} + +/* Loading states */ +.arksp-loading { + opacity: 0.5; + pointer-events: none; +} + +/* QR code container */ +#arksp-2fa-qr img, +#arksp-2fa-qr svg { + border: 1px solid var(--arksp-border); + padding: 12px; + background: var(--arksp-bg); + border-radius: var(--arksp-radius); + margin: 12px 0; +} + +/* Backup codes display */ +#arksp-backup-codes-display pre, +#arksp-new-backup-codes { + background: var(--arksp-bg-alt); + padding: 16px; + border-radius: var(--arksp-radius-sm); + font-family: 'SF Mono', Monaco, Consolas, monospace; + font-size: 14px; + line-height: 2; + border: 1px solid var(--arksp-border); +} + +/* Scan results */ +.arksp-scan-results { + max-height: 400px; + overflow-y: auto; +} + +/* Checkbox styling */ +.arksp-section input[type="checkbox"] { + margin-right: 10px; + width: 18px; + height: 18px; +} + +/* Description text */ +.arksp-section .description { + color: var(--arksp-text-light); + font-style: normal; + margin-top: 6px; + font-size: 13px; + line-height: 1.5; +} + +/* Tab intro descriptions */ +.arksp-section p.arksp-description { + color: var(--arksp-text-light); + font-style: normal; + font-size: 14px; + margin: -8px 0 20px 0; + line-height: 1.6; +} + +/* Import/export section */ +#arksp-import-file { + margin-right: 12px; +} + +#arksp-import-status { + margin-left: 12px; + color: var(--arksp-success); + font-weight: 500; +} + +/* System info table */ +.arksp-section .widefat td:first-child { + font-weight: 600; + width: 200px; + color: var(--arksp-text); +} + +/* Status checklist icons */ +.arksp-section .widefat td span[style*="color: #46b450"], +.arksp-section .widefat td span[style*="color: #dc3232"] { + font-size: 20px; + line-height: 1; +} + +/* Notice improvements within sections */ +.arksp-section .notice { + border-radius: var(--arksp-radius-sm); + margin: 12px 0; +} + +/* Input focus states */ +.arksp-section input[type="text"]:focus, +.arksp-section input[type="number"]:focus, +.arksp-section textarea:focus, +.arksp-section select:focus { + border-color: var(--arksp-primary); + box-shadow: 0 0 0 1px var(--arksp-primary); + outline: none; +} + +/* Textarea styling */ +.arksp-section textarea { + border-radius: var(--arksp-radius-sm); +} + +/* Select styling */ +.arksp-section select { + border-radius: var(--arksp-radius-sm); +} + +/* Dashboard widget */ +.arksp-widget-score { + text-align: center; + padding: 15px 0; + border-bottom: 1px solid #eee; + margin-bottom: 15px; +} + +.arksp-widget-score-value { + font-size: 42px; + font-weight: 700; + line-height: 1; +} + +.arksp-widget-score-label { + color: #666; + font-size: 12px; + margin-top: 5px; +} + +.arksp-widget-score.good .arksp-widget-score-value { color: #00a32a; } +.arksp-widget-score.warning .arksp-widget-score-value { color: #dba617; } +.arksp-widget-score.bad .arksp-widget-score-value { color: #d63638; } + +.arksp-widget-stats { + display: flex; + justify-content: space-around; + text-align: center; + margin-bottom: 15px; +} + +.arksp-widget-stat-value { + font-size: 24px; + font-weight: 600; +} + +.arksp-widget-stat-label { + font-size: 11px; + color: #666; +} + +.arksp-widget-links { + border-top: 1px solid #eee; + padding-top: 12px; + text-align: center; +} + +.arksp-widget-links a { + margin: 0 8px; +} diff --git a/security-pack/data/index.php b/arkhost-security-pack/assets/css/index.php similarity index 53% rename from security-pack/data/index.php rename to arkhost-security-pack/assets/css/index.php index 91bbd2b..f39ff62 100644 --- a/security-pack/data/index.php +++ b/arkhost-security-pack/assets/css/index.php @@ -2,5 +2,5 @@ /** * Silence is golden. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ diff --git a/security-pack/includes/index.php b/arkhost-security-pack/assets/index.php similarity index 53% rename from security-pack/includes/index.php rename to arkhost-security-pack/assets/index.php index 91bbd2b..f39ff62 100644 --- a/security-pack/includes/index.php +++ b/arkhost-security-pack/assets/index.php @@ -2,5 +2,5 @@ /** * Silence is golden. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ diff --git a/arkhost-security-pack/assets/js/2fa-profile.js b/arkhost-security-pack/assets/js/2fa-profile.js new file mode 100644 index 0000000..b4ca7e8 --- /dev/null +++ b/arkhost-security-pack/assets/js/2fa-profile.js @@ -0,0 +1,89 @@ +/** + * ArkHost Security Pack - 2FA Profile Scripts + * + * Handles 2FA setup, verification, disable, and backup code regeneration + * on the user profile page. Dynamic values are passed via arksp2fa object + * (wp_localize_script). + */ + +/* global jQuery, ajaxurl, arksp2fa, qrcode */ + +jQuery(document).ready(function($) { + + $('#arksp-setup-2fa').on('click', function() { + $('#arksp-2fa-setup').show(); + $(this).hide(); + + // Generate new secret. + $.post(ajaxurl, { + action: 'arksp_generate_2fa_secret', + user_id: arksp2fa.userId, + _ajax_nonce: arksp2fa.nonce + }, function(response) { + if (response.success) { + $('#arksp-2fa-secret').text(response.data.secret); + + // Generate QR code client-side. + var qr = qrcode(0, 'M'); + qr.addData(response.data.otpauth); + qr.make(); + $('#arksp-2fa-qr').html(qr.createSvgTag(5, 0)); + } + }); + }); + + $('#arksp-verify-2fa-setup').on('click', function() { + var code = $('#arksp-2fa-verify-code').val(); + $.post(ajaxurl, { + action: 'arksp_verify_2fa_setup', + user_id: arksp2fa.userId, + code: code, + _ajax_nonce: arksp2fa.nonce + }, function(response) { + if (response.success && response.data.backup_codes) { + // Show backup codes - user MUST save these. + var codesHtml = '
'; + codesHtml += '

' + arksp2fa.strings.twoFaEnabledTitle + '

'; + codesHtml += '

' + arksp2fa.strings.codesNotShownAgain + '

'; + codesHtml += '
' + response.data.backup_codes.join('\n') + '
'; + codesHtml += '

' + arksp2fa.strings.storeCodesInfo + '

'; + codesHtml += ''; + codesHtml += '
'; + $('#arksp-2fa-setup').html(codesHtml); + } else if (response.success) { + location.reload(); + } else { + $('#arksp-2fa-setup-result').html('

' + response.data.message + '

'); + } + }); + }); + + $('#arksp-disable-2fa').on('click', function() { + if (confirm(arksp2fa.strings.confirmDisable)) { + $.post(ajaxurl, { + action: 'arksp_disable_2fa', + user_id: arksp2fa.userId, + _ajax_nonce: arksp2fa.nonce + }, function(response) { + if (response.success) { + location.reload(); + } + }); + } + }); + + $('#arksp-regenerate-backup-codes').on('click', function() { + if (confirm(arksp2fa.strings.confirmRegenerate)) { + $.post(ajaxurl, { + action: 'arksp_regenerate_backup_codes', + user_id: arksp2fa.userId, + _ajax_nonce: arksp2fa.nonce + }, function(response) { + if (response.success) { + $('#arksp-new-backup-codes').text(response.data.codes.join('\n')); + $('#arksp-backup-codes-display').show(); + } + }); + } + }); +}); diff --git a/arkhost-security-pack/assets/js/admin.js b/arkhost-security-pack/assets/js/admin.js new file mode 100644 index 0000000..6b34e06 --- /dev/null +++ b/arkhost-security-pack/assets/js/admin.js @@ -0,0 +1,400 @@ +/** + * ArkHost Security Pack - Admin Scripts + * + * All admin page AJAX handlers. Dynamic values are passed via arkspAdmin object + * (wp_localize_script). + */ + +/* global jQuery, ajaxurl, arkspAdmin */ + +jQuery(document).ready(function($) { + + // ------------------------------------------------------------------------- + // Status tab: Delete WP info files (readme.html, license.txt). + // ------------------------------------------------------------------------- + $('.arksp-delete-file').on('click', function() { + var $btn = $(this); + var file = $btn.data('file'); + + if (!confirm(arkspAdmin.strings.confirmDeleteFile)) { + return; + } + + $btn.prop('disabled', true).text(arkspAdmin.strings.deleting); + + $.post(ajaxurl, { + action: 'arksp_delete_wp_file', + file: file, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $btn.closest('tr').fadeOut(); + } else { + alert(response.data.message || arkspAdmin.strings.failedDeleteFile); + $btn.prop('disabled', false).text(arkspAdmin.strings.deleteBtn); + } + }); + }); + + // ------------------------------------------------------------------------- + // Login tab: Test email. + // ------------------------------------------------------------------------- + $('#arksp-test-email').on('click', function() { + var $btn = $(this); + var $result = $('#arksp-test-email-result'); + var email = $('#arksp-alert-email').val() || arkspAdmin.adminEmail; + + $btn.prop('disabled', true); + $result.html('' + arkspAdmin.strings.sending + ''); + + $.post(ajaxurl, { + action: 'arksp_test_email', + email: email, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + $btn.prop('disabled', false); + if (response.success) { + $result.html('' + arkspAdmin.strings.testEmailSent + ''); + } else { + $result.html('' + response.data.message + ''); + } + }).fail(function() { + $btn.prop('disabled', false); + $result.html('' + arkspAdmin.strings.requestFailed + ''); + }); + }); + + // ------------------------------------------------------------------------- + // IP Control tab: Unblock IP and download geo DB. + // ------------------------------------------------------------------------- + $('.arksp-unblock-ip').on('click', function() { + var ip = $(this).data('ip'); + var $row = $(this).closest('tr'); + + $.post(ajaxurl, { + action: 'arksp_unblock_ip', + ip: ip, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $row.fadeOut(); + } + }); + }); + + $('#arksp-download-geo-db').on('click', function() { + var $btn = $(this); + $btn.prop('disabled', true).text(arkspAdmin.strings.downloading); + + $.post(ajaxurl, { + action: 'arksp_download_geo_db', + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + location.reload(); + } else { + alert(response.data.message); + $btn.prop('disabled', false).text(arkspAdmin.strings.downloadGeoDb); + } + }); + }); + + // ------------------------------------------------------------------------- + // Headers tab: Reset headers and load example CSP. + // ------------------------------------------------------------------------- + $('#arksp-reset-headers').on('click', function() { + if (confirm(arkspAdmin.strings.confirmResetHeaders)) { + $('[id^="header-"]').each(function() { + var defaultVal = $(this).data('default'); + $(this).val(defaultVal); + }); + } + }); + + $('#arksp-load-example-csp').on('click', function() { + var exampleCSP = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';"; + $('#header-content_security_policy').val(exampleCSP); + }); + + // ------------------------------------------------------------------------- + // Scanner tab: File integrity scan, malware scan, quarantine. + // ------------------------------------------------------------------------- + $('#arksp-run-file-scan').on('click', function() { + var $btn = $(this); + $btn.prop('disabled', true).text(arkspAdmin.strings.scanning); + + $.post(ajaxurl, { + action: 'arksp_run_file_scan', + _ajax_nonce: arkspAdmin.nonce + }, function() { + location.reload(); + }); + }); + + $('#arksp-reset-baseline, #arksp-dismiss-file-changes').on('click', function() { + var $btn = $(this); + $btn.prop('disabled', true); + + $.post(ajaxurl, { + action: 'arksp_reset_file_baseline', + _ajax_nonce: arkspAdmin.nonce + }, function() { + location.reload(); + }); + }); + + $('#arksp-run-malware-scan').on('click', function() { + var $btn = $(this); + $btn.prop('disabled', true); + $('#arksp-scan-status').text(arkspAdmin.strings.scanningMalware); + + $.post(ajaxurl, { + action: 'arksp_run_malware_scan', + _ajax_nonce: arkspAdmin.nonce + }, function() { + location.reload(); + }); + }); + + $('#arksp-clear-malware-results').on('click', function() { + var $btn = $(this); + $btn.prop('disabled', true); + + $.post(ajaxurl, { + action: 'arksp_clear_malware_results', + _ajax_nonce: arkspAdmin.nonce + }, function() { + location.reload(); + }); + }); + + // Quarantine file. + $('.arksp-quarantine-file').on('click', function() { + var $btn = $(this); + var filePath = $btn.data('file'); + + if (!confirm(arkspAdmin.strings.confirmQuarantine)) { + return; + } + + $btn.prop('disabled', true).text(arkspAdmin.strings.moving); + + $.post(ajaxurl, { + action: 'arksp_quarantine_file', + file_path: filePath, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $btn.closest('tr').fadeOut(function() { + $(this).remove(); + location.reload(); + }); + } else { + alert(response.data.message || arkspAdmin.strings.failedQuarantine); + $btn.prop('disabled', false).text(arkspAdmin.strings.quarantineBtn); + } + }); + }); + + // Restore file from quarantine. + $('.arksp-restore-file').on('click', function() { + var $btn = $(this); + var name = $btn.data('name'); + + if (!confirm(arkspAdmin.strings.confirmRestore)) { + return; + } + + $btn.prop('disabled', true).text(arkspAdmin.strings.restoring); + + $.post(ajaxurl, { + action: 'arksp_restore_file', + quarantine_name: name, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + location.reload(); + } else { + alert(response.data.message || arkspAdmin.strings.failedRestore); + $btn.prop('disabled', false).text(arkspAdmin.strings.restoreBtn); + } + }); + }); + + // Delete quarantined file permanently. + $('.arksp-delete-quarantined').on('click', function() { + var $btn = $(this); + var name = $btn.data('name'); + + if (!confirm(arkspAdmin.strings.confirmDeletePermanent)) { + return; + } + + $btn.prop('disabled', true).text(arkspAdmin.strings.deleting); + + $.post(ajaxurl, { + action: 'arksp_delete_quarantined', + quarantine_name: name, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $btn.closest('tr').fadeOut(function() { + $(this).remove(); + }); + } else { + alert(response.data.message || arkspAdmin.strings.failedDeleteFile); + $btn.prop('disabled', false).text(arkspAdmin.strings.deleteBtn); + } + }); + }); + + // ------------------------------------------------------------------------- + // Logs tab: Filter, export, clear. + // ------------------------------------------------------------------------- + $('#arksp-filter-logs').on('click', function() { + var type = $('#arksp-filter-type').val(); + var url = new URL(window.location.href); + if (type) { + url.searchParams.set('event_type', type); + } else { + url.searchParams.delete('event_type'); + } + url.searchParams.delete('paged'); + window.location.href = url.toString(); + }); + + $('#arksp-export-logs').on('click', function() { + $.post(ajaxurl, { + action: 'arksp_export_logs', + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + var blob = new Blob([response.data.csv], {type: 'text/csv'}); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = 'arkhost-security-pack-logs-' + new Date().toISOString().split('T')[0] + '.csv'; + a.click(); + URL.revokeObjectURL(url); + } + }); + }); + + $('#arksp-clear-logs').on('click', function() { + if (confirm(arkspAdmin.strings.confirmClearLogs)) { + $.post(ajaxurl, { + action: 'arksp_clear_logs', + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + location.reload(); + } + }); + } + }); + + // ------------------------------------------------------------------------- + // Tools tab: Whitelist IP, clear lockouts, force logout, export/import/reset. + // ------------------------------------------------------------------------- + $('#arksp-whitelist-my-ip').on('click', function() { + var $btn = $(this); + $btn.prop('disabled', true); + $.post(ajaxurl, { + action: 'arksp_whitelist_ip', + ip: arkspAdmin.currentIp, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $('#arksp-whitelist-status').text(arkspAdmin.strings.ipWhitelisted).css('color', 'green'); + setTimeout(function() { location.reload(); }, 1000); + } else { + $('#arksp-whitelist-status').text(response.data.message || arkspAdmin.strings.error).css('color', 'red'); + $btn.prop('disabled', false); + } + }); + }); + + $('#arksp-clear-lockouts').on('click', function() { + if (confirm(arkspAdmin.strings.confirmClearLockouts)) { + $.post(ajaxurl, { + action: 'arksp_clear_lockouts', + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $('#arksp-lockout-status').text(arkspAdmin.strings.lockoutsCleared).css('color', 'green'); + } + }); + } + }); + + $('#arksp-force-logout-all').on('click', function() { + if (confirm(arkspAdmin.strings.confirmForceLogout)) { + $.post(ajaxurl, { + action: 'arksp_force_logout_all', + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $('#arksp-logout-status').text(arkspAdmin.strings.sessionsTerminated).css('color', 'green'); + setTimeout(function() { + window.location.href = arkspAdmin.loginUrl; + }, 1500); + } + }); + } + }); + + $('#arksp-export-settings').on('click', function() { + $.post(ajaxurl, { + action: 'arksp_export_settings', + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + var blob = new Blob([JSON.stringify(response.data, null, 2)], {type: 'application/json'}); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = 'arkhost-security-pack-settings.json'; + a.click(); + URL.revokeObjectURL(url); + } + }); + }); + + $('#arksp-import-settings').on('click', function() { + var file = $('#arksp-import-file')[0].files[0]; + if (!file) { + alert(arkspAdmin.strings.selectFileToImport); + return; + } + + var reader = new FileReader(); + reader.onload = function(e) { + $.post(ajaxurl, { + action: 'arksp_import_settings', + settings: e.target.result, + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + $('#arksp-import-status').text(arkspAdmin.strings.settingsImported).css('color', 'green'); + setTimeout(function() { location.reload(); }, 1000); + } else { + $('#arksp-import-status').text(response.data.message).css('color', 'red'); + } + }); + }; + reader.readAsText(file); + }); + + $('#arksp-reset-settings').on('click', function() { + if (confirm(arkspAdmin.strings.confirmResetSettings)) { + $.post(ajaxurl, { + action: 'arksp_reset_settings', + _ajax_nonce: arkspAdmin.nonce + }, function(response) { + if (response.success) { + location.reload(); + } + }); + } + }); +}); diff --git a/security-pack/assets/js/index.php b/arkhost-security-pack/assets/js/index.php similarity index 100% rename from security-pack/assets/js/index.php rename to arkhost-security-pack/assets/js/index.php diff --git a/security-pack/assets/js/qrcode.min.js b/arkhost-security-pack/assets/js/qrcode.min.js similarity index 100% rename from security-pack/assets/js/qrcode.min.js rename to arkhost-security-pack/assets/js/qrcode.min.js diff --git a/security-pack/includes/class-wpsp-activity-log.php b/arkhost-security-pack/includes/class-arksp-activity-log.php similarity index 78% rename from security-pack/includes/class-wpsp-activity-log.php rename to arkhost-security-pack/includes/class-arksp-activity-log.php index 9239fd5..d740bd5 100644 --- a/security-pack/includes/class-wpsp-activity-log.php +++ b/arkhost-security-pack/includes/class-arksp-activity-log.php @@ -2,7 +2,7 @@ /** * Activity logging for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) { /** * Activity log class. */ -class WPSP_Activity_Log { +class ARKSP_Activity_Log { /** * Event types. @@ -45,7 +45,7 @@ class WPSP_Activity_Log { global $wpdb; if ( null === $ip_address ) { - $ip_address = WPSP_Helper::get_client_ip(); + $ip_address = ARKSP_Helper::get_client_ip(); } // Get user agent. @@ -55,14 +55,14 @@ class WPSP_Activity_Log { // Get country code if geo-blocking is available. $country_code = null; - $geo_blocking = wpsp()->get_component( 'geo_blocking' ); + $geo_blocking = arksp()->get_component( 'geo_blocking' ); if ( $geo_blocking && $ip_address ) { $country_code = $geo_blocking->get_country_code( $ip_address ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $result = $wpdb->insert( - $wpdb->prefix . 'wpsp_activity_log', + $wpdb->prefix . 'arksp_activity_log', array( 'event_type' => $event_type, 'ip_address' => $ip_address ? $ip_address : '', @@ -103,7 +103,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return $wpdb->get_results( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_activity_log WHERE event_type = %s AND ip_address = %s ORDER BY created_at DESC LIMIT %d OFFSET %d", + "SELECT * FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s AND ip_address = %s ORDER BY created_at DESC LIMIT %d OFFSET %d", $args['event_type'], $args['ip_address'], $limit, @@ -114,7 +114,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return $wpdb->get_results( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_activity_log WHERE event_type = %s ORDER BY created_at DESC LIMIT %d OFFSET %d", + "SELECT * FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s ORDER BY created_at DESC LIMIT %d OFFSET %d", $args['event_type'], $limit, $offset @@ -124,7 +124,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return $wpdb->get_results( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_activity_log WHERE ip_address = %s ORDER BY created_at DESC LIMIT %d OFFSET %d", + "SELECT * FROM {$wpdb->prefix}arksp_activity_log WHERE ip_address = %s ORDER BY created_at DESC LIMIT %d OFFSET %d", $args['ip_address'], $limit, $offset @@ -136,7 +136,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return $wpdb->get_results( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_activity_log ORDER BY created_at DESC LIMIT %d OFFSET %d", + "SELECT * FROM {$wpdb->prefix}arksp_activity_log ORDER BY created_at DESC LIMIT %d OFFSET %d", $limit, $offset ) @@ -157,7 +157,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return (int) $wpdb->get_var( $wpdb->prepare( - "SELECT COUNT(*) FROM {$wpdb->prefix}wpsp_activity_log WHERE event_type = %s AND ip_address = %s", + "SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s AND ip_address = %s", $args['event_type'], $args['ip_address'] ) @@ -166,7 +166,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return (int) $wpdb->get_var( $wpdb->prepare( - "SELECT COUNT(*) FROM {$wpdb->prefix}wpsp_activity_log WHERE event_type = %s", + "SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s", $args['event_type'] ) ); @@ -174,7 +174,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return (int) $wpdb->get_var( $wpdb->prepare( - "SELECT COUNT(*) FROM {$wpdb->prefix}wpsp_activity_log WHERE ip_address = %s", + "SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log WHERE ip_address = %s", $args['ip_address'] ) ); @@ -182,7 +182,7 @@ class WPSP_Activity_Log { // No filters. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. - return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wpsp_activity_log" ); + return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log" ); } /** @@ -199,7 +199,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security stats must be real-time. $results = $wpdb->get_results( $wpdb->prepare( - "SELECT event_type, COUNT(*) as count FROM {$wpdb->prefix}wpsp_activity_log WHERE created_at >= %s GROUP BY event_type", + "SELECT event_type, COUNT(*) as count FROM {$wpdb->prefix}arksp_activity_log WHERE created_at >= %s GROUP BY event_type", $cutoff ) ); @@ -227,14 +227,14 @@ class WPSP_Activity_Log { public static function cleanup_old_logs() { global $wpdb; - $retention_days = Security_Pack::get_setting( 'log_retention_days', 30 ); + $retention_days = ARKSP_Plugin::get_setting( 'log_retention_days', 30 ); $cutoff = gmdate( 'Y-m-d H:i:s', strtotime( "-{$retention_days} days" ) ); // Delete old logs. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable. $wpdb->query( $wpdb->prepare( - "DELETE FROM {$wpdb->prefix}wpsp_activity_log WHERE created_at < %s", + "DELETE FROM {$wpdb->prefix}arksp_activity_log WHERE created_at < %s", $cutoff ) ); @@ -243,7 +243,7 @@ class WPSP_Activity_Log { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable. $wpdb->query( $wpdb->prepare( - "DELETE FROM {$wpdb->prefix}wpsp_lockouts WHERE lockout_until IS NOT NULL AND lockout_until > 0 AND lockout_until < %d", + "DELETE FROM {$wpdb->prefix}arksp_lockouts WHERE lockout_until IS NOT NULL AND lockout_until > 0 AND lockout_until < %d", time() ) ); @@ -258,7 +258,7 @@ class WPSP_Activity_Log { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Truncate for admin action. - return false !== $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}wpsp_activity_log" ); + return false !== $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}arksp_activity_log" ); } /** @@ -269,12 +269,12 @@ class WPSP_Activity_Log { */ public static function get_event_label( $event_type ) { $labels = array( - self::EVENT_LOGIN_SUCCESS => __( 'Login Success', 'security-pack' ), - self::EVENT_LOGIN_FAILED => __( 'Login Failed', 'security-pack' ), - self::EVENT_LOCKOUT => __( 'Lockout', 'security-pack' ), - self::EVENT_IP_BLOCKED => __( 'IP Blocked', 'security-pack' ), - self::EVENT_GEO_BLOCKED => __( 'Geo Blocked', 'security-pack' ), - self::EVENT_LOCKOUT_LIFTED => __( 'Lockout Lifted', 'security-pack' ), + self::EVENT_LOGIN_SUCCESS => __( 'Login Success', 'arkhost-security-pack' ), + self::EVENT_LOGIN_FAILED => __( 'Login Failed', 'arkhost-security-pack' ), + self::EVENT_LOCKOUT => __( 'Lockout', 'arkhost-security-pack' ), + self::EVENT_IP_BLOCKED => __( 'IP Blocked', 'arkhost-security-pack' ), + self::EVENT_GEO_BLOCKED => __( 'Geo Blocked', 'arkhost-security-pack' ), + self::EVENT_LOCKOUT_LIFTED => __( 'Lockout Lifted', 'arkhost-security-pack' ), ); return isset( $labels[ $event_type ] ) ? $labels[ $event_type ] : $event_type; diff --git a/arkhost-security-pack/includes/class-arksp-admin.php b/arkhost-security-pack/includes/class-arksp-admin.php new file mode 100644 index 0000000..2eeb969 --- /dev/null +++ b/arkhost-security-pack/includes/class-arksp-admin.php @@ -0,0 +1,2805 @@ + __( 'Status', 'arkhost-security-pack' ), + 'login' => __( 'Login Protection', 'arkhost-security-pack' ), + 'ip' => __( 'IP Control', 'arkhost-security-pack' ), + 'hardening' => __( 'Hardening', 'arkhost-security-pack' ), + 'headers' => __( 'Security Headers', 'arkhost-security-pack' ), + '2fa' => __( '2FA', 'arkhost-security-pack' ), + 'scanner' => __( 'Scanner', 'arkhost-security-pack' ), + 'logs' => __( 'Activity Log', 'arkhost-security-pack' ), + 'tools' => __( 'Tools', 'arkhost-security-pack' ), + ); + + foreach ( $submenus as $slug => $title ) { + add_submenu_page( + 'arkhost-security-pack', + $title . ' - ' . __( 'ArkHost Security Pack', 'arkhost-security-pack' ), + $title, + 'manage_options', + 'status' === $slug ? 'arkhost-security-pack' : 'arkhost-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 ( 'arkhost-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 'arkhost-security-pack'; + } + + return 'arkhost-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=arkhost-security-pack' ), + __( 'Settings', 'arkhost-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 ( ARKSP_PLUGIN_BASENAME !== $file ) { + return $links; + } + + $links[] = sprintf( + '%s', + 'https://arkhost.com/products-menu.php', + __( 'Get Hosting', 'arkhost-security-pack' ) + ); + + return $links; + } + + /** + * Add dashboard widget. + */ + public function add_dashboard_widget() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + wp_add_dashboard_widget( + 'arksp_security_widget', + __( 'Security Status', 'arkhost-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 = ARKSP_Activity_Log::get_stats( 7 ); + ?> +
+
%
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + wp_create_nonce( 'arksp_admin' ), + 'adminEmail' => get_option( 'admin_email' ), + 'currentIp' => ARKSP_Helper::get_client_ip(), + 'loginUrl' => wp_login_url(), + 'strings' => array( + 'confirmDeleteFile' => __( 'Delete this file?', 'arkhost-security-pack' ), + 'deleting' => __( 'Deleting...', 'arkhost-security-pack' ), + 'failedDeleteFile' => __( 'Failed to delete file.', 'arkhost-security-pack' ), + 'deleteBtn' => __( 'Delete', 'arkhost-security-pack' ), + 'sending' => __( 'Sending...', 'arkhost-security-pack' ), + 'testEmailSent' => __( 'Test email sent!', 'arkhost-security-pack' ), + 'requestFailed' => __( 'Request failed.', 'arkhost-security-pack' ), + 'downloading' => __( 'Downloading...', 'arkhost-security-pack' ), + 'downloadGeoDb' => __( 'Download IP2Location Lite DB', 'arkhost-security-pack' ), + 'confirmResetHeaders' => __( 'Reset all security headers to their default values?', 'arkhost-security-pack' ), + 'scanning' => __( 'Scanning...', 'arkhost-security-pack' ), + 'scanningMalware' => __( 'Scanning... This may take a while.', 'arkhost-security-pack' ), + 'confirmQuarantine' => __( 'Quarantine this file? It will be moved to a safe location and cannot execute.', 'arkhost-security-pack' ), + 'moving' => __( 'Moving...', 'arkhost-security-pack' ), + 'failedQuarantine' => __( 'Failed to quarantine file.', 'arkhost-security-pack' ), + 'quarantineBtn' => __( 'Quarantine', 'arkhost-security-pack' ), + 'confirmRestore' => __( 'Restore this file to its original location? Make sure it is safe before restoring.', 'arkhost-security-pack' ), + 'restoring' => __( 'Restoring...', 'arkhost-security-pack' ), + 'failedRestore' => __( 'Failed to restore file.', 'arkhost-security-pack' ), + 'restoreBtn' => __( 'Restore', 'arkhost-security-pack' ), + 'confirmDeletePermanent' => __( 'Permanently delete this file? This cannot be undone.', 'arkhost-security-pack' ), + 'confirmClearLogs' => __( 'Are you sure you want to clear all logs? This cannot be undone.', 'arkhost-security-pack' ), + 'ipWhitelisted' => __( 'IP added to whitelist!', 'arkhost-security-pack' ), + 'error' => __( 'Error', 'arkhost-security-pack' ), + 'confirmClearLockouts' => __( 'Are you sure you want to clear all lockouts?', 'arkhost-security-pack' ), + 'lockoutsCleared' => __( 'All lockouts cleared.', 'arkhost-security-pack' ), + 'confirmForceLogout' => __( 'Are you sure? ALL users (including you) will be logged out immediately.', 'arkhost-security-pack' ), + 'sessionsTerminated' => __( 'All sessions terminated. Redirecting to login...', 'arkhost-security-pack' ), + 'selectFileToImport' => __( 'Please select a file to import.', 'arkhost-security-pack' ), + 'settingsImported' => __( 'Settings imported successfully!', 'arkhost-security-pack' ), + 'confirmResetSettings' => __( 'Are you sure you want to reset all settings to defaults? This cannot be undone.', 'arkhost-security-pack' ), + ), + ) ); + } + } + + /** + * Render settings page. + */ + public function render_settings_page() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + // Get current tab. + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $current_tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'status'; + + $tabs = array( + 'status' => __( 'Status', 'arkhost-security-pack' ), + 'login' => __( 'Login Protection', 'arkhost-security-pack' ), + 'ip' => __( 'IP Control', 'arkhost-security-pack' ), + 'hardening' => __( 'Hardening', 'arkhost-security-pack' ), + 'headers' => __( 'Security Headers', 'arkhost-security-pack' ), + '2fa' => __( '2FA', 'arkhost-security-pack' ), + 'scanner' => __( 'Scanner', 'arkhost-security-pack' ), + 'logs' => __( 'Activity Log', 'arkhost-security-pack' ), + 'tools' => __( 'Tools', 'arkhost-security-pack' ), + ); + ?> +
+

+ + + +
+ render_status_tab(); + break; + case 'login': + $this->render_login_tab(); + break; + case 'ip': + $this->render_ip_tab(); + break; + case 'hardening': + $this->render_hardening_tab(); + break; + case 'headers': + $this->render_headers_tab(); + break; + case '2fa': + $this->render_2fa_tab(); + break; + case 'scanner': + $this->render_scanner_tab(); + break; + case 'logs': + $this->render_logs_tab(); + break; + case 'tools': + $this->render_tools_tab(); + break; + } + + if ( ! in_array( $current_tab, array( 'status', 'logs', 'tools' ), true ) ) { + submit_button(); + } + ?> +
+ + +
+ 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; + ?> +
+

+ +
+
+ % + +
+
+ + +
+
+ + +
+
+ + + + + + + + + + + + $check ) : ?> + + + + + + + + +
+ + + + + + + + + + + +
+
+ +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + +
+ + prefix ) : ?> + wp_ + + + prefix ); ?> + +
+ + + + + + +
+ + +
+
+ + +
+

+
+

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
readme.html + +
license.txt + +
+
+ + __( 'Login Attempt Limiting', 'arkhost-security-pack' ), + 'description' => __( 'Limits failed login attempts to prevent brute force attacks', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'login_limit_enabled', true ), + 'tab' => 'login', + ); + + $checks['honeypot'] = array( + 'name' => __( 'Login Honeypot', 'arkhost-security-pack' ), + 'description' => __( 'Hidden field that traps automated bots', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'honeypot_enabled', true ), + 'tab' => 'login', + ); + + $checks['hide_login_errors'] = array( + 'name' => __( 'Login Errors Hidden', 'arkhost-security-pack' ), + 'description' => __( 'Generic error message prevents username enumeration', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'hide_login_errors', true ), + 'tab' => 'login', + ); + + // Hardening. + $checks['xmlrpc'] = array( + 'name' => __( 'XML-RPC Disabled', 'arkhost-security-pack' ), + 'description' => __( 'Blocks XML-RPC endpoint commonly used in attacks', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'disable_xmlrpc', true ), + 'tab' => 'hardening', + ); + + $checks['file_editing'] = array( + 'name' => __( 'File Editor Disabled', 'arkhost-security-pack' ), + 'description' => __( 'Prevents editing theme/plugin files from dashboard', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'disable_file_editing', true ), + 'tab' => 'hardening', + ); + + $checks['rest_api'] = array( + 'name' => __( 'REST API Restricted', 'arkhost-security-pack' ), + 'description' => __( 'Requires authentication for REST API access', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'restrict_rest_api', true ), + 'tab' => 'hardening', + ); + + $checks['user_enum'] = array( + 'name' => __( 'User Enumeration Blocked', 'arkhost-security-pack' ), + 'description' => __( 'Prevents attackers from discovering usernames', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'disable_user_enumeration', true ), + 'tab' => 'hardening', + ); + + $checks['pingbacks'] = array( + 'name' => __( 'Pingbacks Disabled', 'arkhost-security-pack' ), + 'description' => __( 'Prevents pingback-based DDoS amplification', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'disable_pingbacks', true ), + 'tab' => 'hardening', + ); + + $checks['wp_version'] = array( + 'name' => __( 'WP Version Hidden', 'arkhost-security-pack' ), + 'description' => __( 'Hides WordPress version from public view', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'remove_wp_version', true ), + 'tab' => 'hardening', + ); + + $checks['security_headers'] = array( + 'name' => __( 'Security Headers', 'arkhost-security-pack' ), + 'description' => __( 'HTTP headers that enable browser security features', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::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 = ARKSP_Plugin::get_setting( 'two_factor_enabled', false ); + $two_fa_enforced = ARKSP_Plugin::get_setting( 'two_factor_enforce_admin', false ); + $checks['two_factor'] = array( + 'name' => __( 'Two-Factor Authentication', 'arkhost-security-pack' ), + 'description' => $two_fa_enabled && ! $two_fa_enforced + ? __( 'Available but not enforced for admins', 'arkhost-security-pack' ) + : __( 'Enforced for administrator accounts', 'arkhost-security-pack' ), + 'status' => $two_fa_enabled && $two_fa_enforced, + 'tab' => '2fa', + ); + + // Monitoring. + $checks['file_integrity'] = array( + 'name' => __( 'File Integrity Monitoring', 'arkhost-security-pack' ), + 'description' => __( 'Detects unauthorized changes to WordPress core', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'file_integrity_enabled', true ), + 'tab' => 'scanner', + ); + + $checks['malware_scan'] = array( + 'name' => __( 'Malware Scanning', 'arkhost-security-pack' ), + 'description' => __( 'Scans files for malicious code patterns', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'malware_scan_enabled', true ), + 'tab' => 'scanner', + ); + + // Optional features (not counted negatively if disabled). + $checks['custom_login'] = array( + 'name' => __( 'Custom Login URL', 'arkhost-security-pack' ), + 'description' => __( 'Hides wp-login.php from automated scanners', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'login_rename_enabled', false ), + 'tab' => 'login', + ); + + $checks['geo_blocking'] = array( + 'name' => __( 'Country Blocking', 'arkhost-security-pack' ), + 'description' => __( 'Blocks access from specific countries', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'geo_blocking_enabled', false ), + 'tab' => 'ip', + ); + + $checks['auto_blacklist'] = array( + 'name' => __( 'Auto-Blacklist Repeat Offenders', 'arkhost-security-pack' ), + 'description' => __( 'Permanently blocks IPs with repeated lockouts', 'arkhost-security-pack' ), + 'status' => ARKSP_Plugin::get_setting( 'auto_blacklist_enabled', false ), + 'tab' => 'ip', + ); + + // Environment checks (no tab - these are recommendations). + global $wpdb; + $checks['ssl'] = array( + 'name' => __( 'SSL/HTTPS', 'arkhost-security-pack' ), + 'description' => is_ssl() + ? __( 'Site is served over HTTPS', 'arkhost-security-pack' ) + : __( 'Site should use HTTPS for security', 'arkhost-security-pack' ), + 'status' => is_ssl(), + 'tab' => '', + ); + + $checks['debug_mode'] = array( + 'name' => __( 'Debug Mode Disabled', 'arkhost-security-pack' ), + 'description' => defined( 'WP_DEBUG' ) && WP_DEBUG + ? __( 'WP_DEBUG is enabled - disable in production', 'arkhost-security-pack' ) + : __( 'Debug mode is properly disabled', 'arkhost-security-pack' ), + 'status' => ! ( defined( 'WP_DEBUG' ) && WP_DEBUG ), + 'tab' => '', + ); + + $checks['db_prefix'] = array( + 'name' => __( 'Database Prefix Changed', 'arkhost-security-pack' ), + 'description' => 'wp_' === $wpdb->prefix + ? __( 'Using default wp_ prefix - consider changing', 'arkhost-security-pack' ) + : __( 'Using custom database prefix', 'arkhost-security-pack' ), + 'status' => 'wp_' !== $wpdb->prefix, + 'tab' => '', + ); + + $admin_user = get_user_by( 'login', 'admin' ); + $checks['admin_username'] = array( + 'name' => __( 'No "admin" Username', 'arkhost-security-pack' ), + 'description' => $admin_user + ? __( 'Default "admin" username exists - consider renaming', 'arkhost-security-pack' ) + : __( 'No user with "admin" username', 'arkhost-security-pack' ), + 'status' => ! $admin_user, + 'tab' => '', + ); + + $checks['php_version'] = array( + 'name' => __( 'PHP Version', 'arkhost-security-pack' ), + 'description' => version_compare( PHP_VERSION, '8.0', '<' ) + ? sprintf( + /* translators: %s: PHP version */ + __( 'PHP %s is outdated - update recommended', 'arkhost-security-pack' ), + PHP_VERSION + ) + : sprintf( + /* translators: %s: PHP version */ + __( 'Running PHP %s', 'arkhost-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', 'arkhost-security-pack' ), + 'description' => $auto_updates_enabled + ? __( 'WordPress core auto-updates are enabled', 'arkhost-security-pack' ) + : __( 'Enable auto-updates for security patches', 'arkhost-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', 'arkhost-security-pack' ) . '' + ); + ?> +

+
+ +
+

+
+ + + + + + + + + + + + + + +
+ +
+ + +

+
+ +

+ + ' . esc_html( ARKSP_Helper::get_client_ip() ) . '' ); + ?> +

+
+
+ +
+

+ + + + + + + + + + + + + +
+ +
+ + + +

+
+ +
+
+ +
+

+

+ + + + + + + + + + + + + +
+ +
+ +

+
+ + +
+
+ +
+

+

+ + + + + + + + + + + + + +
+ +
+ + +

+
+ +

+
+
+ +
+

+

+ + + + + + + + + +
+ +
+ + +
+
+ +
+

+ + + + + + + + + +
+ +

+
+ +
+
+ get_component( 'ip_control' ); + if ( $ip_control ) { + $blocked_ips = $ip_control->get_blocked_ips(); + } + + $geo_blocking = arksp()->get_component( 'geo_blocking' ); + $db_info = $geo_blocking ? $geo_blocking->get_database_info() : array( 'exists' => false ); + $countries = ARKSP_Helper::get_countries_with_flags(); + ?> + +
+

+

+ + +

+ + + + + + + + + + + + + +
+ +
+ + + + + + - + + + + + +

+ +

+ +

+ + + + + +

+ +

+ +
+ +

+
+
+ +
+

+

+ + + + + +
+ +

+ + ' . esc_html( ARKSP_Helper::get_client_ip() ) . '' ); + ?> +

+
+
+ +
+

+

+ + + + + + + + + + + + + +
+ +

+
+ +
+ + +
+
+ + +
+

+ + + + + + + + + + + + + + + + + + + +
ip_address ); ?>failed_attempts ); ?>lockout_until ); ?> + +
+
+ + + +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+ +

+
+ +

+
+ +

+
+ +

+
+ +

+
+ +

+
+ +

+
+ +

+
+
+ +
+

+

+ +

+ +
+ get_default_headers(); + $headers = ARKSP_Plugin::get_setting( 'security_headers', $default_headers ); + ?> + +
+

+

+
+

+ securityheaders.com' + ); + ?> + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+ +

+
+ +

+
+ +

+
+ +

+
+ +

+ + +

+

+ +

+
+ +

+
+
+ '_arksp_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 ) : ?> +
+
+ + +
+
+ + +
+
+ + + + + + + + + + + +
+ +

+
+ +

+
+
+ +
+

+

+ +
+ +
+

+
    +
  1. +
  2. + ' . esc_html__( 'Profile page', 'arkhost-security-pack' ) . '' + ); + ?> +
  3. +
  4. +
  5. +
  6. +
+
+ +
+

+

+ +

+ +
+ + get_last_scan_results(); + + $malware_scanner = new ARKSP_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 ) : ?> + + + + + + 0 ) : ?> + + +
+ + +

+
+

+
+ + + + + + + + + + + + + + + +
+

+ + +

+ 0 ) : ?> +
+

+
+ +
+ + +
+

+

+ + + + + + + + + + +
+ +

+
+ 0 ) : + ?> + + + + + + + + + + + +
+ + +

+
+

+ +

+
+ + + + + + + + + + $findings ) : ?> + + + + + + + +
+ + + + + + + + + + + +
+ + + +

+ +

+ 0 ) : ?> +
+

+
+ + + get_quarantined_files(); + if ( ! empty( $quarantined_files ) ) : + ?> +

+ + + + + + + + + + + $meta ) : ?> + + + + + + + + +
+ + +
+ +
+ +
+

+ +

+ + +

+ + + + + + + + + + + +
+
+ $per_page, + 'offset' => $offset, + 'event_type' => $filter_type, + ) ); + + $total = ARKSP_Activity_Log::get_log_count( array( 'event_type' => $filter_type ) ); + $pages = ceil( $total / $per_page ); + + $stats = ARKSP_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; + ?> + +
+

+ + + + + + + + + + +
+ + + + + get_component( 'ip_control' ); + $is_whitelisted = $ip_control && $ip_control->is_whitelisted( $current_ip ); + ?> + + + + + +
+ + + +

+ + + +
+
+ +
+

+ + + + + + + + + + + + + + +
+ +
+ + wp-content/plugins/arkhost-security-pack +
+ +
TRUNCATE TABLE prefix ); ?>arksp_lockouts; +
+
+ +
+

+ + + + + + +
+ + +

+
+
+ +
+

+ + + + + + +
+ + +

+
+
+ +
+

+ + + + + + + + + + +
+ +

+
+ + + +

+
+
+ +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + () + +
+ + + () + + + () + +
+ + + + + +
+
+ +
+

+

+ + + + + + +
+ +
+
+ __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + // Get current whitelist. + $whitelist = ARKSP_Plugin::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.', 'arkhost-security-pack' ) ) ); + } + + // Add IP to whitelist. + $whitelist_array[] = $ip; + $new_whitelist = implode( "\n", $whitelist_array ); + + ARKSP_Plugin::update_setting( 'ip_whitelist', $new_whitelist ); + + // Also remove from lockouts if present. + $ip_control = arksp()->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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + $file_integrity = new ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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 ARKSP_Malware_Scanner(); + $results = $scanner->scan_files(); + + update_option( ARKSP_Malware_Scanner::RESULTS_OPTION, $results ); + update_option( ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + $file_integrity = new ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + $scanner = new ARKSP_Malware_Scanner(); + $scanner->clear_results(); + + wp_send_json_success(); + } + + /** + * AJAX: Download GeoIP database. + */ + public function ajax_download_geo_db() { + check_ajax_referer( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + $geo_blocking = arksp()->get_component( 'geo_blocking' ); + if ( ! $geo_blocking ) { + wp_send_json_error( array( 'message' => __( 'Geo blocking not available.', 'arkhost-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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + $site_name = get_bloginfo( 'name' ); + $subject = sprintf( + /* translators: %s: Site name */ + __( '[%s] ArkHost Security Pack - Test Email', 'arkhost-security-pack' ), + $site_name + ); + + $message = sprintf( + /* translators: 1: Site name, 2: Site URL */ + __( "This is a test email from ArkHost Security Pack.\n\nIf you received this email, your email alerts are configured correctly.\n\nSite: %1\$s\nURL: %2\$s", 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + } + + /** + * AJAX: Force logout all users. + */ + public function ajax_force_logout_all() { + check_ajax_referer( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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. + ARKSP_Activity_Log::log( 'force_logout', ARKSP_Helper::get_client_ip(), wp_get_current_user()->user_login, __( 'All user sessions terminated', 'arkhost-security-pack' ) ); + + wp_send_json_success(); + } + + /** + * AJAX: Export settings. + */ + public function ajax_export_settings() { + check_ajax_referer( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + $settings = get_option( 'arksp_settings', array() ); + + wp_send_json_success( $settings ); + } + + /** + * AJAX: Import settings. + */ + public function ajax_import_settings() { + check_ajax_referer( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + $settings = json_decode( $settings_json, true ); + + if ( null === $settings ) { + wp_send_json_error( array( 'message' => __( 'Invalid JSON format.', 'arkhost-security-pack' ) ) ); + } + + // Sanitize the imported settings (individual values are sanitized here). + $sanitized = $this->sanitize_settings( $settings ); + + update_option( 'arksp_settings', $sanitized ); + + wp_send_json_success(); + } + + /** + * AJAX: Export logs as CSV. + */ + public function ajax_export_logs() { + check_ajax_referer( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + $logs = ARKSP_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( '"', '""', ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + $ip_control = arksp()->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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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}arksp_lockouts" ); + + wp_send_json_success(); + } + + /** + * AJAX: Reset all settings to defaults. + */ + public function ajax_reset_settings() { + check_ajax_referer( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) ); + } + + $defaults = ARKSP_Plugin::get_default_settings(); + update_option( 'arksp_settings', $defaults ); + + wp_send_json_success(); + } + + /** + * AJAX: Quarantine a suspicious file. + */ + public function ajax_quarantine_file() { + check_ajax_referer( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + $scanner = new ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + $scanner = new ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + $scanner = new ARKSP_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( 'arksp_admin' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + $file_path = ABSPATH . $file; + + if ( ! file_exists( $file_path ) ) { + wp_send_json_error( array( 'message' => __( 'File not found.', 'arkhost-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.', 'arkhost-security-pack' ) ) ); + } + + wp_send_json_success(); + } +} diff --git a/security-pack/includes/class-wpsp-db.php b/arkhost-security-pack/includes/class-arksp-db.php similarity index 73% rename from security-pack/includes/class-wpsp-db.php rename to arkhost-security-pack/includes/class-arksp-db.php index 4731061..9d17ae8 100644 --- a/security-pack/includes/class-wpsp-db.php +++ b/arkhost-security-pack/includes/class-arksp-db.php @@ -2,7 +2,7 @@ /** * Database management for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) { /** * Database class for table creation and management. */ -class WPSP_DB { +class ARKSP_DB { /** * Database version. @@ -29,7 +29,7 @@ class WPSP_DB { */ public static function get_log_table() { global $wpdb; - return esc_sql( $wpdb->prefix . 'wpsp_activity_log' ); + return esc_sql( $wpdb->prefix . 'arksp_activity_log' ); } /** @@ -39,7 +39,7 @@ class WPSP_DB { */ public static function get_lockout_table() { global $wpdb; - return esc_sql( $wpdb->prefix . 'wpsp_lockouts' ); + return esc_sql( $wpdb->prefix . 'arksp_lockouts' ); } /** @@ -50,7 +50,7 @@ class WPSP_DB { self::set_default_options(); // Store DB version. - update_option( 'wpsp_db_version', self::DB_VERSION ); + update_option( 'arksp_db_version', self::DB_VERSION ); // Clear rewrite rules for custom login URL. flush_rewrite_rules(); @@ -60,11 +60,11 @@ class WPSP_DB { * Check if database needs upgrading. */ public static function maybe_upgrade() { - $current_version = get_option( 'wpsp_db_version', '0' ); + $current_version = get_option( 'arksp_db_version', '0' ); if ( version_compare( $current_version, self::DB_VERSION, '<' ) ) { self::create_tables(); - update_option( 'wpsp_db_version', self::DB_VERSION ); + update_option( 'arksp_db_version', self::DB_VERSION ); } } @@ -73,7 +73,7 @@ class WPSP_DB { */ public static function deactivate() { // Clear scheduled events. - wp_clear_scheduled_hook( 'wpsp_daily_cleanup' ); + wp_clear_scheduled_hook( 'arksp_daily_cleanup' ); // Flush rewrite rules. flush_rewrite_rules(); @@ -129,11 +129,11 @@ class WPSP_DB { * Set default options on activation. */ private static function set_default_options() { - $existing = get_option( 'wpsp_settings', false ); + $existing = get_option( 'arksp_settings', false ); if ( false === $existing ) { - $defaults = Security_Pack::get_default_settings(); - update_option( 'wpsp_settings', $defaults ); + $defaults = ARKSP_Plugin::get_default_settings(); + update_option( 'arksp_settings', $defaults ); } } @@ -145,24 +145,24 @@ class WPSP_DB { // Drop tables. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange -- Uninstall cleanup. - $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}wpsp_activity_log" ); + $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}arksp_activity_log" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange -- Uninstall cleanup. - $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}wpsp_lockouts" ); + $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}arksp_lockouts" ); // Delete options. - delete_option( 'wpsp_settings' ); - delete_option( 'wpsp_db_version' ); - delete_option( 'wpsp_malware_scan_results' ); - delete_option( 'wpsp_malware_last_scan' ); - delete_option( 'wpsp_malware_hashes' ); - delete_option( 'wpsp_malware_hashes_updated' ); - delete_option( 'wpsp_malware_scan_stats' ); - delete_option( 'wpsp_quarantined_files' ); - delete_option( 'wpsp_file_scan_results' ); - delete_option( 'wpsp_file_last_scan' ); - delete_option( 'wpsp_file_baseline' ); + delete_option( 'arksp_settings' ); + delete_option( 'arksp_db_version' ); + delete_option( 'arksp_malware_scan_results' ); + delete_option( 'arksp_malware_last_scan' ); + delete_option( 'arksp_malware_hashes' ); + delete_option( 'arksp_malware_hashes_updated' ); + delete_option( 'arksp_malware_scan_stats' ); + delete_option( 'arksp_quarantined_files' ); + delete_option( 'arksp_file_scan_results' ); + delete_option( 'arksp_file_last_scan' ); + delete_option( 'arksp_file_baseline' ); // Delete transients. - delete_transient( 'wpsp_geo_cache' ); + delete_transient( 'arksp_geo_cache' ); } } diff --git a/security-pack/includes/class-wpsp-file-integrity.php b/arkhost-security-pack/includes/class-arksp-file-integrity.php similarity index 82% rename from security-pack/includes/class-wpsp-file-integrity.php rename to arkhost-security-pack/includes/class-arksp-file-integrity.php index 1677696..e2995fc 100644 --- a/security-pack/includes/class-wpsp-file-integrity.php +++ b/arkhost-security-pack/includes/class-arksp-file-integrity.php @@ -2,7 +2,7 @@ /** * File integrity monitoring for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,42 +13,42 @@ if ( ! defined( 'ABSPATH' ) ) { /** * File integrity monitoring class. */ -class WPSP_File_Integrity { +class ARKSP_File_Integrity { /** * Option key for file hashes. * * @var string */ - const HASHES_OPTION = 'wpsp_file_hashes'; + const HASHES_OPTION = 'arksp_file_hashes'; /** * Option key for last scan time. * * @var string */ - const LAST_SCAN_OPTION = 'wpsp_file_integrity_last_scan'; + const LAST_SCAN_OPTION = 'arksp_file_integrity_last_scan'; /** * Option key for changed files. * * @var string */ - const CHANGES_OPTION = 'wpsp_file_changes'; + const CHANGES_OPTION = 'arksp_file_changes'; /** * Constructor. */ public function __construct() { - if ( ! Security_Pack::get_setting( 'file_integrity_enabled', true ) ) { + if ( ! ARKSP_Plugin::get_setting( 'file_integrity_enabled', true ) ) { return; } // Schedule daily scan. - add_action( 'wpsp_daily_file_scan', array( $this, 'run_scheduled_scan' ) ); + add_action( 'arksp_daily_file_scan', array( $this, 'run_scheduled_scan' ) ); - if ( ! wp_next_scheduled( 'wpsp_daily_file_scan' ) ) { - wp_schedule_event( time(), 'daily', 'wpsp_daily_file_scan' ); + if ( ! wp_next_scheduled( 'arksp_daily_file_scan' ) ) { + wp_schedule_event( time(), 'daily', 'arksp_daily_file_scan' ); } } @@ -189,7 +189,7 @@ class WPSP_File_Integrity { $locale = get_locale(); // Try to get from cache. - $cache_key = 'wpsp_checksums_' . md5( $version . $locale ); + $cache_key = 'arksp_checksums_' . md5( $version . $locale ); $cached = get_transient( $cache_key ); if ( false !== $cached ) { @@ -256,28 +256,28 @@ class WPSP_File_Integrity { * @param array $changes File changes. */ private function send_alert( $changes ) { - if ( ! Security_Pack::get_setting( 'email_alerts_enabled', false ) ) { + if ( ! ARKSP_Plugin::get_setting( 'email_alerts_enabled', false ) ) { return; } - $email = Security_Pack::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); + $email = ARKSP_Plugin::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); $site_name = get_bloginfo( 'name' ); $site_url = home_url(); $subject = sprintf( /* translators: %s: Site name */ - __( '[%s] File Integrity Alert - Core Files Changed', 'security-pack' ), + __( '[%s] File Integrity Alert - Core Files Changed', 'arkhost-security-pack' ), $site_name ); $message = sprintf( /* translators: %s: Site URL */ - __( "Security Pack has detected changes to WordPress core files on %s.\n\n", 'security-pack' ), + __( "ArkHost Security Pack has detected changes to WordPress core files on %s.\n\n", 'arkhost-security-pack' ), $site_url ); if ( ! empty( $changes['modified'] ) ) { - $message .= __( "Modified files:\n", 'security-pack' ); + $message .= __( "Modified files:\n", 'arkhost-security-pack' ); foreach ( $changes['modified'] as $file ) { $message .= '- ' . ( is_array( $file ) ? $file['file'] : $file ) . "\n"; } @@ -285,7 +285,7 @@ class WPSP_File_Integrity { } if ( ! empty( $changes['added'] ) ) { - $message .= __( "New files detected:\n", 'security-pack' ); + $message .= __( "New files detected:\n", 'arkhost-security-pack' ); foreach ( $changes['added'] as $file ) { $message .= '- ' . $file . "\n"; } @@ -293,18 +293,18 @@ class WPSP_File_Integrity { } if ( ! empty( $changes['removed'] ) ) { - $message .= __( "Removed files:\n", 'security-pack' ); + $message .= __( "Removed files:\n", 'arkhost-security-pack' ); foreach ( $changes['removed'] as $file ) { $message .= '- ' . $file . "\n"; } $message .= "\n"; } - $message .= __( "This could indicate:\n", 'security-pack' ); - $message .= __( "- A recent WordPress update (normal)\n", 'security-pack' ); - $message .= __( "- Unauthorized modifications (investigate)\n", 'security-pack' ); - $message .= __( "- Plugin/theme conflicts (rare)\n\n", 'security-pack' ); - $message .= __( "Review these changes in your WordPress admin panel.", 'security-pack' ); + $message .= __( "This could indicate:\n", 'arkhost-security-pack' ); + $message .= __( "- A recent WordPress update (normal)\n", 'arkhost-security-pack' ); + $message .= __( "- Unauthorized modifications (investigate)\n", 'arkhost-security-pack' ); + $message .= __( "- Plugin/theme conflicts (rare)\n\n", 'arkhost-security-pack' ); + $message .= __( "Review these changes in your WordPress admin panel.", 'arkhost-security-pack' ); wp_mail( $email, $subject, $message ); } diff --git a/security-pack/includes/class-wpsp-geo-blocking.php b/arkhost-security-pack/includes/class-arksp-geo-blocking.php similarity index 81% rename from security-pack/includes/class-wpsp-geo-blocking.php rename to arkhost-security-pack/includes/class-arksp-geo-blocking.php index f8d1281..9c50b5a 100644 --- a/security-pack/includes/class-wpsp-geo-blocking.php +++ b/arkhost-security-pack/includes/class-arksp-geo-blocking.php @@ -2,7 +2,7 @@ /** * Geo-blocking for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) { /** * Geo-blocking class using IP2Location Lite database. */ -class WPSP_Geo_Blocking { +class ARKSP_Geo_Blocking { /** * IP2Location database file path. @@ -40,16 +40,17 @@ class WPSP_Geo_Blocking { * Constructor. */ public function __construct() { - $this->db_path = Security_Pack::get_setting( 'geo_database_path', '' ); + $this->db_path = ARKSP_Plugin::get_setting( 'geo_database_path', '' ); if ( empty( $this->db_path ) ) { - // Default path in plugin directory. - $this->db_path = WPSP_PLUGIN_DIR . 'data/IP2LOCATION-LITE-DB1.BIN'; + // Default path in uploads directory. + $upload_dir = wp_upload_dir(); + $this->db_path = $upload_dir['basedir'] . '/arkhost-security-pack/IP2LOCATION-LITE-DB1.BIN'; } // Check geo-blocking immediately (constructor runs during init). // This blocks access to the entire website for blocked countries. - if ( Security_Pack::get_setting( 'geo_blocking_enabled', false ) ) { + if ( ARKSP_Plugin::get_setting( 'geo_blocking_enabled', false ) ) { $this->check_geo_access(); } } @@ -58,14 +59,14 @@ class WPSP_Geo_Blocking { * Check if geo-blocking should apply. */ public function check_geo_access() { - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); if ( ! $ip ) { return; } // Check if IP is whitelisted. - $ip_control = wpsp()->get_component( 'ip_control' ); + $ip_control = arksp()->get_component( 'ip_control' ); if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) { return; } @@ -78,16 +79,16 @@ class WPSP_Geo_Blocking { } // Check if country is blocked. - $blocked_countries = Security_Pack::get_setting( 'geo_blocked_countries', array() ); + $blocked_countries = ARKSP_Plugin::get_setting( 'geo_blocked_countries', array() ); if ( ! empty( $blocked_countries ) && in_array( $country_code, $blocked_countries, true ) ) { - WPSP_Activity_Log::log( - WPSP_Activity_Log::EVENT_GEO_BLOCKED, + ARKSP_Activity_Log::log( + ARKSP_Activity_Log::EVENT_GEO_BLOCKED, $ip, null, sprintf( /* translators: %s: Country code */ - __( 'Blocked country: %s', 'security-pack' ), + __( 'Blocked country: %s', 'arkhost-security-pack' ), $country_code ) ); @@ -109,7 +110,7 @@ class WPSP_Geo_Blocking { } // Check transient cache. - $cache_key = 'wpsp_geo_' . md5( $ip ); + $cache_key = 'arksp_geo_' . md5( $ip ); $cached = get_transient( $cache_key ); if ( false !== $cached ) { @@ -151,8 +152,8 @@ class WPSP_Geo_Blocking { // Load the database reader. if ( null === $this->db ) { - require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-ip2location.php'; - $this->db = new WPSP_IP2Location( $this->db_path ); + require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-ip2location.php'; + $this->db = new ARKSP_IP2Location( $this->db_path ); } if ( ! $this->db ) { @@ -177,12 +178,12 @@ class WPSP_Geo_Blocking { status_header( 403 ); nocache_headers(); - $countries = WPSP_Helper::get_countries(); + $countries = ARKSP_Helper::get_countries(); $country_name = isset( $countries[ $country_code ] ) ? $countries[ $country_code ] : $country_code; $message = sprintf( /* translators: %s: Country name */ - __( 'Access from %s is not permitted.', 'security-pack' ), + __( 'Access from %s is not permitted.', 'arkhost-security-pack' ), $country_name ); @@ -192,7 +193,7 @@ class WPSP_Geo_Blocking { wp_die( esc_html( $message ), - esc_html__( 'Access Denied', 'security-pack' ), + esc_html__( 'Access Denied', 'arkhost-security-pack' ), array( 'response' => 403, 'back_link' => false, @@ -230,8 +231,8 @@ class WPSP_Geo_Blocking { // Try to get database type. if ( null === $this->db ) { - require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-ip2location.php'; - $this->db = new WPSP_IP2Location( $this->db_path ); + require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-ip2location.php'; + $this->db = new ARKSP_IP2Location( $this->db_path ); } if ( $this->db ) { @@ -257,8 +258,9 @@ class WPSP_Geo_Blocking { $download_url = 'https://www.ip2location.com/download/?token=' . urlencode( $download_token ) . '&file=DB1LITEBIN'; } - // Create data directory. - $data_dir = WPSP_PLUGIN_DIR . 'data'; + // Create data directory in uploads. + $upload_dir = wp_upload_dir(); + $data_dir = $upload_dir['basedir'] . '/arkhost-security-pack'; if ( ! file_exists( $data_dir ) ) { wp_mkdir_p( $data_dir ); } @@ -303,10 +305,10 @@ class WPSP_Geo_Blocking { if ( file_exists( $target_path ) ) { // Update database path setting. - Security_Pack::update_setting( 'geo_database_path', $target_path ); + ARKSP_Plugin::update_setting( 'geo_database_path', $target_path ); return true; } - return new WP_Error( 'download_failed', __( 'Failed to download or extract database.', 'security-pack' ) ); + return new WP_Error( 'download_failed', __( 'Failed to download or extract database.', 'arkhost-security-pack' ) ); } } diff --git a/security-pack/includes/class-wpsp-hardening.php b/arkhost-security-pack/includes/class-arksp-hardening.php similarity index 89% rename from security-pack/includes/class-wpsp-hardening.php rename to arkhost-security-pack/includes/class-arksp-hardening.php index 4469f82..2588ee4 100644 --- a/security-pack/includes/class-wpsp-hardening.php +++ b/arkhost-security-pack/includes/class-arksp-hardening.php @@ -2,7 +2,7 @@ /** * Security hardening for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,26 +13,26 @@ if ( ! defined( 'ABSPATH' ) ) { /** * Hardening class for security enhancements. */ -class WPSP_Hardening { +class ARKSP_Hardening { /** * Constructor. */ public function __construct() { // Disable XML-RPC. - if ( Security_Pack::get_setting( 'disable_xmlrpc', true ) ) { + if ( ARKSP_Plugin::get_setting( 'disable_xmlrpc', true ) ) { add_filter( 'xmlrpc_enabled', '__return_false' ); add_filter( 'wp_headers', array( $this, 'remove_xmlrpc_headers' ) ); add_action( 'wp', array( $this, 'block_xmlrpc_requests' ), 1 ); } // Disable file editing. - if ( Security_Pack::get_setting( 'disable_file_editing', true ) ) { + if ( ARKSP_Plugin::get_setting( 'disable_file_editing', true ) ) { $this->disable_file_editing(); } // Remove WordPress version. - if ( Security_Pack::get_setting( 'remove_wp_version', true ) ) { + if ( ARKSP_Plugin::get_setting( 'remove_wp_version', true ) ) { add_filter( 'the_generator', '__return_empty_string' ); remove_action( 'wp_head', 'wp_generator' ); add_filter( 'style_loader_src', array( $this, 'remove_version_strings' ), 10, 2 ); @@ -40,17 +40,17 @@ class WPSP_Hardening { } // Add security headers. - if ( Security_Pack::get_setting( 'add_security_headers', true ) ) { + if ( ARKSP_Plugin::get_setting( 'add_security_headers', true ) ) { add_action( 'send_headers', array( $this, 'add_security_headers' ) ); } // Restrict REST API. - if ( Security_Pack::get_setting( 'restrict_rest_api', true ) ) { + if ( ARKSP_Plugin::get_setting( 'restrict_rest_api', true ) ) { add_filter( 'rest_authentication_errors', array( $this, 'restrict_rest_api' ) ); } // Disable application passwords for non-admins. - if ( Security_Pack::get_setting( 'disable_application_passwords', false ) ) { + if ( ARKSP_Plugin::get_setting( 'disable_application_passwords', false ) ) { add_filter( 'wp_is_application_passwords_available', '__return_false' ); } @@ -58,14 +58,14 @@ class WPSP_Hardening { add_action( 'init', array( $this, 'remove_unnecessary_headers' ) ); // Disable user enumeration. - if ( Security_Pack::get_setting( 'disable_user_enumeration', true ) ) { + if ( ARKSP_Plugin::get_setting( 'disable_user_enumeration', true ) ) { add_action( 'init', array( $this, 'block_author_scanning' ) ); add_filter( 'rest_endpoints', array( $this, 'restrict_users_endpoint' ) ); add_filter( 'oembed_response_data', array( $this, 'remove_author_from_oembed' ) ); } // Disable pingbacks/trackbacks. - if ( Security_Pack::get_setting( 'disable_pingbacks', true ) ) { + if ( ARKSP_Plugin::get_setting( 'disable_pingbacks', true ) ) { add_filter( 'xmlrpc_methods', array( $this, 'disable_pingback_methods' ) ); add_filter( 'wp_headers', array( $this, 'remove_pingback_header' ) ); add_filter( 'pings_open', '__return_false', 20, 2 ); @@ -131,7 +131,7 @@ class WPSP_Hardening { } // Get header settings. - $headers = Security_Pack::get_setting( 'security_headers', $this->get_default_headers() ); + $headers = ARKSP_Plugin::get_setting( 'security_headers', $this->get_default_headers() ); // X-Content-Type-Options. if ( ! empty( $headers['x_content_type_options'] ) ) { @@ -204,7 +204,7 @@ class WPSP_Hardening { } // Get allowed REST routes. - $allowed_routes = Security_Pack::get_setting( 'rest_api_allowed_routes', array() ); + $allowed_routes = ARKSP_Plugin::get_setting( 'rest_api_allowed_routes', array() ); // Always allow some essential routes. $essential_routes = array( @@ -227,7 +227,7 @@ class WPSP_Hardening { // Block unauthenticated access. return new WP_Error( 'rest_not_logged_in', - __( 'You must be authenticated to access this endpoint.', 'security-pack' ), + __( 'You must be authenticated to access this endpoint.', 'arkhost-security-pack' ), array( 'status' => 401 ) ); } @@ -246,7 +246,7 @@ class WPSP_Hardening { remove_action( 'wp_head', 'wp_shortlink_wp_head' ); // Remove feed links. - if ( Security_Pack::get_setting( 'remove_feed_links', false ) ) { + if ( ARKSP_Plugin::get_setting( 'remove_feed_links', false ) ) { remove_action( 'wp_head', 'feed_links', 2 ); remove_action( 'wp_head', 'feed_links_extra', 3 ); } diff --git a/arkhost-security-pack/includes/class-arksp-helper.php b/arkhost-security-pack/includes/class-arksp-helper.php new file mode 100644 index 0000000..dae910a --- /dev/null +++ b/arkhost-security-pack/includes/class-arksp-helper.php @@ -0,0 +1,555 @@ + 0 && $full_bytes < 16 ) { + $bit_mask = ( ( 1 << $remaining_bits ) - 1 ) << ( 8 - $remaining_bits ); + if ( ( ord( $ip_bin[ $full_bytes ] ) & $bit_mask ) !== ( ord( $network_bin[ $full_bytes ] ) & $bit_mask ) ) { + return false; + } + } + + return true; + } + + /** + * Check if an IP matches any rule in a list. + * + * @param string $ip IP address to check. + * @param array $rules Array of IP addresses or CIDR ranges. + * @return bool + */ + public static function ip_matches_rules( $ip, $rules ) { + if ( empty( $rules ) || ! is_array( $rules ) ) { + return false; + } + + foreach ( $rules as $rule ) { + $rule = trim( $rule ); + if ( empty( $rule ) || strpos( $rule, '#' ) === 0 ) { + continue; // Skip empty lines and comments. + } + + if ( self::ip_in_cidr( $ip, $rule ) ) { + return true; + } + } + + return false; + } + + /** + * Parse IP list from textarea. + * + * @param string $text Textarea content with IPs/CIDRs. + * @return array + */ + public static function parse_ip_list( $text ) { + if ( empty( $text ) ) { + return array(); + } + + $lines = explode( "\n", $text ); + $ips = array(); + + foreach ( $lines as $line ) { + $line = trim( $line ); + + // Skip empty lines and comments. + if ( empty( $line ) || strpos( $line, '#' ) === 0 ) { + continue; + } + + // Remove inline comments. + if ( strpos( $line, '#' ) !== false ) { + $line = trim( substr( $line, 0, strpos( $line, '#' ) ) ); + } + + // Validate IP or CIDR. + if ( self::is_valid_ip_or_cidr( $line ) ) { + $ips[] = $line; + } + } + + return $ips; + } + + /** + * Check if a string is a valid IP or CIDR. + * + * @param string $value Value to check. + * @return bool + */ + public static function is_valid_ip_or_cidr( $value ) { + // Plain IP. + if ( filter_var( $value, FILTER_VALIDATE_IP ) ) { + return true; + } + + // CIDR notation. + if ( strpos( $value, '/' ) !== false ) { + list( $ip, $mask ) = explode( '/', $value ); + + if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) { + return false; + } + + $mask = (int) $mask; + $is_ipv6 = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ); + $max_mask = $is_ipv6 ? 128 : 32; + + return $mask >= 0 && $mask <= $max_mask; + } + + return false; + } + + /** + * Get country list for geo-blocking UI. + * + * @return array + */ + public static function get_countries() { + return array( + 'AF' => __( 'Afghanistan', 'arkhost-security-pack' ), + 'AL' => __( 'Albania', 'arkhost-security-pack' ), + 'DZ' => __( 'Algeria', 'arkhost-security-pack' ), + 'AS' => __( 'American Samoa', 'arkhost-security-pack' ), + 'AD' => __( 'Andorra', 'arkhost-security-pack' ), + 'AO' => __( 'Angola', 'arkhost-security-pack' ), + 'AI' => __( 'Anguilla', 'arkhost-security-pack' ), + 'AQ' => __( 'Antarctica', 'arkhost-security-pack' ), + 'AG' => __( 'Antigua and Barbuda', 'arkhost-security-pack' ), + 'AR' => __( 'Argentina', 'arkhost-security-pack' ), + 'AM' => __( 'Armenia', 'arkhost-security-pack' ), + 'AW' => __( 'Aruba', 'arkhost-security-pack' ), + 'AU' => __( 'Australia', 'arkhost-security-pack' ), + 'AT' => __( 'Austria', 'arkhost-security-pack' ), + 'AZ' => __( 'Azerbaijan', 'arkhost-security-pack' ), + 'BS' => __( 'Bahamas', 'arkhost-security-pack' ), + 'BH' => __( 'Bahrain', 'arkhost-security-pack' ), + 'BD' => __( 'Bangladesh', 'arkhost-security-pack' ), + 'BB' => __( 'Barbados', 'arkhost-security-pack' ), + 'BY' => __( 'Belarus', 'arkhost-security-pack' ), + 'BE' => __( 'Belgium', 'arkhost-security-pack' ), + 'BZ' => __( 'Belize', 'arkhost-security-pack' ), + 'BJ' => __( 'Benin', 'arkhost-security-pack' ), + 'BM' => __( 'Bermuda', 'arkhost-security-pack' ), + 'BT' => __( 'Bhutan', 'arkhost-security-pack' ), + 'BO' => __( 'Bolivia', 'arkhost-security-pack' ), + 'BA' => __( 'Bosnia and Herzegovina', 'arkhost-security-pack' ), + 'BW' => __( 'Botswana', 'arkhost-security-pack' ), + 'BR' => __( 'Brazil', 'arkhost-security-pack' ), + 'BN' => __( 'Brunei', 'arkhost-security-pack' ), + 'BG' => __( 'Bulgaria', 'arkhost-security-pack' ), + 'BF' => __( 'Burkina Faso', 'arkhost-security-pack' ), + 'BI' => __( 'Burundi', 'arkhost-security-pack' ), + 'KH' => __( 'Cambodia', 'arkhost-security-pack' ), + 'CM' => __( 'Cameroon', 'arkhost-security-pack' ), + 'CA' => __( 'Canada', 'arkhost-security-pack' ), + 'CV' => __( 'Cape Verde', 'arkhost-security-pack' ), + 'KY' => __( 'Cayman Islands', 'arkhost-security-pack' ), + 'CF' => __( 'Central African Republic', 'arkhost-security-pack' ), + 'TD' => __( 'Chad', 'arkhost-security-pack' ), + 'CL' => __( 'Chile', 'arkhost-security-pack' ), + 'CN' => __( 'China', 'arkhost-security-pack' ), + 'CO' => __( 'Colombia', 'arkhost-security-pack' ), + 'KM' => __( 'Comoros', 'arkhost-security-pack' ), + 'CG' => __( 'Congo', 'arkhost-security-pack' ), + 'CD' => __( 'Congo (DRC)', 'arkhost-security-pack' ), + 'CR' => __( 'Costa Rica', 'arkhost-security-pack' ), + 'CI' => __( 'Ivory Coast', 'arkhost-security-pack' ), + 'HR' => __( 'Croatia', 'arkhost-security-pack' ), + 'CU' => __( 'Cuba', 'arkhost-security-pack' ), + 'CY' => __( 'Cyprus', 'arkhost-security-pack' ), + 'CZ' => __( 'Czech Republic', 'arkhost-security-pack' ), + 'DK' => __( 'Denmark', 'arkhost-security-pack' ), + 'DJ' => __( 'Djibouti', 'arkhost-security-pack' ), + 'DM' => __( 'Dominica', 'arkhost-security-pack' ), + 'DO' => __( 'Dominican Republic', 'arkhost-security-pack' ), + 'EC' => __( 'Ecuador', 'arkhost-security-pack' ), + 'EG' => __( 'Egypt', 'arkhost-security-pack' ), + 'SV' => __( 'El Salvador', 'arkhost-security-pack' ), + 'GQ' => __( 'Equatorial Guinea', 'arkhost-security-pack' ), + 'ER' => __( 'Eritrea', 'arkhost-security-pack' ), + 'EE' => __( 'Estonia', 'arkhost-security-pack' ), + 'ET' => __( 'Ethiopia', 'arkhost-security-pack' ), + 'FJ' => __( 'Fiji', 'arkhost-security-pack' ), + 'FI' => __( 'Finland', 'arkhost-security-pack' ), + 'FR' => __( 'France', 'arkhost-security-pack' ), + 'GA' => __( 'Gabon', 'arkhost-security-pack' ), + 'GM' => __( 'Gambia', 'arkhost-security-pack' ), + 'GE' => __( 'Georgia', 'arkhost-security-pack' ), + 'DE' => __( 'Germany', 'arkhost-security-pack' ), + 'GH' => __( 'Ghana', 'arkhost-security-pack' ), + 'GR' => __( 'Greece', 'arkhost-security-pack' ), + 'GL' => __( 'Greenland', 'arkhost-security-pack' ), + 'GD' => __( 'Grenada', 'arkhost-security-pack' ), + 'GU' => __( 'Guam', 'arkhost-security-pack' ), + 'GT' => __( 'Guatemala', 'arkhost-security-pack' ), + 'GN' => __( 'Guinea', 'arkhost-security-pack' ), + 'GW' => __( 'Guinea-Bissau', 'arkhost-security-pack' ), + 'GY' => __( 'Guyana', 'arkhost-security-pack' ), + 'HT' => __( 'Haiti', 'arkhost-security-pack' ), + 'HN' => __( 'Honduras', 'arkhost-security-pack' ), + 'HK' => __( 'Hong Kong', 'arkhost-security-pack' ), + 'HU' => __( 'Hungary', 'arkhost-security-pack' ), + 'IS' => __( 'Iceland', 'arkhost-security-pack' ), + 'IN' => __( 'India', 'arkhost-security-pack' ), + 'ID' => __( 'Indonesia', 'arkhost-security-pack' ), + 'IR' => __( 'Iran', 'arkhost-security-pack' ), + 'IQ' => __( 'Iraq', 'arkhost-security-pack' ), + 'IE' => __( 'Ireland', 'arkhost-security-pack' ), + 'IL' => __( 'Israel', 'arkhost-security-pack' ), + 'IT' => __( 'Italy', 'arkhost-security-pack' ), + 'JM' => __( 'Jamaica', 'arkhost-security-pack' ), + 'JP' => __( 'Japan', 'arkhost-security-pack' ), + 'JO' => __( 'Jordan', 'arkhost-security-pack' ), + 'KZ' => __( 'Kazakhstan', 'arkhost-security-pack' ), + 'KE' => __( 'Kenya', 'arkhost-security-pack' ), + 'KI' => __( 'Kiribati', 'arkhost-security-pack' ), + 'KP' => __( 'North Korea', 'arkhost-security-pack' ), + 'KR' => __( 'South Korea', 'arkhost-security-pack' ), + 'KW' => __( 'Kuwait', 'arkhost-security-pack' ), + 'KG' => __( 'Kyrgyzstan', 'arkhost-security-pack' ), + 'LA' => __( 'Laos', 'arkhost-security-pack' ), + 'LV' => __( 'Latvia', 'arkhost-security-pack' ), + 'LB' => __( 'Lebanon', 'arkhost-security-pack' ), + 'LS' => __( 'Lesotho', 'arkhost-security-pack' ), + 'LR' => __( 'Liberia', 'arkhost-security-pack' ), + 'LY' => __( 'Libya', 'arkhost-security-pack' ), + 'LI' => __( 'Liechtenstein', 'arkhost-security-pack' ), + 'LT' => __( 'Lithuania', 'arkhost-security-pack' ), + 'LU' => __( 'Luxembourg', 'arkhost-security-pack' ), + 'MO' => __( 'Macao', 'arkhost-security-pack' ), + 'MK' => __( 'North Macedonia', 'arkhost-security-pack' ), + 'MG' => __( 'Madagascar', 'arkhost-security-pack' ), + 'MW' => __( 'Malawi', 'arkhost-security-pack' ), + 'MY' => __( 'Malaysia', 'arkhost-security-pack' ), + 'MV' => __( 'Maldives', 'arkhost-security-pack' ), + 'ML' => __( 'Mali', 'arkhost-security-pack' ), + 'MT' => __( 'Malta', 'arkhost-security-pack' ), + 'MH' => __( 'Marshall Islands', 'arkhost-security-pack' ), + 'MR' => __( 'Mauritania', 'arkhost-security-pack' ), + 'MU' => __( 'Mauritius', 'arkhost-security-pack' ), + 'MX' => __( 'Mexico', 'arkhost-security-pack' ), + 'FM' => __( 'Micronesia', 'arkhost-security-pack' ), + 'MD' => __( 'Moldova', 'arkhost-security-pack' ), + 'MC' => __( 'Monaco', 'arkhost-security-pack' ), + 'MN' => __( 'Mongolia', 'arkhost-security-pack' ), + 'ME' => __( 'Montenegro', 'arkhost-security-pack' ), + 'MA' => __( 'Morocco', 'arkhost-security-pack' ), + 'MZ' => __( 'Mozambique', 'arkhost-security-pack' ), + 'MM' => __( 'Myanmar', 'arkhost-security-pack' ), + 'NA' => __( 'Namibia', 'arkhost-security-pack' ), + 'NR' => __( 'Nauru', 'arkhost-security-pack' ), + 'NP' => __( 'Nepal', 'arkhost-security-pack' ), + 'NL' => __( 'Netherlands', 'arkhost-security-pack' ), + 'NZ' => __( 'New Zealand', 'arkhost-security-pack' ), + 'NI' => __( 'Nicaragua', 'arkhost-security-pack' ), + 'NE' => __( 'Niger', 'arkhost-security-pack' ), + 'NG' => __( 'Nigeria', 'arkhost-security-pack' ), + 'NO' => __( 'Norway', 'arkhost-security-pack' ), + 'OM' => __( 'Oman', 'arkhost-security-pack' ), + 'PK' => __( 'Pakistan', 'arkhost-security-pack' ), + 'PW' => __( 'Palau', 'arkhost-security-pack' ), + 'PS' => __( 'Palestine', 'arkhost-security-pack' ), + 'PA' => __( 'Panama', 'arkhost-security-pack' ), + 'PG' => __( 'Papua New Guinea', 'arkhost-security-pack' ), + 'PY' => __( 'Paraguay', 'arkhost-security-pack' ), + 'PE' => __( 'Peru', 'arkhost-security-pack' ), + 'PH' => __( 'Philippines', 'arkhost-security-pack' ), + 'PL' => __( 'Poland', 'arkhost-security-pack' ), + 'PT' => __( 'Portugal', 'arkhost-security-pack' ), + 'PR' => __( 'Puerto Rico', 'arkhost-security-pack' ), + 'QA' => __( 'Qatar', 'arkhost-security-pack' ), + 'RO' => __( 'Romania', 'arkhost-security-pack' ), + 'RU' => __( 'Russia', 'arkhost-security-pack' ), + 'RW' => __( 'Rwanda', 'arkhost-security-pack' ), + 'SA' => __( 'Saudi Arabia', 'arkhost-security-pack' ), + 'SN' => __( 'Senegal', 'arkhost-security-pack' ), + 'RS' => __( 'Serbia', 'arkhost-security-pack' ), + 'SC' => __( 'Seychelles', 'arkhost-security-pack' ), + 'SL' => __( 'Sierra Leone', 'arkhost-security-pack' ), + 'SG' => __( 'Singapore', 'arkhost-security-pack' ), + 'SK' => __( 'Slovakia', 'arkhost-security-pack' ), + 'SI' => __( 'Slovenia', 'arkhost-security-pack' ), + 'SB' => __( 'Solomon Islands', 'arkhost-security-pack' ), + 'SO' => __( 'Somalia', 'arkhost-security-pack' ), + 'ZA' => __( 'South Africa', 'arkhost-security-pack' ), + 'SS' => __( 'South Sudan', 'arkhost-security-pack' ), + 'ES' => __( 'Spain', 'arkhost-security-pack' ), + 'LK' => __( 'Sri Lanka', 'arkhost-security-pack' ), + 'SD' => __( 'Sudan', 'arkhost-security-pack' ), + 'SR' => __( 'Suriname', 'arkhost-security-pack' ), + 'SZ' => __( 'Eswatini', 'arkhost-security-pack' ), + 'SE' => __( 'Sweden', 'arkhost-security-pack' ), + 'CH' => __( 'Switzerland', 'arkhost-security-pack' ), + 'SY' => __( 'Syria', 'arkhost-security-pack' ), + 'TW' => __( 'Taiwan', 'arkhost-security-pack' ), + 'TJ' => __( 'Tajikistan', 'arkhost-security-pack' ), + 'TZ' => __( 'Tanzania', 'arkhost-security-pack' ), + 'TH' => __( 'Thailand', 'arkhost-security-pack' ), + 'TL' => __( 'Timor-Leste', 'arkhost-security-pack' ), + 'TG' => __( 'Togo', 'arkhost-security-pack' ), + 'TO' => __( 'Tonga', 'arkhost-security-pack' ), + 'TT' => __( 'Trinidad and Tobago', 'arkhost-security-pack' ), + 'TN' => __( 'Tunisia', 'arkhost-security-pack' ), + 'TR' => __( 'Turkey', 'arkhost-security-pack' ), + 'TM' => __( 'Turkmenistan', 'arkhost-security-pack' ), + 'TV' => __( 'Tuvalu', 'arkhost-security-pack' ), + 'UG' => __( 'Uganda', 'arkhost-security-pack' ), + 'UA' => __( 'Ukraine', 'arkhost-security-pack' ), + 'AE' => __( 'United Arab Emirates', 'arkhost-security-pack' ), + 'GB' => __( 'United Kingdom', 'arkhost-security-pack' ), + 'US' => __( 'United States', 'arkhost-security-pack' ), + 'UY' => __( 'Uruguay', 'arkhost-security-pack' ), + 'UZ' => __( 'Uzbekistan', 'arkhost-security-pack' ), + 'VU' => __( 'Vanuatu', 'arkhost-security-pack' ), + 'VE' => __( 'Venezuela', 'arkhost-security-pack' ), + 'VN' => __( 'Vietnam', 'arkhost-security-pack' ), + 'YE' => __( 'Yemen', 'arkhost-security-pack' ), + 'ZM' => __( 'Zambia', 'arkhost-security-pack' ), + 'ZW' => __( 'Zimbabwe', 'arkhost-security-pack' ), + ); + } + + /** + * Sanitize a custom login URL slug. + * + * @param string $slug The slug to sanitize. + * @return string + */ + public static function sanitize_login_slug( $slug ) { + $slug = sanitize_title( $slug ); + $slug = preg_replace( '/[^a-z0-9\-]/', '', $slug ); + + // Prevent common reserved slugs. + $reserved = array( 'wp-admin', 'wp-login', 'admin', 'login', 'wp-content', 'wp-includes' ); + if ( in_array( $slug, $reserved, true ) ) { + return ''; + } + + return $slug; + } + + /** + * Get the server's public IP address. + * + * Uses external service to determine the server's outbound IP. + * Result is cached for 1 hour to avoid excessive external requests. + * + * @return string|null Server IP or null on failure. + */ + public static function get_server_ip() { + $cached = get_transient( 'arksp_server_ip' ); + if ( false !== $cached ) { + return $cached; + } + + // Try multiple services for reliability. + $services = array( + 'https://api.ipify.org', + 'https://ifconfig.me/ip', + 'https://icanhazip.com', + ); + + $ip = null; + + foreach ( $services as $service ) { + $response = wp_remote_get( + $service, + array( + 'timeout' => 5, + 'sslverify' => true, + ) + ); + + if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) { + $body = trim( wp_remote_retrieve_body( $response ) ); + if ( filter_var( $body, FILTER_VALIDATE_IP ) ) { + $ip = $body; + break; + } + } + } + + if ( $ip ) { + // Cache for 1 hour. + set_transient( 'arksp_server_ip', $ip, HOUR_IN_SECONDS ); + } + + return $ip; + } + + /** + * Get country flag emoji from country code. + * + * Converts ISO 3166-1 alpha-2 country codes to Unicode flag emojis. + * + * @param string $country_code Two-letter country code (e.g., "US", "GB"). + * @return string Flag emoji or globe emoji for invalid codes. + */ + public static function get_country_flag( $country_code ) { + if ( empty( $country_code ) || strlen( $country_code ) !== 2 ) { + return '🌐'; // Globe emoji for invalid codes. + } + + $country_code = strtoupper( $country_code ); + + // Convert country code to Unicode regional indicator symbols. + // Regional indicators are U+1F1E6 (A) through U+1F1FF (Z). + $first_letter = mb_chr( ord( $country_code[0] ) - ord( 'A' ) + 0x1F1E6, 'UTF-8' ); + $second_letter = mb_chr( ord( $country_code[1] ) - ord( 'A' ) + 0x1F1E6, 'UTF-8' ); + + return $first_letter . $second_letter; + } + + /** + * Get countries array with flags. + * + * @return array Country code => "Flag Name" format. + */ + public static function get_countries_with_flags() { + $countries = self::get_countries(); + $countries_flags = array(); + + foreach ( $countries as $code => $name ) { + $flag = self::get_country_flag( $code ); + $countries_flags[ $code ] = $flag . ' ' . $name; + } + + return $countries_flags; + } +} diff --git a/security-pack/includes/class-wpsp-ip-control.php b/arkhost-security-pack/includes/class-arksp-ip-control.php similarity index 81% rename from security-pack/includes/class-wpsp-ip-control.php rename to arkhost-security-pack/includes/class-arksp-ip-control.php index 13c227a..e584a24 100644 --- a/security-pack/includes/class-wpsp-ip-control.php +++ b/arkhost-security-pack/includes/class-arksp-ip-control.php @@ -2,7 +2,7 @@ /** * IP access control for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) { /** * IP control class for whitelist/blacklist management. */ -class WPSP_IP_Control { +class ARKSP_IP_Control { /** * Cached whitelist IPs. @@ -42,7 +42,7 @@ class WPSP_IP_Control { * Check IP access on every request. */ public function check_ip_access() { - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); if ( ! $ip ) { return; @@ -55,14 +55,14 @@ class WPSP_IP_Control { // Block blacklisted IPs. if ( $this->is_blacklisted( $ip ) ) { - WPSP_Activity_Log::log( WPSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, __( 'IP blacklisted', 'security-pack' ) ); - $this->block_access( __( 'Your IP address has been blocked.', 'security-pack' ) ); + ARKSP_Activity_Log::log( ARKSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, __( 'IP blacklisted', 'arkhost-security-pack' ) ); + $this->block_access( __( 'Your IP address has been blocked.', 'arkhost-security-pack' ) ); } // Check auto-blocked IPs. if ( $this->is_auto_blocked( $ip ) ) { - WPSP_Activity_Log::log( WPSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, __( 'IP auto-blocked', 'security-pack' ) ); - $this->block_access( __( 'Your IP address has been temporarily blocked due to suspicious activity.', 'security-pack' ) ); + ARKSP_Activity_Log::log( ARKSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, __( 'IP auto-blocked', 'arkhost-security-pack' ) ); + $this->block_access( __( 'Your IP address has been temporarily blocked due to suspicious activity.', 'arkhost-security-pack' ) ); } } @@ -74,7 +74,7 @@ class WPSP_IP_Control { */ public function is_whitelisted( $ip ) { $whitelist = $this->get_whitelist(); - return WPSP_Helper::ip_matches_rules( $ip, $whitelist ); + return ARKSP_Helper::ip_matches_rules( $ip, $whitelist ); } /** @@ -85,7 +85,7 @@ class WPSP_IP_Control { */ public function is_blacklisted( $ip ) { $blacklist = $this->get_blacklist(); - return WPSP_Helper::ip_matches_rules( $ip, $blacklist ); + return ARKSP_Helper::ip_matches_rules( $ip, $blacklist ); } /** @@ -97,13 +97,13 @@ class WPSP_IP_Control { public function is_auto_blocked( $ip ) { global $wpdb; - $max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); - $lockout_minutes = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); + $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 ); + $lockout_minutes = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $lockout = $wpdb->get_row( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", + "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s", $ip ) ); @@ -141,8 +141,8 @@ class WPSP_IP_Control { */ public function get_whitelist() { if ( null === $this->whitelist_cache ) { - $whitelist_text = Security_Pack::get_setting( 'ip_whitelist', '' ); - $this->whitelist_cache = WPSP_Helper::parse_ip_list( $whitelist_text ); + $whitelist_text = ARKSP_Plugin::get_setting( 'ip_whitelist', '' ); + $this->whitelist_cache = ARKSP_Helper::parse_ip_list( $whitelist_text ); } return $this->whitelist_cache; } @@ -154,8 +154,8 @@ class WPSP_IP_Control { */ public function get_blacklist() { if ( null === $this->blacklist_cache ) { - $blacklist_text = Security_Pack::get_setting( 'ip_blacklist', '' ); - $this->blacklist_cache = WPSP_Helper::parse_ip_list( $blacklist_text ); + $blacklist_text = ARKSP_Plugin::get_setting( 'ip_blacklist', '' ); + $this->blacklist_cache = ARKSP_Helper::parse_ip_list( $blacklist_text ); } return $this->blacklist_cache; } @@ -186,7 +186,7 @@ class WPSP_IP_Control { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $existing = $wpdb->get_var( $wpdb->prepare( - "SELECT id FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", + "SELECT id FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s", $ip ) ); @@ -195,7 +195,7 @@ class WPSP_IP_Control { // Update existing record. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return false !== $wpdb->update( - $wpdb->prefix . 'wpsp_lockouts', + $wpdb->prefix . 'arksp_lockouts', array( 'lockout_until' => $lockout_until, 'updated_at' => current_time( 'mysql' ), @@ -209,7 +209,7 @@ class WPSP_IP_Control { // Insert new record. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return false !== $wpdb->insert( - $wpdb->prefix . 'wpsp_lockouts', + $wpdb->prefix . 'arksp_lockouts', array( 'ip_address' => $ip, 'failed_attempts' => 0, @@ -232,7 +232,7 @@ class WPSP_IP_Control { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return false !== $wpdb->delete( - $wpdb->prefix . 'wpsp_lockouts', + $wpdb->prefix . 'arksp_lockouts', array( 'ip_address' => $ip ), array( '%s' ) ); @@ -246,13 +246,13 @@ class WPSP_IP_Control { public function get_blocked_ips() { global $wpdb; - $max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); - $lockout_minutes = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); + $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 ); + $lockout_minutes = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 ); $current_time = time(); // Get all lockout records. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. - $lockouts = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wpsp_lockouts ORDER BY updated_at DESC" ); + $lockouts = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}arksp_lockouts ORDER BY updated_at DESC" ); $blocked = array(); foreach ( $lockouts as $lockout ) { @@ -306,7 +306,7 @@ class WPSP_IP_Control { // Simple blocked page. wp_die( esc_html( $message ), - esc_html__( 'Access Denied', 'security-pack' ), + esc_html__( 'Access Denied', 'arkhost-security-pack' ), array( 'response' => 403, 'back_link' => false, diff --git a/security-pack/includes/class-wpsp-ip2location.php b/arkhost-security-pack/includes/class-arksp-ip2location.php similarity index 99% rename from security-pack/includes/class-wpsp-ip2location.php rename to arkhost-security-pack/includes/class-arksp-ip2location.php index 11ff516..bdb21ed 100644 --- a/security-pack/includes/class-wpsp-ip2location.php +++ b/arkhost-security-pack/includes/class-arksp-ip2location.php @@ -4,7 +4,7 @@ * * Reads IP2Location BIN format databases. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -17,7 +17,7 @@ if ( ! defined( 'ABSPATH' ) ) { * * Based on IP2Location PHP Module but simplified for our needs. */ -class WPSP_IP2Location { +class ARKSP_IP2Location { /** * Database file handle. diff --git a/security-pack/includes/class-wpsp-login-protection.php b/arkhost-security-pack/includes/class-arksp-login-protection.php similarity index 78% rename from security-pack/includes/class-wpsp-login-protection.php rename to arkhost-security-pack/includes/class-arksp-login-protection.php index 5eeaed5..42d7cbe 100644 --- a/security-pack/includes/class-wpsp-login-protection.php +++ b/arkhost-security-pack/includes/class-arksp-login-protection.php @@ -2,7 +2,7 @@ /** * Login protection for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) { /** * Login protection class. */ -class WPSP_Login_Protection { +class ARKSP_Login_Protection { /** * Custom login URL slug. @@ -27,7 +27,7 @@ class WPSP_Login_Protection { * * @var string */ - private $cookie_name = 'wpsp_login_access'; + private $cookie_name = 'arksp_login_access'; /** * Constructor. @@ -48,7 +48,7 @@ class WPSP_Login_Protection { // Honeypot field. // NOTE: Must be registered BEFORE handle_custom_login_slug_early() which // may load wp-login.php and exit, preventing any later hook registrations. - if ( Security_Pack::get_setting( 'honeypot_enabled', true ) ) { + if ( ARKSP_Plugin::get_setting( 'honeypot_enabled', true ) ) { add_action( 'login_form', array( $this, 'add_honeypot_field' ) ); add_action( 'register_form', array( $this, 'add_honeypot_field' ) ); add_filter( 'authenticate', array( $this, 'check_honeypot' ), 1, 3 ); @@ -58,7 +58,7 @@ class WPSP_Login_Protection { // Hide login error messages (prevents username enumeration). // NOTE: Must be registered BEFORE handle_custom_login_slug_early() which // may load wp-login.php and exit, preventing any later hook registrations. - if ( Security_Pack::get_setting( 'hide_login_errors', true ) ) { + if ( ARKSP_Plugin::get_setting( 'hide_login_errors', true ) ) { // Replace specific auth errors at the source (runs after WP's authenticate at priority 20). add_filter( 'authenticate', array( $this, 'genericize_auth_error' ), PHP_INT_MAX, 3 ); // Also filter the rendered error string as a safety net. @@ -68,8 +68,8 @@ class WPSP_Login_Protection { // Custom login URL - must run very early before WordPress processes the request. // WARNING: handle_custom_login_slug_early() may require wp-login.php and exit. // All login-related hooks MUST be registered above this point. - if ( Security_Pack::get_setting( 'login_rename_enabled', false ) ) { - $this->custom_login_slug = Security_Pack::get_setting( 'login_custom_url', '' ); + if ( ARKSP_Plugin::get_setting( 'login_rename_enabled', false ) ) { + $this->custom_login_slug = ARKSP_Plugin::get_setting( 'login_custom_url', '' ); if ( ! empty( $this->custom_login_slug ) ) { // Handle custom login slug immediately (constructor runs during plugins_loaded). $this->handle_custom_login_slug_early(); @@ -83,7 +83,7 @@ class WPSP_Login_Protection { } // Hide wp-admin for non-logged-in users. - if ( Security_Pack::get_setting( 'hide_wp_admin', false ) ) { + if ( ARKSP_Plugin::get_setting( 'hide_wp_admin', false ) ) { // Must run early - WordPress redirects wp-admin to wp-login.php before init. $this->block_wp_admin_early(); add_action( 'init', array( $this, 'hide_wp_admin' ), 1 ); @@ -118,11 +118,11 @@ class WPSP_Login_Protection { */ private function should_block_direct_wp_login() { // Only applies when custom login URL is enabled. - if ( ! Security_Pack::get_setting( 'login_rename_enabled', false ) ) { + if ( ! ARKSP_Plugin::get_setting( 'login_rename_enabled', false ) ) { return false; } - $custom_slug = Security_Pack::get_setting( 'login_custom_url', '' ); + $custom_slug = ARKSP_Plugin::get_setting( 'login_custom_url', '' ); if ( empty( $custom_slug ) ) { return false; } @@ -141,8 +141,8 @@ class WPSP_Login_Protection { // Allow if user has the access cookie (visited custom login URL before). // This allows all WordPress flows (password reset, etc.) to work normally. - $expected_value = wp_hash( 'wpsp_login_' . $custom_slug ); - $cookie_value = isset( $_COOKIE['wpsp_login_access'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['wpsp_login_access'] ) ) : ''; + $expected_value = wp_hash( 'arksp_login_' . $custom_slug ); + $cookie_value = isset( $_COOKIE['arksp_login_access'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['arksp_login_access'] ) ) : ''; if ( $cookie_value === $expected_value ) { return false; @@ -158,27 +158,27 @@ class WPSP_Login_Protection { * @return bool True if access should be denied. */ private function is_admin_access_restricted() { - if ( ! Security_Pack::get_setting( 'admin_access_restriction', false ) ) { + if ( ! ARKSP_Plugin::get_setting( 'admin_access_restriction', false ) ) { return false; } - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); if ( ! $ip ) { return false; } // Check if IP is whitelisted (always allow). - $ip_control = wpsp()->get_component( 'ip_control' ); + $ip_control = arksp()->get_component( 'ip_control' ); if ( ! $ip_control ) { - $ip_control = new WPSP_IP_Control(); + $ip_control = new ARKSP_IP_Control(); } if ( $ip_control->is_whitelisted( $ip ) ) { return false; } - $allowed_countries = Security_Pack::get_setting( 'admin_allowed_countries', array() ); - $allowed_ips = Security_Pack::get_setting( 'admin_allowed_ips', '' ); - $allowed_ip_list = WPSP_Helper::parse_ip_list( $allowed_ips ); + $allowed_countries = ARKSP_Plugin::get_setting( 'admin_allowed_countries', array() ); + $allowed_ips = ARKSP_Plugin::get_setting( 'admin_allowed_ips', '' ); + $allowed_ip_list = ARKSP_Helper::parse_ip_list( $allowed_ips ); $has_country_restriction = ! empty( $allowed_countries ); $has_ip_restriction = ! empty( $allowed_ip_list ); @@ -191,15 +191,15 @@ class WPSP_Login_Protection { $access_granted = false; // Check IP restriction. - if ( $has_ip_restriction && WPSP_Helper::ip_matches_rules( $ip, $allowed_ip_list ) ) { + if ( $has_ip_restriction && ARKSP_Helper::ip_matches_rules( $ip, $allowed_ip_list ) ) { $access_granted = true; } // Check country restriction. if ( ! $access_granted && $has_country_restriction ) { - $geo_blocking = wpsp()->get_component( 'geo_blocking' ); + $geo_blocking = arksp()->get_component( 'geo_blocking' ); if ( ! $geo_blocking ) { - $geo_blocking = new WPSP_Geo_Blocking(); + $geo_blocking = new ARKSP_Geo_Blocking(); } $country_code = $geo_blocking->get_country_code( $ip ); @@ -216,22 +216,22 @@ class WPSP_Login_Protection { * Show admin access denied message and exit. */ private function show_admin_access_denied_message() { - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); // Log the blocked access attempt. - WPSP_Activity_Log::log( - WPSP_Activity_Log::EVENT_IP_BLOCKED, + ARKSP_Activity_Log::log( + ARKSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, - __( 'Admin access denied: country/IP not allowed', 'security-pack' ) + __( 'Admin access denied: country/IP not allowed', 'arkhost-security-pack' ) ); status_header( 403 ); nocache_headers(); wp_die( - esc_html__( 'Admin access is not permitted from your location.', 'security-pack' ), - esc_html__( 'Access Denied', 'security-pack' ), + esc_html__( 'Admin access is not permitted from your location.', 'arkhost-security-pack' ), + esc_html__( 'Access Denied', 'arkhost-security-pack' ), array( 'response' => 403, 'back_link' => false, @@ -245,31 +245,31 @@ class WPSP_Login_Protection { * @param string $username Username attempted. */ public function handle_failed_login( $username ) { - if ( ! Security_Pack::get_setting( 'login_limit_enabled', true ) ) { + if ( ! ARKSP_Plugin::get_setting( 'login_limit_enabled', true ) ) { return; } - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); if ( ! $ip ) { return; } // Check if IP is whitelisted. - $ip_control = wpsp()->get_component( 'ip_control' ); + $ip_control = arksp()->get_component( 'ip_control' ); if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) { - WPSP_Activity_Log::log( WPSP_Activity_Log::EVENT_LOGIN_FAILED, $ip, $username, __( 'Failed login (whitelisted IP)', 'security-pack' ) ); + ARKSP_Activity_Log::log( ARKSP_Activity_Log::EVENT_LOGIN_FAILED, $ip, $username, __( 'Failed login (whitelisted IP)', 'arkhost-security-pack' ) ); return; } // Log the failed attempt. - WPSP_Activity_Log::log( WPSP_Activity_Log::EVENT_LOGIN_FAILED, $ip, $username ); + ARKSP_Activity_Log::log( ARKSP_Activity_Log::EVENT_LOGIN_FAILED, $ip, $username ); // Increment failed attempts counter. $attempts = $this->increment_failed_attempts( $ip ); // Check if lockout threshold reached. - $max_attempts = Security_Pack::get_setting( 'login_max_attempts', 5 ); + $max_attempts = ARKSP_Plugin::get_setting( 'login_max_attempts', 5 ); if ( $attempts >= $max_attempts ) { $this->lockout_ip( $ip ); @@ -286,16 +286,16 @@ class WPSP_Login_Protection { * @param WP_User $user User object. */ public function handle_successful_login( $user_login, $user ) { - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); // Log the successful login. - WPSP_Activity_Log::log( WPSP_Activity_Log::EVENT_LOGIN_SUCCESS, $ip, $user_login ); + ARKSP_Activity_Log::log( ARKSP_Activity_Log::EVENT_LOGIN_SUCCESS, $ip, $user_login ); // Clear failed attempts for this IP. $this->clear_failed_attempts( $ip ); // Send admin login notification if enabled. - if ( Security_Pack::get_setting( 'admin_login_notify', false ) && user_can( $user, 'manage_options' ) ) { + if ( ARKSP_Plugin::get_setting( 'admin_login_notify', false ) && user_can( $user, 'manage_options' ) ) { $this->send_admin_login_notification( $user, $ip ); } } @@ -307,13 +307,13 @@ class WPSP_Login_Protection { * @param string $ip IP address. */ private function send_admin_login_notification( $user, $ip ) { - $email = Security_Pack::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); + $email = ARKSP_Plugin::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); if ( empty( $email ) ) { return; } // Check if this is a new IP for this user. - $known_ips = get_user_meta( $user->ID, '_wpsp_known_ips', true ); + $known_ips = get_user_meta( $user->ID, '_arksp_known_ips', true ); if ( ! is_array( $known_ips ) ) { $known_ips = array(); } @@ -325,7 +325,7 @@ class WPSP_Login_Protection { if ( $is_new_ip ) { $known_ips[] = $ip; $known_ips = array_slice( $known_ips, -10 ); - update_user_meta( $user->ID, '_wpsp_known_ips', $known_ips ); + update_user_meta( $user->ID, '_arksp_known_ips', $known_ips ); } // Only send notification for new IPs. @@ -336,24 +336,24 @@ class WPSP_Login_Protection { $site_name = get_bloginfo( 'name' ); $subject = sprintf( /* translators: %s: Site name */ - __( '[%s] Admin Login from New IP', 'security-pack' ), + __( '[%s] Admin Login from New IP', 'arkhost-security-pack' ), $site_name ); $message = sprintf( /* translators: 1: Username, 2: Site name */ - __( 'An administrator account logged in to %2$s from a new IP address.', 'security-pack' ), + __( 'An administrator account logged in to %2$s from a new IP address.', 'arkhost-security-pack' ), $user->user_login, $site_name ) . "\n\n"; /* translators: %s: Username */ - $message .= sprintf( __( 'Username: %s', 'security-pack' ), $user->user_login ) . "\n"; + $message .= sprintf( __( 'Username: %s', 'arkhost-security-pack' ), $user->user_login ) . "\n"; /* translators: %s: IP address */ - $message .= sprintf( __( 'IP Address: %s', 'security-pack' ), $ip ) . "\n"; + $message .= sprintf( __( 'IP Address: %s', 'arkhost-security-pack' ), $ip ) . "\n"; /* translators: %s: Login time */ - $message .= sprintf( __( 'Time: %s', 'security-pack' ), current_time( 'mysql' ) ) . "\n\n"; - $message .= __( 'If this was not you, please secure your account immediately.', 'security-pack' ) . "\n"; + $message .= sprintf( __( 'Time: %s', 'arkhost-security-pack' ), current_time( 'mysql' ) ) . "\n\n"; + $message .= __( 'If this was not you, please secure your account immediately.', 'arkhost-security-pack' ) . "\n"; wp_mail( $email, $subject, $message ); } @@ -366,7 +366,7 @@ class WPSP_Login_Protection { */ public function hide_login_errors( $error ) { // Return generic message instead of revealing if username or password was wrong. - return __( 'Invalid username or password.', 'security-pack' ); + return __( 'Invalid username or password.', 'arkhost-security-pack' ); } /** @@ -388,7 +388,7 @@ class WPSP_Login_Protection { if ( in_array( $user->get_error_code(), $specific_codes, true ) ) { return new WP_Error( 'authentication_failed', - __( 'Invalid username or password.', 'security-pack' ) + __( 'Invalid username or password.', 'arkhost-security-pack' ) ); } } @@ -405,7 +405,7 @@ class WPSP_Login_Protection { * @return WP_User|WP_Error|null */ public function check_lockout( $user, $username, $password ) { - if ( ! Security_Pack::get_setting( 'login_limit_enabled', true ) ) { + if ( ! ARKSP_Plugin::get_setting( 'login_limit_enabled', true ) ) { return $user; } @@ -413,22 +413,22 @@ class WPSP_Login_Protection { return $user; } - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); if ( ! $ip ) { return $user; } // Check if IP is whitelisted. - $ip_control = wpsp()->get_component( 'ip_control' ); + $ip_control = arksp()->get_component( 'ip_control' ); if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) { return $user; } // Check if IP is locked out. $lockout = $this->get_lockout( $ip ); - $max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); - $lockout_duration = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); + $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 ); + $lockout_duration = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 ); if ( $lockout && (int) $lockout->failed_attempts >= $max_attempts ) { // Calculate lockout expiry from updated_at + duration. @@ -442,10 +442,10 @@ class WPSP_Login_Protection { $remaining = human_time_diff( $current_time, $lockout_expires ); return new WP_Error( - 'wpsp_locked_out', + 'arksp_locked_out', sprintf( /* translators: %s: Time remaining */ - __( 'Too many failed login attempts. Please try again in %s.', 'security-pack' ), + __( 'Too many failed login attempts. Please try again in %s.', 'arkhost-security-pack' ), $remaining ) ); @@ -470,7 +470,7 @@ class WPSP_Login_Protection { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $existing = $wpdb->get_row( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", + "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s", $ip ) ); @@ -480,7 +480,7 @@ class WPSP_Login_Protection { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $wpdb->update( - $wpdb->prefix . 'wpsp_lockouts', + $wpdb->prefix . 'arksp_lockouts', array( 'failed_attempts' => $new_count, 'updated_at' => current_time( 'mysql' ), @@ -496,7 +496,7 @@ class WPSP_Login_Protection { // Create new record. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $wpdb->insert( - $wpdb->prefix . 'wpsp_lockouts', + $wpdb->prefix . 'arksp_lockouts', array( 'ip_address' => $ip, 'failed_attempts' => 1, @@ -518,7 +518,7 @@ class WPSP_Login_Protection { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $wpdb->delete( - $wpdb->prefix . 'wpsp_lockouts', + $wpdb->prefix . 'arksp_lockouts', array( 'ip_address' => $ip ), array( '%s' ) ); @@ -532,13 +532,13 @@ class WPSP_Login_Protection { private function lockout_ip( $ip ) { global $wpdb; - $duration = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); + $duration = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 ); // Store Unix timestamp to avoid timezone issues. $lockout_until = time() + ( $duration * 60 ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $wpdb->update( - $wpdb->prefix . 'wpsp_lockouts', + $wpdb->prefix . 'arksp_lockouts', array( 'lockout_until' => $lockout_until, 'updated_at' => current_time( 'mysql' ), @@ -549,13 +549,13 @@ class WPSP_Login_Protection { ); // Log the lockout. - WPSP_Activity_Log::log( - WPSP_Activity_Log::EVENT_LOCKOUT, + ARKSP_Activity_Log::log( + ARKSP_Activity_Log::EVENT_LOCKOUT, $ip, null, sprintf( /* translators: %d: Duration in minutes */ - __( 'Locked out for %d minutes', 'security-pack' ), + __( 'Locked out for %d minutes', 'arkhost-security-pack' ), $duration ) ); @@ -573,11 +573,11 @@ class WPSP_Login_Protection { * @param string $ip IP address. */ private function maybe_auto_blacklist( $ip ) { - if ( ! Security_Pack::get_setting( 'auto_blacklist_enabled', false ) ) { + if ( ! ARKSP_Plugin::get_setting( 'auto_blacklist_enabled', false ) ) { return; } - $threshold = (int) Security_Pack::get_setting( 'auto_blacklist_threshold', 3 ); + $threshold = (int) ARKSP_Plugin::get_setting( 'auto_blacklist_threshold', 3 ); if ( $threshold < 1 ) { return; } @@ -589,13 +589,13 @@ class WPSP_Login_Protection { $this->add_ip_to_blacklist( $ip ); // Log the auto-blacklist. - WPSP_Activity_Log::log( - WPSP_Activity_Log::EVENT_IP_BLOCKED, + ARKSP_Activity_Log::log( + ARKSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, sprintf( /* translators: %d: Number of lockouts */ - __( 'Auto-blacklisted after %d lockouts', 'security-pack' ), + __( 'Auto-blacklisted after %d lockouts', 'arkhost-security-pack' ), $lockout_count ) ); @@ -617,9 +617,9 @@ class WPSP_Login_Protection { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $count = $wpdb->get_var( $wpdb->prepare( - "SELECT COUNT(*) FROM {$wpdb->prefix}wpsp_activity_log WHERE ip_address = %s AND event_type = %s", + "SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log WHERE ip_address = %s AND event_type = %s", $ip, - WPSP_Activity_Log::EVENT_LOCKOUT + ARKSP_Activity_Log::EVENT_LOCKOUT ) ); @@ -632,7 +632,7 @@ class WPSP_Login_Protection { * @param string $ip IP address. */ private function add_ip_to_blacklist( $ip ) { - $current_blacklist = Security_Pack::get_setting( 'ip_blacklist', '' ); + $current_blacklist = ARKSP_Plugin::get_setting( 'ip_blacklist', '' ); // Check if IP is already in the blacklist. $blacklist_array = array_filter( array_map( 'trim', explode( "\n", $current_blacklist ) ) ); @@ -644,10 +644,10 @@ class WPSP_Login_Protection { $blacklist_array[] = $ip; $new_blacklist = implode( "\n", $blacklist_array ); - Security_Pack::update_setting( 'ip_blacklist', $new_blacklist ); + ARKSP_Plugin::update_setting( 'ip_blacklist', $new_blacklist ); // Clear the IP control cache. - $ip_control = wpsp()->get_component( 'ip_control' ); + $ip_control = arksp()->get_component( 'ip_control' ); if ( $ip_control ) { $ip_control->clear_cache(); } @@ -665,7 +665,7 @@ class WPSP_Login_Protection { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. return $wpdb->get_row( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", + "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s", $ip ) ); @@ -698,7 +698,7 @@ class WPSP_Login_Protection { if ( $is_wp_admin && ! $is_ajax ) { // Redirect to a non-existent URL to trigger WordPress's themed 404 page. $home = get_option( 'home', '' ); - $fake_url = rtrim( $home, '/' ) . '/wpsp-404-' . wp_rand( 1000, 9999 ); + $fake_url = rtrim( $home, '/' ) . '/arksp-404-' . wp_rand( 1000, 9999 ); header( 'Location: ' . $fake_url, true, 302 ); exit; } @@ -725,22 +725,14 @@ class WPSP_Login_Protection { // Set a flag that user accessed via custom login slug. // This will be checked by restrict_wp_login on subsequent requests. - $cookie_value = wp_hash( 'wpsp_login_' . $this->custom_login_slug ); - - // Define cookie constants if not already defined. - if ( ! defined( 'COOKIEPATH' ) ) { - define( 'COOKIEPATH', '/' ); - } - if ( ! defined( 'COOKIE_DOMAIN' ) ) { - define( 'COOKIE_DOMAIN', '' ); - } + $cookie_value = wp_hash( 'arksp_login_' . $this->custom_login_slug ); setcookie( $this->cookie_name, $cookie_value, time() + DAY_IN_SECONDS, - COOKIEPATH, - COOKIE_DOMAIN, + defined( 'COOKIEPATH' ) ? COOKIEPATH : '/', + defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '', is_ssl(), true ); @@ -759,17 +751,17 @@ class WPSP_Login_Protection { * @return bool */ private function is_ip_locked_out_early() { - if ( ! Security_Pack::get_setting( 'login_limit_enabled', true ) ) { + if ( ! ARKSP_Plugin::get_setting( 'login_limit_enabled', true ) ) { return false; } - $ip = WPSP_Helper::get_client_ip(); + $ip = ARKSP_Helper::get_client_ip(); if ( ! $ip ) { return false; } // Check whitelist first. - $ip_control = wpsp()->get_component( 'ip_control' ); + $ip_control = arksp()->get_component( 'ip_control' ); if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) { return false; } @@ -780,7 +772,7 @@ class WPSP_Login_Protection { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. $lockout = $wpdb->get_row( $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", + "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s", $ip ) ); @@ -789,8 +781,8 @@ class WPSP_Login_Protection { return false; } - $max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); - $lockout_minutes = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); + $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 ); + $lockout_minutes = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 ); $current_time = time(); // Check if IP has reached max attempts and is still within lockout window. @@ -823,10 +815,10 @@ class WPSP_Login_Protection { wp_die( sprintf( /* translators: %d: Lockout duration in minutes */ - esc_html__( 'Too many failed login attempts. Please try again in %d minutes.', 'security-pack' ), - absint( Security_Pack::get_setting( 'login_lockout_duration', 15 ) ) + esc_html__( 'Too many failed login attempts. Please try again in %d minutes.', 'arkhost-security-pack' ), + absint( ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 ) ) ), - esc_html__( 'Access Denied', 'security-pack' ), + esc_html__( 'Access Denied', 'arkhost-security-pack' ), array( 'response' => 403, 'back_link' => false, @@ -873,7 +865,7 @@ class WPSP_Login_Protection { } // Allow if user has the access cookie (came through custom login slug before). - $expected_value = wp_hash( 'wpsp_login_' . $this->custom_login_slug ); + $expected_value = wp_hash( 'arksp_login_' . $this->custom_login_slug ); $cookie_value = isset( $_COOKIE[ $this->cookie_name ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ $this->cookie_name ] ) ) : ''; if ( $cookie_value === $expected_value ) { @@ -912,7 +904,7 @@ class WPSP_Login_Protection { $home_url = home_url( '/' ); // Use a non-existent URL to trigger WordPress 404. - $fake_404_url = home_url( '/wpsp-not-found-' . wp_rand( 1000, 9999 ) . '/' ); + $fake_404_url = home_url( '/arksp-not-found-' . wp_rand( 1000, 9999 ) . '/' ); status_header( 404 ); nocache_headers(); @@ -990,7 +982,7 @@ class WPSP_Login_Protection { // If redirect_to contains wp-admin and hide_wp_admin is enabled, block with 404. // This prevents /wp-admin from revealing the custom login URL. // Use get_option directly to avoid any potential issues with class method. - $settings = get_option( 'wpsp_settings', array() ); + $settings = get_option( 'arksp_settings', array() ); $hide_wp_admin = ! empty( $settings['hide_wp_admin'] ); $is_wp_admin_redirect = strpos( $redirect_to, 'wp-admin' ) !== false && strpos( $redirect_to, 'admin-ajax.php' ) === false; @@ -1026,8 +1018,8 @@ class WPSP_Login_Protection { include $template; } else { wp_die( - esc_html__( 'Page not found.', 'security-pack' ), - esc_html__( '404 Not Found', 'security-pack' ), + esc_html__( 'Page not found.', 'arkhost-security-pack' ), + esc_html__( '404 Not Found', 'arkhost-security-pack' ), array( 'response' => 404 ) ); } @@ -1041,9 +1033,9 @@ class WPSP_Login_Protection { public function add_honeypot_field() { // Honeypot field - hidden via CSS, bots will fill it. ?> -

- - +

+ +

get_component( 'ip_control' ); - $ban_duration = (int) Security_Pack::get_setting( 'honeypot_ban_duration', 60 ); + $ip_control = arksp()->get_component( 'ip_control' ); + $ban_duration = (int) ARKSP_Plugin::get_setting( 'honeypot_ban_duration', 60 ); if ( $ip_control ) { - $ip_control->auto_block_ip( $ip, $ban_duration, __( 'Honeypot triggered', 'security-pack' ) ); + $ip_control->auto_block_ip( $ip, $ban_duration, __( 'Honeypot triggered', 'arkhost-security-pack' ) ); } return new WP_Error( - 'wpsp_honeypot', - __( 'Authentication failed.', 'security-pack' ) + 'arksp_honeypot', + __( 'Authentication failed.', 'arkhost-security-pack' ) ); } @@ -1094,18 +1086,18 @@ class WPSP_Login_Protection { */ public function check_honeypot_registration( $errors, $sanitized_user_login, $user_email ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing - if ( ! empty( $_POST['wpsp_hp_email'] ) ) { + if ( ! empty( $_POST['arksp_hp_email'] ) ) { $errors->add( - 'wpsp_honeypot', - __( 'Registration failed.', 'security-pack' ) + 'arksp_honeypot', + __( 'Registration failed.', 'arkhost-security-pack' ) ); // Auto-block this IP using configurable duration. - $ip = WPSP_Helper::get_client_ip(); - $ip_control = wpsp()->get_component( 'ip_control' ); - $ban_duration = (int) Security_Pack::get_setting( 'honeypot_ban_duration', 60 ); + $ip = ARKSP_Helper::get_client_ip(); + $ip_control = arksp()->get_component( 'ip_control' ); + $ban_duration = (int) ARKSP_Plugin::get_setting( 'honeypot_ban_duration', 60 ); if ( $ip_control && $ip ) { - $ip_control->auto_block_ip( $ip, $ban_duration, __( 'Honeypot triggered on registration', 'security-pack' ) ); + $ip_control->auto_block_ip( $ip, $ban_duration, __( 'Honeypot triggered on registration', 'arkhost-security-pack' ) ); } } @@ -1121,11 +1113,11 @@ class WPSP_Login_Protection { * @param int $attempts Number of attempts (optional). */ private function maybe_send_alert( $type, $ip, $username = '', $attempts = 0 ) { - if ( ! Security_Pack::get_setting( 'email_alerts_enabled', false ) ) { + if ( ! ARKSP_Plugin::get_setting( 'email_alerts_enabled', false ) ) { return; } - $email = Security_Pack::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); + $email = ARKSP_Plugin::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); if ( empty( $email ) ) { return; } @@ -1135,23 +1127,23 @@ class WPSP_Login_Protection { switch ( $type ) { case 'failed_login': - $threshold = Security_Pack::get_setting( 'email_alert_threshold', 3 ); + $threshold = ARKSP_Plugin::get_setting( 'email_alert_threshold', 3 ); if ( $attempts < $threshold ) { return; } $subject = sprintf( /* translators: %s: Site name */ - __( '[%s] Multiple Failed Login Attempts', 'security-pack' ), + __( '[%s] Multiple Failed Login Attempts', 'arkhost-security-pack' ), $site_name ); $message = sprintf( /* translators: 1: Number of attempts, 2: IP address, 3: Username, 4: Site URL */ - __( "There have been %1\$d failed login attempts on your site.\n\nIP Address: %2\$s\nUsername attempted: %3\$s\nSite: %4\$s\n\nIf this wasn't you, the IP will be automatically locked out after reaching the threshold.", 'security-pack' ), + __( "There have been %1\$d failed login attempts on your site.\n\nIP Address: %2\$s\nUsername attempted: %3\$s\nSite: %4\$s\n\nIf this wasn't you, the IP will be automatically locked out after reaching the threshold.", 'arkhost-security-pack' ), $attempts, $ip, - $username ? $username : __( '(empty)', 'security-pack' ), + $username ? $username : __( '(empty)', 'arkhost-security-pack' ), $site_url ); break; @@ -1159,13 +1151,13 @@ class WPSP_Login_Protection { case 'lockout': $subject = sprintf( /* translators: %s: Site name */ - __( '[%s] IP Address Locked Out', 'security-pack' ), + __( '[%s] IP Address Locked Out', 'arkhost-security-pack' ), $site_name ); $message = sprintf( /* translators: 1: IP address, 2: Site URL */ - __( "An IP address has been locked out due to too many failed login attempts.\n\nIP Address: %1\$s\nSite: %2\$s", 'security-pack' ), + __( "An IP address has been locked out due to too many failed login attempts.\n\nIP Address: %1\$s\nSite: %2\$s", 'arkhost-security-pack' ), $ip, $site_url ); @@ -1174,13 +1166,13 @@ class WPSP_Login_Protection { case 'auto_blacklist': $subject = sprintf( /* translators: %s: Site name */ - __( '[%s] IP Permanently Blacklisted', 'security-pack' ), + __( '[%s] IP Permanently Blacklisted', 'arkhost-security-pack' ), $site_name ); $message = sprintf( /* translators: 1: IP address, 2: Number of lockouts, 3: Site URL */ - __( "An IP address has been permanently added to your blacklist due to repeated lockouts.\n\nIP Address: %1\$s\nTotal Lockouts: %2\$d\nSite: %3\$s\n\nThis IP will no longer be able to access your website.", 'security-pack' ), + __( "An IP address has been permanently added to your blacklist due to repeated lockouts.\n\nIP Address: %1\$s\nTotal Lockouts: %2\$d\nSite: %3\$s\n\nThis IP will no longer be able to access your website.", 'arkhost-security-pack' ), $ip, $attempts, $site_url diff --git a/security-pack/includes/class-wpsp-malware-scanner.php b/arkhost-security-pack/includes/class-arksp-malware-scanner.php similarity index 90% rename from security-pack/includes/class-wpsp-malware-scanner.php rename to arkhost-security-pack/includes/class-arksp-malware-scanner.php index 2f80e15..8c111e4 100644 --- a/security-pack/includes/class-wpsp-malware-scanner.php +++ b/arkhost-security-pack/includes/class-arksp-malware-scanner.php @@ -2,7 +2,7 @@ /** * Malware scanner for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -19,35 +19,35 @@ if ( ! defined( 'ABSPATH' ) ) { * * Only scans plugins, themes, and uploads - NOT WordPress core. */ -class WPSP_Malware_Scanner { +class ARKSP_Malware_Scanner { /** * Option key for scan results. * * @var string */ - const RESULTS_OPTION = 'wpsp_malware_scan_results'; + const RESULTS_OPTION = 'arksp_malware_scan_results'; /** * Option key for last scan time. * * @var string */ - const LAST_SCAN_OPTION = 'wpsp_malware_last_scan'; + const LAST_SCAN_OPTION = 'arksp_malware_last_scan'; /** * Option key for malware hash database. * * @var string */ - const HASH_DB_OPTION = 'wpsp_malware_hashes'; + const HASH_DB_OPTION = 'arksp_malware_hashes'; /** * Option key for hash database last update. * * @var string */ - const HASH_DB_UPDATED_OPTION = 'wpsp_malware_hashes_updated'; + const HASH_DB_UPDATED_OPTION = 'arksp_malware_hashes_updated'; /** * Malware signatures (patterns to detect). @@ -70,7 +70,7 @@ class WPSP_Malware_Scanner { */ private $skip_paths = array( // This plugin's own files. - 'security-pack', + 'arkhost-security-pack', // Common libraries that legitimately use shell/network functions. 'phpseclib', @@ -93,15 +93,15 @@ class WPSP_Malware_Scanner { $this->load_signatures(); $this->load_malware_hashes(); - if ( ! Security_Pack::get_setting( 'malware_scan_enabled', true ) ) { + if ( ! ARKSP_Plugin::get_setting( 'malware_scan_enabled', true ) ) { return; } // Schedule weekly scan. - add_action( 'wpsp_weekly_malware_scan', array( $this, 'run_scheduled_scan' ) ); + add_action( 'arksp_weekly_malware_scan', array( $this, 'run_scheduled_scan' ) ); - if ( ! wp_next_scheduled( 'wpsp_weekly_malware_scan' ) ) { - wp_schedule_event( time(), 'weekly', 'wpsp_weekly_malware_scan' ); + if ( ! wp_next_scheduled( 'arksp_weekly_malware_scan' ) ) { + wp_schedule_event( time(), 'weekly', 'arksp_weekly_malware_scan' ); } } @@ -110,14 +110,14 @@ class WPSP_Malware_Scanner { * * Hashes can come from: * 1. Local database (user-added) - * 2. Custom filter (wpsp_malware_hashes) + * 2. Custom filter (arksp_malware_hashes) */ private function load_malware_hashes() { // Load from database (user-added or previously downloaded). $this->malware_hashes = get_option( self::HASH_DB_OPTION, array() ); // Allow adding custom hashes via filter. - $this->malware_hashes = apply_filters( 'wpsp_malware_hashes', $this->malware_hashes ); + $this->malware_hashes = apply_filters( 'arksp_malware_hashes', $this->malware_hashes ); } /** @@ -160,7 +160,7 @@ class WPSP_Malware_Scanner { if ( empty( $source_url ) ) { // Default: Could be a GitHub raw URL or your own endpoint. // For now, just return - users can provide their own source. - return new WP_Error( 'no_source', __( 'No hash database source URL provided.', 'security-pack' ) ); + return new WP_Error( 'no_source', __( 'No hash database source URL provided.', 'arkhost-security-pack' ) ); } $response = wp_remote_get( $source_url, array( 'timeout' => 30 ) ); @@ -173,7 +173,7 @@ class WPSP_Malware_Scanner { $data = json_decode( $body, true ); if ( ! is_array( $data ) ) { - return new WP_Error( 'invalid_data', __( 'Invalid hash database format.', 'security-pack' ) ); + return new WP_Error( 'invalid_data', __( 'Invalid hash database format.', 'arkhost-security-pack' ) ); } // Merge with existing hashes. @@ -510,7 +510,7 @@ class WPSP_Malware_Scanner { ); // Allow adding custom signatures via filter. - $this->signatures = apply_filters( 'wpsp_malware_signatures', $this->signatures ); + $this->signatures = apply_filters( 'arksp_malware_signatures', $this->signatures ); } /** @@ -614,7 +614,7 @@ class WPSP_Malware_Scanner { // Save scan stats. update_option( self::LAST_SCAN_OPTION, time() ); update_option( - 'wpsp_malware_scan_stats', + 'arksp_malware_scan_stats', array( 'files_scanned' => $file_count, 'duration' => round( $duration, 2 ), @@ -693,11 +693,11 @@ class WPSP_Malware_Scanner { * @param array $results Scan results. */ private function send_alert( $results ) { - if ( ! Security_Pack::get_setting( 'email_alerts_enabled', false ) ) { + if ( ! ARKSP_Plugin::get_setting( 'email_alerts_enabled', false ) ) { return; } - $email = Security_Pack::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); + $email = ARKSP_Plugin::get_setting( 'email_alerts_address', get_option( 'admin_email' ) ); $site_name = get_bloginfo( 'name' ); $site_url = home_url(); @@ -717,34 +717,34 @@ class WPSP_Malware_Scanner { $subject = sprintf( /* translators: %s: Site name */ - __( '[%s] Malware Scan Alert - Suspicious Files Found', 'security-pack' ), + __( '[%s] Malware Scan Alert - Suspicious Files Found', 'arkhost-security-pack' ), $site_name ); $message = sprintf( /* translators: %s: Site URL */ - __( "Security Pack malware scan has detected suspicious files on %s.\n\n", 'security-pack' ), + __( "ArkHost Security Pack malware scan has detected suspicious files on %s.\n\n", 'arkhost-security-pack' ), $site_url ); - $message .= __( "Summary:\n", 'security-pack' ); + $message .= __( "Summary:\n", 'arkhost-security-pack' ); /* translators: %d: Number of critical severity issues */ - $message .= sprintf( __( "- Critical: %d\n", 'security-pack' ), $counts['critical'] ); + $message .= sprintf( __( "- Critical: %d\n", 'arkhost-security-pack' ), $counts['critical'] ); /* translators: %d: Number of high severity issues */ - $message .= sprintf( __( "- High: %d\n", 'security-pack' ), $counts['high'] ); + $message .= sprintf( __( "- High: %d\n", 'arkhost-security-pack' ), $counts['high'] ); /* translators: %d: Number of medium severity issues */ - $message .= sprintf( __( "- Medium: %d\n", 'security-pack' ), $counts['medium'] ); + $message .= sprintf( __( "- Medium: %d\n", 'arkhost-security-pack' ), $counts['medium'] ); /* translators: %d: Number of low severity issues */ - $message .= sprintf( __( "- Low: %d\n\n", 'security-pack' ), $counts['low'] ); + $message .= sprintf( __( "- Low: %d\n\n", 'arkhost-security-pack' ), $counts['low'] ); - $message .= __( "Files with issues:\n", 'security-pack' ); + $message .= __( "Files with issues:\n", 'arkhost-security-pack' ); $count = 0; foreach ( $results as $file => $findings ) { if ( $count >= 20 ) { $message .= sprintf( /* translators: %d: Number of additional files */ - __( "... and %d more files\n", 'security-pack' ), + __( "... and %d more files\n", 'arkhost-security-pack' ), count( $results ) - 20 ); break; @@ -760,8 +760,8 @@ class WPSP_Malware_Scanner { $count++; } - $message .= "\n" . __( "Please review these files in your WordPress admin panel under Settings > Security Pack.", 'security-pack' ); - $message .= "\n\n" . __( "Note: Some detections may be false positives. Review each file carefully before taking action.", 'security-pack' ); + $message .= "\n" . __( "Please review these files in your WordPress admin panel under Settings > ArkHost Security Pack.", 'arkhost-security-pack' ); + $message .= "\n\n" . __( "Note: Some detections may be false positives. Review each file carefully before taking action.", 'arkhost-security-pack' ); wp_mail( $email, $subject, $message ); } @@ -809,7 +809,7 @@ class WPSP_Malware_Scanner { */ public function get_quarantine_dir() { $upload_dir = wp_upload_dir(); - $quarantine = $upload_dir['basedir'] . '/wpsp-quarantine'; + $quarantine = $upload_dir['basedir'] . '/arkhost-security-pack/quarantine'; if ( ! file_exists( $quarantine ) ) { wp_mkdir_p( $quarantine ); @@ -839,12 +839,12 @@ class WPSP_Malware_Scanner { */ public function quarantine_file( $file_path ) { if ( ! file_exists( $file_path ) ) { - return new WP_Error( 'file_not_found', __( 'File not found.', 'security-pack' ) ); + return new WP_Error( 'file_not_found', __( 'File not found.', 'arkhost-security-pack' ) ); } // Security: Only allow quarantining files within wp-content. if ( strpos( realpath( $file_path ), realpath( WP_CONTENT_DIR ) ) !== 0 ) { - return new WP_Error( 'invalid_path', __( 'Can only quarantine files within wp-content.', 'security-pack' ) ); + return new WP_Error( 'invalid_path', __( 'Can only quarantine files within wp-content.', 'arkhost-security-pack' ) ); } $quarantine_dir = $this->get_quarantine_dir(); @@ -876,7 +876,7 @@ class WPSP_Malware_Scanner { // Move file to quarantine. // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename if ( ! rename( $file_path, $quarantine_path ) ) { - return new WP_Error( 'move_failed', __( 'Failed to move file to quarantine.', 'security-pack' ) ); + return new WP_Error( 'move_failed', __( 'Failed to move file to quarantine.', 'arkhost-security-pack' ) ); } // Save metadata. @@ -885,9 +885,9 @@ class WPSP_Malware_Scanner { file_put_contents( $meta_file, wp_json_encode( $metadata, JSON_PRETTY_PRINT ) ); // Update quarantine list in options. - $quarantined = get_option( 'wpsp_quarantined_files', array() ); + $quarantined = get_option( 'arksp_quarantined_files', array() ); $quarantined[ $quarantine_name ] = $metadata; - update_option( 'wpsp_quarantined_files', $quarantined ); + update_option( 'arksp_quarantined_files', $quarantined ); // Remove from scan results if present. $results = get_option( self::RESULTS_OPTION, array() ); @@ -915,7 +915,7 @@ class WPSP_Malware_Scanner { $meta_file = $quarantine_path . '.meta'; if ( ! file_exists( $quarantine_path ) ) { - return new WP_Error( 'file_not_found', __( 'Quarantined file not found.', 'security-pack' ) ); + return new WP_Error( 'file_not_found', __( 'Quarantined file not found.', 'arkhost-security-pack' ) ); } // Get metadata. @@ -926,7 +926,7 @@ class WPSP_Malware_Scanner { } if ( empty( $metadata['original_path'] ) ) { - return new WP_Error( 'no_metadata', __( 'Cannot restore: original path unknown.', 'security-pack' ) ); + return new WP_Error( 'no_metadata', __( 'Cannot restore: original path unknown.', 'arkhost-security-pack' ) ); } $original_path = $metadata['original_path']; @@ -940,7 +940,7 @@ class WPSP_Malware_Scanner { // Restore file. // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename if ( ! rename( $quarantine_path, $original_path ) ) { - return new WP_Error( 'restore_failed', __( 'Failed to restore file.', 'security-pack' ) ); + return new WP_Error( 'restore_failed', __( 'Failed to restore file.', 'arkhost-security-pack' ) ); } // Clean up metadata file. @@ -950,10 +950,10 @@ class WPSP_Malware_Scanner { } // Update quarantine list. - $quarantined = get_option( 'wpsp_quarantined_files', array() ); + $quarantined = get_option( 'arksp_quarantined_files', array() ); if ( isset( $quarantined[ $quarantine_name ] ) ) { unset( $quarantined[ $quarantine_name ] ); - update_option( 'wpsp_quarantined_files', $quarantined ); + update_option( 'arksp_quarantined_files', $quarantined ); } return array( @@ -974,13 +974,13 @@ class WPSP_Malware_Scanner { $meta_file = $quarantine_path . '.meta'; if ( ! file_exists( $quarantine_path ) ) { - return new WP_Error( 'file_not_found', __( 'Quarantined file not found.', 'security-pack' ) ); + return new WP_Error( 'file_not_found', __( 'Quarantined file not found.', 'arkhost-security-pack' ) ); } // Delete file. // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink if ( ! unlink( $quarantine_path ) ) { - return new WP_Error( 'delete_failed', __( 'Failed to delete file.', 'security-pack' ) ); + return new WP_Error( 'delete_failed', __( 'Failed to delete file.', 'arkhost-security-pack' ) ); } // Clean up metadata file. @@ -990,10 +990,10 @@ class WPSP_Malware_Scanner { } // Update quarantine list. - $quarantined = get_option( 'wpsp_quarantined_files', array() ); + $quarantined = get_option( 'arksp_quarantined_files', array() ); if ( isset( $quarantined[ $quarantine_name ] ) ) { unset( $quarantined[ $quarantine_name ] ); - update_option( 'wpsp_quarantined_files', $quarantined ); + update_option( 'arksp_quarantined_files', $quarantined ); } return array( 'success' => true ); @@ -1005,7 +1005,7 @@ class WPSP_Malware_Scanner { * @return array */ public function get_quarantined_files() { - return get_option( 'wpsp_quarantined_files', array() ); + return get_option( 'arksp_quarantined_files', array() ); } /** diff --git a/security-pack/includes/class-wpsp-two-factor.php b/arkhost-security-pack/includes/class-arksp-two-factor.php similarity index 64% rename from security-pack/includes/class-wpsp-two-factor.php rename to arkhost-security-pack/includes/class-arksp-two-factor.php index bbccb9f..88ebdbe 100644 --- a/security-pack/includes/class-wpsp-two-factor.php +++ b/arkhost-security-pack/includes/class-arksp-two-factor.php @@ -2,7 +2,7 @@ /** * Two-Factor Authentication for Security Pack. * - * @package Security_Pack + * @package ArkHost_Security_Pack */ // Prevent direct access. @@ -13,28 +13,28 @@ if ( ! defined( 'ABSPATH' ) ) { /** * Two-Factor Authentication class using TOTP. */ -class WPSP_Two_Factor { +class ARKSP_Two_Factor { /** * User meta key for 2FA secret. * * @var string */ - const SECRET_META_KEY = '_wpsp_2fa_secret'; + const SECRET_META_KEY = '_arksp_2fa_secret'; /** * User meta key for 2FA enabled status. * * @var string */ - const ENABLED_META_KEY = '_wpsp_2fa_enabled'; + const ENABLED_META_KEY = '_arksp_2fa_enabled'; /** * User meta key for backup codes. * * @var string */ - const BACKUP_CODES_META_KEY = '_wpsp_2fa_backup_codes'; + const BACKUP_CODES_META_KEY = '_arksp_2fa_backup_codes'; /** * TOTP code length. @@ -54,7 +54,7 @@ class WPSP_Two_Factor { * Constructor. */ public function __construct() { - if ( ! Security_Pack::get_setting( 'two_factor_enabled', false ) ) { + if ( ! ARKSP_Plugin::get_setting( 'two_factor_enabled', false ) ) { return; } @@ -62,8 +62,8 @@ class WPSP_Two_Factor { add_filter( 'authenticate', array( $this, 'check_2fa_on_authenticate' ), 100, 3 ); // Handle the 2FA verification form. - // Use login_form_wpsp_2fa for standard wp-login.php. - add_action( 'login_form_wpsp_2fa', array( $this, 'render_2fa_form' ) ); + // Use login_form_arksp_2fa for standard wp-login.php. + add_action( 'login_form_arksp_2fa', array( $this, 'render_2fa_form' ) ); // Also check on login_init for custom login URLs. add_action( 'login_init', array( $this, 'maybe_render_2fa_form' ), 5 ); @@ -77,13 +77,13 @@ class WPSP_Two_Factor { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_profile_scripts' ) ); // AJAX handlers. - add_action( 'wp_ajax_wpsp_generate_2fa_secret', array( $this, 'ajax_generate_secret' ) ); - add_action( 'wp_ajax_wpsp_verify_2fa_setup', array( $this, 'ajax_verify_setup' ) ); - add_action( 'wp_ajax_wpsp_disable_2fa', array( $this, 'ajax_disable_2fa' ) ); - add_action( 'wp_ajax_wpsp_regenerate_backup_codes', array( $this, 'ajax_regenerate_backup_codes' ) ); + add_action( 'wp_ajax_arksp_generate_2fa_secret', array( $this, 'ajax_generate_secret' ) ); + add_action( 'wp_ajax_arksp_verify_2fa_setup', array( $this, 'ajax_verify_setup' ) ); + add_action( 'wp_ajax_arksp_disable_2fa', array( $this, 'ajax_disable_2fa' ) ); + add_action( 'wp_ajax_arksp_regenerate_backup_codes', array( $this, 'ajax_regenerate_backup_codes' ) ); // Enforce 2FA for admins. - if ( Security_Pack::get_setting( 'two_factor_enforce_admin', false ) ) { + if ( ARKSP_Plugin::get_setting( 'two_factor_enforce_admin', false ) ) { add_action( 'admin_init', array( $this, 'enforce_admin_2fa' ) ); } } @@ -98,13 +98,41 @@ class WPSP_Two_Factor { return; } + // Determine which user is being edited. + if ( 'user-edit.php' === $hook && isset( $_GET['user_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $user_id = (int) $_GET['user_id']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + } else { + $user_id = get_current_user_id(); + } + wp_enqueue_script( - 'wpsp-qrcode', - WPSP_PLUGIN_URL . 'assets/js/qrcode.min.js', + 'arksp-qrcode', + ARKSP_PLUGIN_URL . 'assets/js/qrcode.min.js', array(), - WPSP_VERSION, + ARKSP_VERSION, true ); + + wp_enqueue_script( + 'arksp-2fa-profile', + ARKSP_PLUGIN_URL . 'assets/js/2fa-profile.js', + array( 'jquery', 'arksp-qrcode' ), + ARKSP_VERSION, + true + ); + + wp_localize_script( 'arksp-2fa-profile', 'arksp2fa', array( + 'userId' => $user_id, + 'nonce' => wp_create_nonce( 'arksp_2fa_setup' ), + 'strings' => array( + 'twoFaEnabledTitle' => __( '2FA Enabled! Save Your Backup Codes', 'arkhost-security-pack' ), + 'codesNotShownAgain' => __( 'IMPORTANT: These codes will NOT be shown again!', 'arkhost-security-pack' ), + 'storeCodesInfo' => __( 'Store these codes in a safe place. Each code can only be used once.', 'arkhost-security-pack' ), + 'savedCodes' => __( 'I have saved my codes', 'arkhost-security-pack' ), + 'confirmDisable' => __( 'Are you sure you want to disable 2FA?', 'arkhost-security-pack' ), + 'confirmRegenerate' => __( 'This will invalidate all existing backup codes. Are you sure?', 'arkhost-security-pack' ), + ), + ) ); } /** @@ -133,14 +161,14 @@ class WPSP_Two_Factor { // Nonce not possible during 2FA authentication flow; transient token validates the session. // phpcs:ignore WordPress.Security.NonceVerification.Recommended $redirect = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : admin_url(); - set_transient( 'wpsp_2fa_' . $token, array( + set_transient( 'arksp_2fa_' . $token, array( 'user_id' => $user->ID, 'redirect' => $redirect, ), 5 * MINUTE_IN_SECONDS ); // Redirect to 2FA form immediately. wp_safe_redirect( add_query_arg( array( - 'action' => 'wpsp_2fa', + 'action' => 'arksp_2fa', 'token' => $token, ), wp_login_url() ) ); exit; @@ -174,7 +202,7 @@ class WPSP_Two_Factor { } // Redirect to profile page with notice. - wp_safe_redirect( add_query_arg( 'wpsp_2fa_required', '1', admin_url( 'profile.php' ) ) ); + wp_safe_redirect( add_query_arg( 'arksp_2fa_required', '1', admin_url( 'profile.php' ) ) ); exit; } @@ -183,7 +211,7 @@ class WPSP_Two_Factor { */ public function maybe_render_2fa_form() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - if ( isset( $_GET['action'] ) && 'wpsp_2fa' === $_GET['action'] ) { + if ( isset( $_GET['action'] ) && 'arksp_2fa' === $_GET['action'] ) { $this->render_2fa_form(); } } @@ -200,7 +228,7 @@ class WPSP_Two_Factor { exit; } - $data = get_transient( 'wpsp_2fa_' . $token ); + $data = get_transient( 'arksp_2fa_' . $token ); if ( ! $data ) { wp_safe_redirect( wp_login_url() ); @@ -210,20 +238,20 @@ class WPSP_Two_Factor { $error = ''; // Handle form submission - 2FA verification uses transient token instead of nonce; user already authenticated via password. - if ( isset( $_POST['wpsp_2fa_code'] ) || isset( $_POST['wpsp_backup_code'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( isset( $_POST['arksp_2fa_code'] ) || isset( $_POST['arksp_backup_code'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing // Check TOTP code first, then backup code. $code = ''; - if ( ! empty( $_POST['wpsp_2fa_code'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing - $code = sanitize_text_field( wp_unslash( $_POST['wpsp_2fa_code'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing - } elseif ( ! empty( $_POST['wpsp_backup_code'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing - $code = sanitize_text_field( wp_unslash( $_POST['wpsp_backup_code'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( ! empty( $_POST['arksp_2fa_code'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + $code = sanitize_text_field( wp_unslash( $_POST['arksp_2fa_code'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + } elseif ( ! empty( $_POST['arksp_backup_code'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + $code = sanitize_text_field( wp_unslash( $_POST['arksp_backup_code'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing } $user_id = $data['user_id']; if ( ! empty( $code ) && $this->verify_code( $user_id, $code ) ) { // Delete the token. - delete_transient( 'wpsp_2fa_' . $token ); + delete_transient( 'arksp_2fa_' . $token ); // Log the user in. wp_set_auth_cookie( $user_id, false ); @@ -234,38 +262,38 @@ class WPSP_Two_Factor { exit; } - $error = __( 'Invalid verification code.', 'security-pack' ); + $error = __( 'Invalid verification code.', 'arkhost-security-pack' ); } // Render the form. - login_header( __( 'Two-Factor Authentication', 'security-pack' ) ); + login_header( __( 'Two-Factor Authentication', 'arkhost-security-pack' ) ); ?> -
+
-

+

- - + +

- +

- -