This commit is contained in:
Yuri Karamian
2026-02-04 20:30:32 +01:00
parent 291fd61b67
commit 6b719f4fac
44 changed files with 4974 additions and 4948 deletions
@@ -1,6 +1,6 @@
<?php <?php
/** /**
* Plugin Name: Security Pack * Plugin Name: ArkHost Security Pack
* Description: A free, lightweight security plugin with zero upsells. Login protection, IP blocking, hardening, and activity logging. * Description: A free, lightweight security plugin with zero upsells. Login protection, IP blocking, hardening, and activity logging.
* Version: 1.0 * Version: 1.0
* Requires at least: 5.0 * Requires at least: 5.0
@@ -9,10 +9,10 @@
* Author URI: https://arkhost.com * Author URI: https://arkhost.com
* License: GPL v2 or later * License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html * License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: security-pack * Text Domain: arkhost-security-pack
* Domain Path: /languages * Domain Path: /languages
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -21,21 +21,21 @@ if ( ! defined( 'ABSPATH' ) ) {
} }
// Plugin constants. // Plugin constants.
define( 'WPSP_VERSION', '1.0' ); define( 'ARKSP_VERSION', '1.0' );
define( 'WPSP_PLUGIN_FILE', __FILE__ ); define( 'ARKSP_PLUGIN_FILE', __FILE__ );
define( 'WPSP_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'ARKSP_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'WPSP_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'ARKSP_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
define( 'WPSP_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); define( 'ARKSP_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
/** /**
* Main plugin class. * Main plugin class.
*/ */
final class Security_Pack { final class ARKSP_Plugin {
/** /**
* Single instance. * Single instance.
* *
* @var Security_Pack * @var ARKSP_Plugin
*/ */
private static $instance = null; private static $instance = null;
@@ -49,7 +49,7 @@ final class Security_Pack {
/** /**
* Get single instance. * Get single instance.
* *
* @return Security_Pack * @return ARKSP_Plugin
*/ */
public static function instance() { public static function instance() {
if ( null === self::$instance ) { if ( null === self::$instance ) {
@@ -70,19 +70,19 @@ final class Security_Pack {
* Load required files. * Load required files.
*/ */
private function load_dependencies() { private function load_dependencies() {
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-helper.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-helper.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-db.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-db.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-activity-log.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-activity-log.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-ip-control.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-ip-control.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-login-protection.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-login-protection.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-hardening.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-hardening.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-geo-blocking.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-geo-blocking.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-two-factor.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-two-factor.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-file-integrity.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-file-integrity.php';
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-malware-scanner.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-malware-scanner.php';
if ( is_admin() ) { if ( is_admin() ) {
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-admin.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-admin.php';
} }
} }
@@ -90,17 +90,17 @@ final class Security_Pack {
* Initialize hooks. * Initialize hooks.
*/ */
private function init_hooks() { private function init_hooks() {
register_activation_hook( WPSP_PLUGIN_FILE, array( 'WPSP_DB', 'activate' ) ); register_activation_hook( ARKSP_PLUGIN_FILE, array( 'ARKSP_DB', 'activate' ) );
register_deactivation_hook( WPSP_PLUGIN_FILE, array( 'WPSP_DB', 'deactivate' ) ); register_deactivation_hook( ARKSP_PLUGIN_FILE, array( 'ARKSP_DB', 'deactivate' ) );
add_action( 'plugins_loaded', array( 'WPSP_DB', 'maybe_upgrade' ) ); add_action( 'plugins_loaded', array( 'ARKSP_DB', 'maybe_upgrade' ) );
add_action( 'init', array( $this, 'init_components' ), 1 ); add_action( 'init', array( $this, 'init_components' ), 1 );
// Schedule cleanup cron. // Schedule cleanup cron.
add_action( 'wpsp_daily_cleanup', array( 'WPSP_Activity_Log', 'cleanup_old_logs' ) ); add_action( 'arksp_daily_cleanup', array( 'ARKSP_Activity_Log', 'cleanup_old_logs' ) );
if ( ! wp_next_scheduled( 'wpsp_daily_cleanup' ) ) { if ( ! wp_next_scheduled( 'arksp_daily_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'wpsp_daily_cleanup' ); wp_schedule_event( time(), 'daily', 'arksp_daily_cleanup' );
} }
} }
@@ -112,19 +112,19 @@ final class Security_Pack {
* to register hooks (like two_factor for 2FA on login). * to register hooks (like two_factor for 2FA on login).
*/ */
public function init_components() { public function init_components() {
$this->components['activity_log'] = new WPSP_Activity_Log(); $this->components['activity_log'] = new ARKSP_Activity_Log();
$this->components['ip_control'] = new WPSP_IP_Control(); $this->components['ip_control'] = new ARKSP_IP_Control();
$this->components['hardening'] = new WPSP_Hardening(); $this->components['hardening'] = new ARKSP_Hardening();
$this->components['geo_blocking'] = new WPSP_Geo_Blocking(); $this->components['geo_blocking'] = new ARKSP_Geo_Blocking();
$this->components['two_factor'] = new WPSP_Two_Factor(); $this->components['two_factor'] = new ARKSP_Two_Factor();
$this->components['file_integrity'] = new WPSP_File_Integrity(); $this->components['file_integrity'] = new ARKSP_File_Integrity();
$this->components['malware_scanner'] = new WPSP_Malware_Scanner(); $this->components['malware_scanner'] = new ARKSP_Malware_Scanner();
// Login protection must be last - custom login URL handling may exit early. // 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() ) { 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 * @return mixed
*/ */
public static function get_setting( $key, $default = null ) { 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(); $defaults = self::get_default_settings();
if ( isset( $settings[ $key ] ) ) { if ( isset( $settings[ $key ] ) ) {
@@ -234,9 +234,9 @@ final class Security_Pack {
* @return bool * @return bool
*/ */
public static function update_setting( $key, $value ) { public static function update_setting( $key, $value ) {
$settings = get_option( 'wpsp_settings', array() ); $settings = get_option( 'arksp_settings', array() );
$settings[ $key ] = $value; $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. * Get plugin instance.
* *
* @return Security_Pack * @return ARKSP_Plugin
*/ */
function wpsp() { function arksp() {
return Security_Pack::instance(); return ARKSP_Plugin::instance();
} }
// Initialize plugin. // Initialize plugin.
wpsp(); arksp();
+575
View File
@@ -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;
}
@@ -2,5 +2,5 @@
/** /**
* Silence is golden. * Silence is golden.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
@@ -2,5 +2,5 @@
/** /**
* Silence is golden. * Silence is golden.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
@@ -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 = '<div style="background:#d4edda;border:1px solid #c3e6cb;padding:20px;margin:10px 0;border-radius:4px;">';
codesHtml += '<h3 style="margin-top:0;color:#155724;">' + arksp2fa.strings.twoFaEnabledTitle + '</h3>';
codesHtml += '<p style="color:#dc3232;font-weight:bold;">' + arksp2fa.strings.codesNotShownAgain + '</p>';
codesHtml += '<pre style="background:#fff;padding:15px;font-size:14px;line-height:1.8;">' + response.data.backup_codes.join('\n') + '</pre>';
codesHtml += '<p>' + arksp2fa.strings.storeCodesInfo + '</p>';
codesHtml += '<button type="button" class="button button-primary" onclick="location.reload();">' + arksp2fa.strings.savedCodes + '</button>';
codesHtml += '</div>';
$('#arksp-2fa-setup').html(codesHtml);
} else if (response.success) {
location.reload();
} else {
$('#arksp-2fa-setup-result').html('<p style="color:red;">' + response.data.message + '</p>');
}
});
});
$('#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();
}
});
}
});
});
+400
View File
@@ -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('<span style="color: #666;">' + arkspAdmin.strings.sending + '</span>');
$.post(ajaxurl, {
action: 'arksp_test_email',
email: email,
_ajax_nonce: arkspAdmin.nonce
}, function(response) {
$btn.prop('disabled', false);
if (response.success) {
$result.html('<span style="color: #46b450;">' + arkspAdmin.strings.testEmailSent + '</span>');
} else {
$result.html('<span style="color: #dc3232;">' + response.data.message + '</span>');
}
}).fail(function() {
$btn.prop('disabled', false);
$result.html('<span style="color: #dc3232;">' + arkspAdmin.strings.requestFailed + '</span>');
});
});
// -------------------------------------------------------------------------
// 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();
}
});
}
});
});
@@ -2,7 +2,7 @@
/** /**
* Activity logging for Security Pack. * Activity logging for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Activity log class. * Activity log class.
*/ */
class WPSP_Activity_Log { class ARKSP_Activity_Log {
/** /**
* Event types. * Event types.
@@ -45,7 +45,7 @@ class WPSP_Activity_Log {
global $wpdb; global $wpdb;
if ( null === $ip_address ) { if ( null === $ip_address ) {
$ip_address = WPSP_Helper::get_client_ip(); $ip_address = ARKSP_Helper::get_client_ip();
} }
// Get user agent. // Get user agent.
@@ -55,14 +55,14 @@ class WPSP_Activity_Log {
// Get country code if geo-blocking is available. // Get country code if geo-blocking is available.
$country_code = null; $country_code = null;
$geo_blocking = wpsp()->get_component( 'geo_blocking' ); $geo_blocking = arksp()->get_component( 'geo_blocking' );
if ( $geo_blocking && $ip_address ) { if ( $geo_blocking && $ip_address ) {
$country_code = $geo_blocking->get_country_code( $ip_address ); $country_code = $geo_blocking->get_country_code( $ip_address );
} }
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$result = $wpdb->insert( $result = $wpdb->insert(
$wpdb->prefix . 'wpsp_activity_log', $wpdb->prefix . 'arksp_activity_log',
array( array(
'event_type' => $event_type, 'event_type' => $event_type,
'ip_address' => $ip_address ? $ip_address : '', '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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return $wpdb->get_results( return $wpdb->get_results(
$wpdb->prepare( $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['event_type'],
$args['ip_address'], $args['ip_address'],
$limit, $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return $wpdb->get_results( return $wpdb->get_results(
$wpdb->prepare( $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'], $args['event_type'],
$limit, $limit,
$offset $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return $wpdb->get_results( return $wpdb->get_results(
$wpdb->prepare( $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'], $args['ip_address'],
$limit, $limit,
$offset $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return $wpdb->get_results( return $wpdb->get_results(
$wpdb->prepare( $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, $limit,
$offset $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return (int) $wpdb->get_var( return (int) $wpdb->get_var(
$wpdb->prepare( $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['event_type'],
$args['ip_address'] $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return (int) $wpdb->get_var( return (int) $wpdb->get_var(
$wpdb->prepare( $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'] $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return (int) $wpdb->get_var( return (int) $wpdb->get_var(
$wpdb->prepare( $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'] $args['ip_address']
) )
); );
@@ -182,7 +182,7 @@ class WPSP_Activity_Log {
// No filters. // No filters.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. // 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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security stats must be real-time.
$results = $wpdb->get_results( $results = $wpdb->get_results(
$wpdb->prepare( $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 $cutoff
) )
); );
@@ -227,14 +227,14 @@ class WPSP_Activity_Log {
public static function cleanup_old_logs() { public static function cleanup_old_logs() {
global $wpdb; 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" ) ); $cutoff = gmdate( 'Y-m-d H:i:s', strtotime( "-{$retention_days} days" ) );
// Delete old logs. // Delete old logs.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable.
$wpdb->query( $wpdb->query(
$wpdb->prepare( $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 $cutoff
) )
); );
@@ -243,7 +243,7 @@ class WPSP_Activity_Log {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable.
$wpdb->query( $wpdb->query(
$wpdb->prepare( $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() time()
) )
); );
@@ -258,7 +258,7 @@ class WPSP_Activity_Log {
global $wpdb; global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Truncate for admin action. // 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 ) { public static function get_event_label( $event_type ) {
$labels = array( $labels = array(
self::EVENT_LOGIN_SUCCESS => __( 'Login Success', 'security-pack' ), self::EVENT_LOGIN_SUCCESS => __( 'Login Success', 'arkhost-security-pack' ),
self::EVENT_LOGIN_FAILED => __( 'Login Failed', 'security-pack' ), self::EVENT_LOGIN_FAILED => __( 'Login Failed', 'arkhost-security-pack' ),
self::EVENT_LOCKOUT => __( 'Lockout', 'security-pack' ), self::EVENT_LOCKOUT => __( 'Lockout', 'arkhost-security-pack' ),
self::EVENT_IP_BLOCKED => __( 'IP Blocked', 'security-pack' ), self::EVENT_IP_BLOCKED => __( 'IP Blocked', 'arkhost-security-pack' ),
self::EVENT_GEO_BLOCKED => __( 'Geo Blocked', 'security-pack' ), self::EVENT_GEO_BLOCKED => __( 'Geo Blocked', 'arkhost-security-pack' ),
self::EVENT_LOCKOUT_LIFTED => __( 'Lockout Lifted', 'security-pack' ), self::EVENT_LOCKOUT_LIFTED => __( 'Lockout Lifted', 'arkhost-security-pack' ),
); );
return isset( $labels[ $event_type ] ) ? $labels[ $event_type ] : $event_type; return isset( $labels[ $event_type ] ) ? $labels[ $event_type ] : $event_type;
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@
/** /**
* Database management for Security Pack. * Database management for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Database class for table creation and management. * Database class for table creation and management.
*/ */
class WPSP_DB { class ARKSP_DB {
/** /**
* Database version. * Database version.
@@ -29,7 +29,7 @@ class WPSP_DB {
*/ */
public static function get_log_table() { public static function get_log_table() {
global $wpdb; 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() { public static function get_lockout_table() {
global $wpdb; 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(); self::set_default_options();
// Store DB version. // 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. // Clear rewrite rules for custom login URL.
flush_rewrite_rules(); flush_rewrite_rules();
@@ -60,11 +60,11 @@ class WPSP_DB {
* Check if database needs upgrading. * Check if database needs upgrading.
*/ */
public static function maybe_upgrade() { 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, '<' ) ) { if ( version_compare( $current_version, self::DB_VERSION, '<' ) ) {
self::create_tables(); 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() { public static function deactivate() {
// Clear scheduled events. // Clear scheduled events.
wp_clear_scheduled_hook( 'wpsp_daily_cleanup' ); wp_clear_scheduled_hook( 'arksp_daily_cleanup' );
// Flush rewrite rules. // Flush rewrite rules.
flush_rewrite_rules(); flush_rewrite_rules();
@@ -129,11 +129,11 @@ class WPSP_DB {
* Set default options on activation. * Set default options on activation.
*/ */
private static function set_default_options() { private static function set_default_options() {
$existing = get_option( 'wpsp_settings', false ); $existing = get_option( 'arksp_settings', false );
if ( false === $existing ) { if ( false === $existing ) {
$defaults = Security_Pack::get_default_settings(); $defaults = ARKSP_Plugin::get_default_settings();
update_option( 'wpsp_settings', $defaults ); update_option( 'arksp_settings', $defaults );
} }
} }
@@ -145,24 +145,24 @@ class WPSP_DB {
// Drop tables. // Drop tables.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange -- Uninstall cleanup. // 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. // 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 options.
delete_option( 'wpsp_settings' ); delete_option( 'arksp_settings' );
delete_option( 'wpsp_db_version' ); delete_option( 'arksp_db_version' );
delete_option( 'wpsp_malware_scan_results' ); delete_option( 'arksp_malware_scan_results' );
delete_option( 'wpsp_malware_last_scan' ); delete_option( 'arksp_malware_last_scan' );
delete_option( 'wpsp_malware_hashes' ); delete_option( 'arksp_malware_hashes' );
delete_option( 'wpsp_malware_hashes_updated' ); delete_option( 'arksp_malware_hashes_updated' );
delete_option( 'wpsp_malware_scan_stats' ); delete_option( 'arksp_malware_scan_stats' );
delete_option( 'wpsp_quarantined_files' ); delete_option( 'arksp_quarantined_files' );
delete_option( 'wpsp_file_scan_results' ); delete_option( 'arksp_file_scan_results' );
delete_option( 'wpsp_file_last_scan' ); delete_option( 'arksp_file_last_scan' );
delete_option( 'wpsp_file_baseline' ); delete_option( 'arksp_file_baseline' );
// Delete transients. // Delete transients.
delete_transient( 'wpsp_geo_cache' ); delete_transient( 'arksp_geo_cache' );
} }
} }
@@ -2,7 +2,7 @@
/** /**
* File integrity monitoring for Security Pack. * File integrity monitoring for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,42 +13,42 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* File integrity monitoring class. * File integrity monitoring class.
*/ */
class WPSP_File_Integrity { class ARKSP_File_Integrity {
/** /**
* Option key for file hashes. * Option key for file hashes.
* *
* @var string * @var string
*/ */
const HASHES_OPTION = 'wpsp_file_hashes'; const HASHES_OPTION = 'arksp_file_hashes';
/** /**
* Option key for last scan time. * Option key for last scan time.
* *
* @var string * @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. * Option key for changed files.
* *
* @var string * @var string
*/ */
const CHANGES_OPTION = 'wpsp_file_changes'; const CHANGES_OPTION = 'arksp_file_changes';
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() { public function __construct() {
if ( ! Security_Pack::get_setting( 'file_integrity_enabled', true ) ) { if ( ! ARKSP_Plugin::get_setting( 'file_integrity_enabled', true ) ) {
return; return;
} }
// Schedule daily scan. // 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' ) ) { if ( ! wp_next_scheduled( 'arksp_daily_file_scan' ) ) {
wp_schedule_event( time(), 'daily', 'wpsp_daily_file_scan' ); wp_schedule_event( time(), 'daily', 'arksp_daily_file_scan' );
} }
} }
@@ -189,7 +189,7 @@ class WPSP_File_Integrity {
$locale = get_locale(); $locale = get_locale();
// Try to get from cache. // Try to get from cache.
$cache_key = 'wpsp_checksums_' . md5( $version . $locale ); $cache_key = 'arksp_checksums_' . md5( $version . $locale );
$cached = get_transient( $cache_key ); $cached = get_transient( $cache_key );
if ( false !== $cached ) { if ( false !== $cached ) {
@@ -256,28 +256,28 @@ class WPSP_File_Integrity {
* @param array $changes File changes. * @param array $changes File changes.
*/ */
private function send_alert( $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; 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_name = get_bloginfo( 'name' );
$site_url = home_url(); $site_url = home_url();
$subject = sprintf( $subject = sprintf(
/* translators: %s: Site name */ /* 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 $site_name
); );
$message = sprintf( $message = sprintf(
/* translators: %s: Site URL */ /* 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 $site_url
); );
if ( ! empty( $changes['modified'] ) ) { if ( ! empty( $changes['modified'] ) ) {
$message .= __( "Modified files:\n", 'security-pack' ); $message .= __( "Modified files:\n", 'arkhost-security-pack' );
foreach ( $changes['modified'] as $file ) { foreach ( $changes['modified'] as $file ) {
$message .= '- ' . ( is_array( $file ) ? $file['file'] : $file ) . "\n"; $message .= '- ' . ( is_array( $file ) ? $file['file'] : $file ) . "\n";
} }
@@ -285,7 +285,7 @@ class WPSP_File_Integrity {
} }
if ( ! empty( $changes['added'] ) ) { if ( ! empty( $changes['added'] ) ) {
$message .= __( "New files detected:\n", 'security-pack' ); $message .= __( "New files detected:\n", 'arkhost-security-pack' );
foreach ( $changes['added'] as $file ) { foreach ( $changes['added'] as $file ) {
$message .= '- ' . $file . "\n"; $message .= '- ' . $file . "\n";
} }
@@ -293,18 +293,18 @@ class WPSP_File_Integrity {
} }
if ( ! empty( $changes['removed'] ) ) { if ( ! empty( $changes['removed'] ) ) {
$message .= __( "Removed files:\n", 'security-pack' ); $message .= __( "Removed files:\n", 'arkhost-security-pack' );
foreach ( $changes['removed'] as $file ) { foreach ( $changes['removed'] as $file ) {
$message .= '- ' . $file . "\n"; $message .= '- ' . $file . "\n";
} }
$message .= "\n"; $message .= "\n";
} }
$message .= __( "This could indicate:\n", 'security-pack' ); $message .= __( "This could indicate:\n", 'arkhost-security-pack' );
$message .= __( "- A recent WordPress update (normal)\n", 'security-pack' ); $message .= __( "- A recent WordPress update (normal)\n", 'arkhost-security-pack' );
$message .= __( "- Unauthorized modifications (investigate)\n", 'security-pack' ); $message .= __( "- Unauthorized modifications (investigate)\n", 'arkhost-security-pack' );
$message .= __( "- Plugin/theme conflicts (rare)\n\n", 'security-pack' ); $message .= __( "- Plugin/theme conflicts (rare)\n\n", 'arkhost-security-pack' );
$message .= __( "Review these changes in your WordPress admin panel.", 'security-pack' ); $message .= __( "Review these changes in your WordPress admin panel.", 'arkhost-security-pack' );
wp_mail( $email, $subject, $message ); wp_mail( $email, $subject, $message );
} }
@@ -2,7 +2,7 @@
/** /**
* Geo-blocking for Security Pack. * Geo-blocking for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Geo-blocking class using IP2Location Lite database. * Geo-blocking class using IP2Location Lite database.
*/ */
class WPSP_Geo_Blocking { class ARKSP_Geo_Blocking {
/** /**
* IP2Location database file path. * IP2Location database file path.
@@ -40,16 +40,17 @@ class WPSP_Geo_Blocking {
* Constructor. * Constructor.
*/ */
public function __construct() { 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 ) ) { if ( empty( $this->db_path ) ) {
// Default path in plugin directory. // Default path in uploads directory.
$this->db_path = WPSP_PLUGIN_DIR . 'data/IP2LOCATION-LITE-DB1.BIN'; $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). // Check geo-blocking immediately (constructor runs during init).
// This blocks access to the entire website for blocked countries. // 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(); $this->check_geo_access();
} }
} }
@@ -58,14 +59,14 @@ class WPSP_Geo_Blocking {
* Check if geo-blocking should apply. * Check if geo-blocking should apply.
*/ */
public function check_geo_access() { public function check_geo_access() {
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
if ( ! $ip ) { if ( ! $ip ) {
return; return;
} }
// Check if IP is whitelisted. // 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 ) ) { if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) {
return; return;
} }
@@ -78,16 +79,16 @@ class WPSP_Geo_Blocking {
} }
// Check if country is blocked. // 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 ) ) { if ( ! empty( $blocked_countries ) && in_array( $country_code, $blocked_countries, true ) ) {
WPSP_Activity_Log::log( ARKSP_Activity_Log::log(
WPSP_Activity_Log::EVENT_GEO_BLOCKED, ARKSP_Activity_Log::EVENT_GEO_BLOCKED,
$ip, $ip,
null, null,
sprintf( sprintf(
/* translators: %s: Country code */ /* translators: %s: Country code */
__( 'Blocked country: %s', 'security-pack' ), __( 'Blocked country: %s', 'arkhost-security-pack' ),
$country_code $country_code
) )
); );
@@ -109,7 +110,7 @@ class WPSP_Geo_Blocking {
} }
// Check transient cache. // Check transient cache.
$cache_key = 'wpsp_geo_' . md5( $ip ); $cache_key = 'arksp_geo_' . md5( $ip );
$cached = get_transient( $cache_key ); $cached = get_transient( $cache_key );
if ( false !== $cached ) { if ( false !== $cached ) {
@@ -151,8 +152,8 @@ class WPSP_Geo_Blocking {
// Load the database reader. // Load the database reader.
if ( null === $this->db ) { if ( null === $this->db ) {
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-ip2location.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-ip2location.php';
$this->db = new WPSP_IP2Location( $this->db_path ); $this->db = new ARKSP_IP2Location( $this->db_path );
} }
if ( ! $this->db ) { if ( ! $this->db ) {
@@ -177,12 +178,12 @@ class WPSP_Geo_Blocking {
status_header( 403 ); status_header( 403 );
nocache_headers(); nocache_headers();
$countries = WPSP_Helper::get_countries(); $countries = ARKSP_Helper::get_countries();
$country_name = isset( $countries[ $country_code ] ) ? $countries[ $country_code ] : $country_code; $country_name = isset( $countries[ $country_code ] ) ? $countries[ $country_code ] : $country_code;
$message = sprintf( $message = sprintf(
/* translators: %s: Country name */ /* translators: %s: Country name */
__( 'Access from %s is not permitted.', 'security-pack' ), __( 'Access from %s is not permitted.', 'arkhost-security-pack' ),
$country_name $country_name
); );
@@ -192,7 +193,7 @@ class WPSP_Geo_Blocking {
wp_die( wp_die(
esc_html( $message ), esc_html( $message ),
esc_html__( 'Access Denied', 'security-pack' ), esc_html__( 'Access Denied', 'arkhost-security-pack' ),
array( array(
'response' => 403, 'response' => 403,
'back_link' => false, 'back_link' => false,
@@ -230,8 +231,8 @@ class WPSP_Geo_Blocking {
// Try to get database type. // Try to get database type.
if ( null === $this->db ) { if ( null === $this->db ) {
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-ip2location.php'; require_once ARKSP_PLUGIN_DIR . 'includes/class-arksp-ip2location.php';
$this->db = new WPSP_IP2Location( $this->db_path ); $this->db = new ARKSP_IP2Location( $this->db_path );
} }
if ( $this->db ) { if ( $this->db ) {
@@ -257,8 +258,9 @@ class WPSP_Geo_Blocking {
$download_url = 'https://www.ip2location.com/download/?token=' . urlencode( $download_token ) . '&file=DB1LITEBIN'; $download_url = 'https://www.ip2location.com/download/?token=' . urlencode( $download_token ) . '&file=DB1LITEBIN';
} }
// Create data directory. // Create data directory in uploads.
$data_dir = WPSP_PLUGIN_DIR . 'data'; $upload_dir = wp_upload_dir();
$data_dir = $upload_dir['basedir'] . '/arkhost-security-pack';
if ( ! file_exists( $data_dir ) ) { if ( ! file_exists( $data_dir ) ) {
wp_mkdir_p( $data_dir ); wp_mkdir_p( $data_dir );
} }
@@ -303,10 +305,10 @@ class WPSP_Geo_Blocking {
if ( file_exists( $target_path ) ) { if ( file_exists( $target_path ) ) {
// Update database path setting. // 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 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' ) );
} }
} }
@@ -2,7 +2,7 @@
/** /**
* Security hardening for Security Pack. * Security hardening for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,26 +13,26 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Hardening class for security enhancements. * Hardening class for security enhancements.
*/ */
class WPSP_Hardening { class ARKSP_Hardening {
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() { public function __construct() {
// Disable XML-RPC. // 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( 'xmlrpc_enabled', '__return_false' );
add_filter( 'wp_headers', array( $this, 'remove_xmlrpc_headers' ) ); add_filter( 'wp_headers', array( $this, 'remove_xmlrpc_headers' ) );
add_action( 'wp', array( $this, 'block_xmlrpc_requests' ), 1 ); add_action( 'wp', array( $this, 'block_xmlrpc_requests' ), 1 );
} }
// Disable file editing. // 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(); $this->disable_file_editing();
} }
// Remove WordPress version. // 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' ); add_filter( 'the_generator', '__return_empty_string' );
remove_action( 'wp_head', 'wp_generator' ); remove_action( 'wp_head', 'wp_generator' );
add_filter( 'style_loader_src', array( $this, 'remove_version_strings' ), 10, 2 ); add_filter( 'style_loader_src', array( $this, 'remove_version_strings' ), 10, 2 );
@@ -40,17 +40,17 @@ class WPSP_Hardening {
} }
// Add security headers. // 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' ) ); add_action( 'send_headers', array( $this, 'add_security_headers' ) );
} }
// Restrict REST API. // 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' ) ); add_filter( 'rest_authentication_errors', array( $this, 'restrict_rest_api' ) );
} }
// Disable application passwords for non-admins. // 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' ); add_filter( 'wp_is_application_passwords_available', '__return_false' );
} }
@@ -58,14 +58,14 @@ class WPSP_Hardening {
add_action( 'init', array( $this, 'remove_unnecessary_headers' ) ); add_action( 'init', array( $this, 'remove_unnecessary_headers' ) );
// Disable user enumeration. // 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_action( 'init', array( $this, 'block_author_scanning' ) );
add_filter( 'rest_endpoints', array( $this, 'restrict_users_endpoint' ) ); add_filter( 'rest_endpoints', array( $this, 'restrict_users_endpoint' ) );
add_filter( 'oembed_response_data', array( $this, 'remove_author_from_oembed' ) ); add_filter( 'oembed_response_data', array( $this, 'remove_author_from_oembed' ) );
} }
// Disable pingbacks/trackbacks. // 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( 'xmlrpc_methods', array( $this, 'disable_pingback_methods' ) );
add_filter( 'wp_headers', array( $this, 'remove_pingback_header' ) ); add_filter( 'wp_headers', array( $this, 'remove_pingback_header' ) );
add_filter( 'pings_open', '__return_false', 20, 2 ); add_filter( 'pings_open', '__return_false', 20, 2 );
@@ -131,7 +131,7 @@ class WPSP_Hardening {
} }
// Get header settings. // 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. // X-Content-Type-Options.
if ( ! empty( $headers['x_content_type_options'] ) ) { if ( ! empty( $headers['x_content_type_options'] ) ) {
@@ -204,7 +204,7 @@ class WPSP_Hardening {
} }
// Get allowed REST routes. // 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. // Always allow some essential routes.
$essential_routes = array( $essential_routes = array(
@@ -227,7 +227,7 @@ class WPSP_Hardening {
// Block unauthenticated access. // Block unauthenticated access.
return new WP_Error( return new WP_Error(
'rest_not_logged_in', '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 ) array( 'status' => 401 )
); );
} }
@@ -246,7 +246,7 @@ class WPSP_Hardening {
remove_action( 'wp_head', 'wp_shortlink_wp_head' ); remove_action( 'wp_head', 'wp_shortlink_wp_head' );
// Remove feed links. // 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', 2 );
remove_action( 'wp_head', 'feed_links_extra', 3 ); remove_action( 'wp_head', 'feed_links_extra', 3 );
} }
@@ -0,0 +1,555 @@
<?php
/**
* Helper functions for Security Pack.
*
* @package ArkHost_Security_Pack
*/
// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Helper class with utility functions.
*/
class ARKSP_Helper {
/**
* Get the real client IP address.
*
* Handles proxies and CDNs like Cloudflare.
*
* @return string|null
*/
public static function get_client_ip() {
$ip = null;
// Priority order for IP detection.
$headers = array(
'HTTP_CF_CONNECTING_IP', // Cloudflare.
'HTTP_X_REAL_IP', // Nginx reverse proxy.
'HTTP_X_FORWARDED_FOR', // Generic proxy.
'REMOTE_ADDR', // Direct connection.
);
foreach ( $headers as $header ) {
if ( ! empty( $_SERVER[ $header ] ) ) {
// X-Forwarded-For can contain multiple IPs, get the first one.
$ip = sanitize_text_field( wp_unslash( $_SERVER[ $header ] ) );
if ( 'HTTP_X_FORWARDED_FOR' === $header && strpos( $ip, ',' ) !== false ) {
$ips = explode( ',', $ip );
$ip = trim( $ips[0] );
}
break;
}
}
// Validate IP.
if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return $ip;
}
return null;
}
/**
* Check if an IP is in a CIDR range.
*
* @param string $ip IP address to check.
* @param string $cidr CIDR notation (e.g., 192.168.1.0/24).
* @return bool
*/
public static function ip_in_cidr( $ip, $cidr ) {
// Handle exact IP match (no CIDR notation).
if ( strpos( $cidr, '/' ) === false ) {
return $ip === $cidr;
}
list( $network, $mask ) = explode( '/', $cidr );
// Detect IP version.
$ip_is_v6 = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
$network_is_v6 = filter_var( $network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
// Both must be same version.
if ( $ip_is_v6 !== $network_is_v6 ) {
return false;
}
if ( $ip_is_v6 ) {
return self::ipv6_in_cidr( $ip, $network, (int) $mask );
}
return self::ipv4_in_cidr( $ip, $network, (int) $mask );
}
/**
* Check if IPv4 is in CIDR range.
*
* @param string $ip IPv4 address.
* @param string $network Network address.
* @param int $mask CIDR mask.
* @return bool
*/
private static function ipv4_in_cidr( $ip, $network, $mask ) {
$ip_long = ip2long( $ip );
$network_long = ip2long( $network );
if ( false === $ip_long || false === $network_long ) {
return false;
}
// Calculate network mask.
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
$network_mask = ~( ( 1 << ( 32 - $mask ) ) - 1 );
return ( $ip_long & $network_mask ) === ( $network_long & $network_mask );
}
/**
* Check if IPv6 is in CIDR range.
*
* @param string $ip IPv6 address.
* @param string $network Network address.
* @param int $mask CIDR mask.
* @return bool
*/
private static function ipv6_in_cidr( $ip, $network, $mask ) {
$ip_bin = inet_pton( $ip );
$network_bin = inet_pton( $network );
if ( false === $ip_bin || false === $network_bin ) {
return false;
}
// Compare full bytes.
$full_bytes = (int) floor( $mask / 8 );
for ( $i = 0; $i < $full_bytes; $i++ ) {
if ( $ip_bin[ $i ] !== $network_bin[ $i ] ) {
return false;
}
}
// Compare remaining bits.
$remaining_bits = $mask % 8;
if ( $remaining_bits > 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;
}
}
@@ -2,7 +2,7 @@
/** /**
* IP access control for Security Pack. * IP access control for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* IP control class for whitelist/blacklist management. * IP control class for whitelist/blacklist management.
*/ */
class WPSP_IP_Control { class ARKSP_IP_Control {
/** /**
* Cached whitelist IPs. * Cached whitelist IPs.
@@ -42,7 +42,7 @@ class WPSP_IP_Control {
* Check IP access on every request. * Check IP access on every request.
*/ */
public function check_ip_access() { public function check_ip_access() {
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
if ( ! $ip ) { if ( ! $ip ) {
return; return;
@@ -55,14 +55,14 @@ class WPSP_IP_Control {
// Block blacklisted IPs. // Block blacklisted IPs.
if ( $this->is_blacklisted( $ip ) ) { if ( $this->is_blacklisted( $ip ) ) {
WPSP_Activity_Log::log( WPSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, __( 'IP blacklisted', '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.', 'security-pack' ) ); $this->block_access( __( 'Your IP address has been blocked.', 'arkhost-security-pack' ) );
} }
// Check auto-blocked IPs. // Check auto-blocked IPs.
if ( $this->is_auto_blocked( $ip ) ) { if ( $this->is_auto_blocked( $ip ) ) {
WPSP_Activity_Log::log( WPSP_Activity_Log::EVENT_IP_BLOCKED, $ip, null, __( 'IP auto-blocked', '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.', '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 ) { public function is_whitelisted( $ip ) {
$whitelist = $this->get_whitelist(); $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 ) { public function is_blacklisted( $ip ) {
$blacklist = $this->get_blacklist(); $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 ) { public function is_auto_blocked( $ip ) {
global $wpdb; global $wpdb;
$max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 );
$lockout_minutes = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$lockout = $wpdb->get_row( $lockout = $wpdb->get_row(
$wpdb->prepare( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s",
$ip $ip
) )
); );
@@ -141,8 +141,8 @@ class WPSP_IP_Control {
*/ */
public function get_whitelist() { public function get_whitelist() {
if ( null === $this->whitelist_cache ) { if ( null === $this->whitelist_cache ) {
$whitelist_text = Security_Pack::get_setting( 'ip_whitelist', '' ); $whitelist_text = ARKSP_Plugin::get_setting( 'ip_whitelist', '' );
$this->whitelist_cache = WPSP_Helper::parse_ip_list( $whitelist_text ); $this->whitelist_cache = ARKSP_Helper::parse_ip_list( $whitelist_text );
} }
return $this->whitelist_cache; return $this->whitelist_cache;
} }
@@ -154,8 +154,8 @@ class WPSP_IP_Control {
*/ */
public function get_blacklist() { public function get_blacklist() {
if ( null === $this->blacklist_cache ) { if ( null === $this->blacklist_cache ) {
$blacklist_text = Security_Pack::get_setting( 'ip_blacklist', '' ); $blacklist_text = ARKSP_Plugin::get_setting( 'ip_blacklist', '' );
$this->blacklist_cache = WPSP_Helper::parse_ip_list( $blacklist_text ); $this->blacklist_cache = ARKSP_Helper::parse_ip_list( $blacklist_text );
} }
return $this->blacklist_cache; 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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$existing = $wpdb->get_var( $existing = $wpdb->get_var(
$wpdb->prepare( $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 $ip
) )
); );
@@ -195,7 +195,7 @@ class WPSP_IP_Control {
// Update existing record. // Update existing record.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return false !== $wpdb->update( return false !== $wpdb->update(
$wpdb->prefix . 'wpsp_lockouts', $wpdb->prefix . 'arksp_lockouts',
array( array(
'lockout_until' => $lockout_until, 'lockout_until' => $lockout_until,
'updated_at' => current_time( 'mysql' ), 'updated_at' => current_time( 'mysql' ),
@@ -209,7 +209,7 @@ class WPSP_IP_Control {
// Insert new record. // Insert new record.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return false !== $wpdb->insert( return false !== $wpdb->insert(
$wpdb->prefix . 'wpsp_lockouts', $wpdb->prefix . 'arksp_lockouts',
array( array(
'ip_address' => $ip, 'ip_address' => $ip,
'failed_attempts' => 0, '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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return false !== $wpdb->delete( return false !== $wpdb->delete(
$wpdb->prefix . 'wpsp_lockouts', $wpdb->prefix . 'arksp_lockouts',
array( 'ip_address' => $ip ), array( 'ip_address' => $ip ),
array( '%s' ) array( '%s' )
); );
@@ -246,13 +246,13 @@ class WPSP_IP_Control {
public function get_blocked_ips() { public function get_blocked_ips() {
global $wpdb; global $wpdb;
$max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 );
$lockout_minutes = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); $lockout_minutes = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 );
$current_time = time(); $current_time = time();
// Get all lockout records. // Get all lockout records.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. // 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(); $blocked = array();
foreach ( $lockouts as $lockout ) { foreach ( $lockouts as $lockout ) {
@@ -306,7 +306,7 @@ class WPSP_IP_Control {
// Simple blocked page. // Simple blocked page.
wp_die( wp_die(
esc_html( $message ), esc_html( $message ),
esc_html__( 'Access Denied', 'security-pack' ), esc_html__( 'Access Denied', 'arkhost-security-pack' ),
array( array(
'response' => 403, 'response' => 403,
'back_link' => false, 'back_link' => false,
@@ -4,7 +4,7 @@
* *
* Reads IP2Location BIN format databases. * Reads IP2Location BIN format databases.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -17,7 +17,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* *
* Based on IP2Location PHP Module but simplified for our needs. * Based on IP2Location PHP Module but simplified for our needs.
*/ */
class WPSP_IP2Location { class ARKSP_IP2Location {
/** /**
* Database file handle. * Database file handle.
@@ -2,7 +2,7 @@
/** /**
* Login protection for Security Pack. * Login protection for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Login protection class. * Login protection class.
*/ */
class WPSP_Login_Protection { class ARKSP_Login_Protection {
/** /**
* Custom login URL slug. * Custom login URL slug.
@@ -27,7 +27,7 @@ class WPSP_Login_Protection {
* *
* @var string * @var string
*/ */
private $cookie_name = 'wpsp_login_access'; private $cookie_name = 'arksp_login_access';
/** /**
* Constructor. * Constructor.
@@ -48,7 +48,7 @@ class WPSP_Login_Protection {
// Honeypot field. // Honeypot field.
// NOTE: Must be registered BEFORE handle_custom_login_slug_early() which // NOTE: Must be registered BEFORE handle_custom_login_slug_early() which
// may load wp-login.php and exit, preventing any later hook registrations. // 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( 'login_form', array( $this, 'add_honeypot_field' ) );
add_action( 'register_form', array( $this, 'add_honeypot_field' ) ); add_action( 'register_form', array( $this, 'add_honeypot_field' ) );
add_filter( 'authenticate', array( $this, 'check_honeypot' ), 1, 3 ); add_filter( 'authenticate', array( $this, 'check_honeypot' ), 1, 3 );
@@ -58,7 +58,7 @@ class WPSP_Login_Protection {
// Hide login error messages (prevents username enumeration). // Hide login error messages (prevents username enumeration).
// NOTE: Must be registered BEFORE handle_custom_login_slug_early() which // NOTE: Must be registered BEFORE handle_custom_login_slug_early() which
// may load wp-login.php and exit, preventing any later hook registrations. // 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). // 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 ); add_filter( 'authenticate', array( $this, 'genericize_auth_error' ), PHP_INT_MAX, 3 );
// Also filter the rendered error string as a safety net. // 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. // 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. // WARNING: handle_custom_login_slug_early() may require wp-login.php and exit.
// All login-related hooks MUST be registered above this point. // All login-related hooks MUST be registered above this point.
if ( Security_Pack::get_setting( 'login_rename_enabled', false ) ) { if ( ARKSP_Plugin::get_setting( 'login_rename_enabled', false ) ) {
$this->custom_login_slug = Security_Pack::get_setting( 'login_custom_url', '' ); $this->custom_login_slug = ARKSP_Plugin::get_setting( 'login_custom_url', '' );
if ( ! empty( $this->custom_login_slug ) ) { if ( ! empty( $this->custom_login_slug ) ) {
// Handle custom login slug immediately (constructor runs during plugins_loaded). // Handle custom login slug immediately (constructor runs during plugins_loaded).
$this->handle_custom_login_slug_early(); $this->handle_custom_login_slug_early();
@@ -83,7 +83,7 @@ class WPSP_Login_Protection {
} }
// Hide wp-admin for non-logged-in users. // 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. // Must run early - WordPress redirects wp-admin to wp-login.php before init.
$this->block_wp_admin_early(); $this->block_wp_admin_early();
add_action( 'init', array( $this, 'hide_wp_admin' ), 1 ); add_action( 'init', array( $this, 'hide_wp_admin' ), 1 );
@@ -118,11 +118,11 @@ class WPSP_Login_Protection {
*/ */
private function should_block_direct_wp_login() { private function should_block_direct_wp_login() {
// Only applies when custom login URL is enabled. // 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; return false;
} }
$custom_slug = Security_Pack::get_setting( 'login_custom_url', '' ); $custom_slug = ARKSP_Plugin::get_setting( 'login_custom_url', '' );
if ( empty( $custom_slug ) ) { if ( empty( $custom_slug ) ) {
return false; return false;
} }
@@ -141,8 +141,8 @@ class WPSP_Login_Protection {
// Allow if user has the access cookie (visited custom login URL before). // Allow if user has the access cookie (visited custom login URL before).
// This allows all WordPress flows (password reset, etc.) to work normally. // This allows all WordPress flows (password reset, etc.) to work normally.
$expected_value = wp_hash( 'wpsp_login_' . $custom_slug ); $expected_value = wp_hash( 'arksp_login_' . $custom_slug );
$cookie_value = isset( $_COOKIE['wpsp_login_access'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['wpsp_login_access'] ) ) : ''; $cookie_value = isset( $_COOKIE['arksp_login_access'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['arksp_login_access'] ) ) : '';
if ( $cookie_value === $expected_value ) { if ( $cookie_value === $expected_value ) {
return false; return false;
@@ -158,27 +158,27 @@ class WPSP_Login_Protection {
* @return bool True if access should be denied. * @return bool True if access should be denied.
*/ */
private function is_admin_access_restricted() { 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; return false;
} }
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
if ( ! $ip ) { if ( ! $ip ) {
return false; return false;
} }
// Check if IP is whitelisted (always allow). // Check if IP is whitelisted (always allow).
$ip_control = wpsp()->get_component( 'ip_control' ); $ip_control = arksp()->get_component( 'ip_control' );
if ( ! $ip_control ) { if ( ! $ip_control ) {
$ip_control = new WPSP_IP_Control(); $ip_control = new ARKSP_IP_Control();
} }
if ( $ip_control->is_whitelisted( $ip ) ) { if ( $ip_control->is_whitelisted( $ip ) ) {
return false; return false;
} }
$allowed_countries = Security_Pack::get_setting( 'admin_allowed_countries', array() ); $allowed_countries = ARKSP_Plugin::get_setting( 'admin_allowed_countries', array() );
$allowed_ips = Security_Pack::get_setting( 'admin_allowed_ips', '' ); $allowed_ips = ARKSP_Plugin::get_setting( 'admin_allowed_ips', '' );
$allowed_ip_list = WPSP_Helper::parse_ip_list( $allowed_ips ); $allowed_ip_list = ARKSP_Helper::parse_ip_list( $allowed_ips );
$has_country_restriction = ! empty( $allowed_countries ); $has_country_restriction = ! empty( $allowed_countries );
$has_ip_restriction = ! empty( $allowed_ip_list ); $has_ip_restriction = ! empty( $allowed_ip_list );
@@ -191,15 +191,15 @@ class WPSP_Login_Protection {
$access_granted = false; $access_granted = false;
// Check IP restriction. // 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; $access_granted = true;
} }
// Check country restriction. // Check country restriction.
if ( ! $access_granted && $has_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 ) { if ( ! $geo_blocking ) {
$geo_blocking = new WPSP_Geo_Blocking(); $geo_blocking = new ARKSP_Geo_Blocking();
} }
$country_code = $geo_blocking->get_country_code( $ip ); $country_code = $geo_blocking->get_country_code( $ip );
@@ -216,22 +216,22 @@ class WPSP_Login_Protection {
* Show admin access denied message and exit. * Show admin access denied message and exit.
*/ */
private function show_admin_access_denied_message() { private function show_admin_access_denied_message() {
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
// Log the blocked access attempt. // Log the blocked access attempt.
WPSP_Activity_Log::log( ARKSP_Activity_Log::log(
WPSP_Activity_Log::EVENT_IP_BLOCKED, ARKSP_Activity_Log::EVENT_IP_BLOCKED,
$ip, $ip,
null, null,
__( 'Admin access denied: country/IP not allowed', 'security-pack' ) __( 'Admin access denied: country/IP not allowed', 'arkhost-security-pack' )
); );
status_header( 403 ); status_header( 403 );
nocache_headers(); nocache_headers();
wp_die( wp_die(
esc_html__( 'Admin access is not permitted from your location.', 'security-pack' ), esc_html__( 'Admin access is not permitted from your location.', 'arkhost-security-pack' ),
esc_html__( 'Access Denied', 'security-pack' ), esc_html__( 'Access Denied', 'arkhost-security-pack' ),
array( array(
'response' => 403, 'response' => 403,
'back_link' => false, 'back_link' => false,
@@ -245,31 +245,31 @@ class WPSP_Login_Protection {
* @param string $username Username attempted. * @param string $username Username attempted.
*/ */
public function handle_failed_login( $username ) { 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; return;
} }
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
if ( ! $ip ) { if ( ! $ip ) {
return; return;
} }
// Check if IP is whitelisted. // 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 ) ) { 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; return;
} }
// Log the failed attempt. // 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. // Increment failed attempts counter.
$attempts = $this->increment_failed_attempts( $ip ); $attempts = $this->increment_failed_attempts( $ip );
// Check if lockout threshold reached. // 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 ) { if ( $attempts >= $max_attempts ) {
$this->lockout_ip( $ip ); $this->lockout_ip( $ip );
@@ -286,16 +286,16 @@ class WPSP_Login_Protection {
* @param WP_User $user User object. * @param WP_User $user User object.
*/ */
public function handle_successful_login( $user_login, $user ) { public function handle_successful_login( $user_login, $user ) {
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
// Log the successful login. // 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. // Clear failed attempts for this IP.
$this->clear_failed_attempts( $ip ); $this->clear_failed_attempts( $ip );
// Send admin login notification if enabled. // 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 ); $this->send_admin_login_notification( $user, $ip );
} }
} }
@@ -307,13 +307,13 @@ class WPSP_Login_Protection {
* @param string $ip IP address. * @param string $ip IP address.
*/ */
private function send_admin_login_notification( $user, $ip ) { 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 ) ) { if ( empty( $email ) ) {
return; return;
} }
// Check if this is a new IP for this user. // 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 ) ) { if ( ! is_array( $known_ips ) ) {
$known_ips = array(); $known_ips = array();
} }
@@ -325,7 +325,7 @@ class WPSP_Login_Protection {
if ( $is_new_ip ) { if ( $is_new_ip ) {
$known_ips[] = $ip; $known_ips[] = $ip;
$known_ips = array_slice( $known_ips, -10 ); $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. // Only send notification for new IPs.
@@ -336,24 +336,24 @@ class WPSP_Login_Protection {
$site_name = get_bloginfo( 'name' ); $site_name = get_bloginfo( 'name' );
$subject = sprintf( $subject = sprintf(
/* translators: %s: Site name */ /* translators: %s: Site name */
__( '[%s] Admin Login from New IP', 'security-pack' ), __( '[%s] Admin Login from New IP', 'arkhost-security-pack' ),
$site_name $site_name
); );
$message = sprintf( $message = sprintf(
/* translators: 1: Username, 2: Site name */ /* 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, $user->user_login,
$site_name $site_name
) . "\n\n"; ) . "\n\n";
/* translators: %s: Username */ /* 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 */ /* 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 */ /* translators: %s: Login time */
$message .= sprintf( __( 'Time: %s', 'security-pack' ), current_time( 'mysql' ) ) . "\n\n"; $message .= sprintf( __( 'Time: %s', 'arkhost-security-pack' ), current_time( 'mysql' ) ) . "\n\n";
$message .= __( 'If this was not you, please secure your account immediately.', 'security-pack' ) . "\n"; $message .= __( 'If this was not you, please secure your account immediately.', 'arkhost-security-pack' ) . "\n";
wp_mail( $email, $subject, $message ); wp_mail( $email, $subject, $message );
} }
@@ -366,7 +366,7 @@ class WPSP_Login_Protection {
*/ */
public function hide_login_errors( $error ) { public function hide_login_errors( $error ) {
// Return generic message instead of revealing if username or password was wrong. // 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 ) ) { if ( in_array( $user->get_error_code(), $specific_codes, true ) ) {
return new WP_Error( return new WP_Error(
'authentication_failed', '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 * @return WP_User|WP_Error|null
*/ */
public function check_lockout( $user, $username, $password ) { 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; return $user;
} }
@@ -413,22 +413,22 @@ class WPSP_Login_Protection {
return $user; return $user;
} }
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
if ( ! $ip ) { if ( ! $ip ) {
return $user; return $user;
} }
// Check if IP is whitelisted. // 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 ) ) { if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) {
return $user; return $user;
} }
// Check if IP is locked out. // Check if IP is locked out.
$lockout = $this->get_lockout( $ip ); $lockout = $this->get_lockout( $ip );
$max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 );
$lockout_duration = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); $lockout_duration = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 );
if ( $lockout && (int) $lockout->failed_attempts >= $max_attempts ) { if ( $lockout && (int) $lockout->failed_attempts >= $max_attempts ) {
// Calculate lockout expiry from updated_at + duration. // Calculate lockout expiry from updated_at + duration.
@@ -442,10 +442,10 @@ class WPSP_Login_Protection {
$remaining = human_time_diff( $current_time, $lockout_expires ); $remaining = human_time_diff( $current_time, $lockout_expires );
return new WP_Error( return new WP_Error(
'wpsp_locked_out', 'arksp_locked_out',
sprintf( sprintf(
/* translators: %s: Time remaining */ /* 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 $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$existing = $wpdb->get_row( $existing = $wpdb->get_row(
$wpdb->prepare( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s",
$ip $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$wpdb->update( $wpdb->update(
$wpdb->prefix . 'wpsp_lockouts', $wpdb->prefix . 'arksp_lockouts',
array( array(
'failed_attempts' => $new_count, 'failed_attempts' => $new_count,
'updated_at' => current_time( 'mysql' ), 'updated_at' => current_time( 'mysql' ),
@@ -496,7 +496,7 @@ class WPSP_Login_Protection {
// Create new record. // Create new record.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$wpdb->insert( $wpdb->insert(
$wpdb->prefix . 'wpsp_lockouts', $wpdb->prefix . 'arksp_lockouts',
array( array(
'ip_address' => $ip, 'ip_address' => $ip,
'failed_attempts' => 1, '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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$wpdb->delete( $wpdb->delete(
$wpdb->prefix . 'wpsp_lockouts', $wpdb->prefix . 'arksp_lockouts',
array( 'ip_address' => $ip ), array( 'ip_address' => $ip ),
array( '%s' ) array( '%s' )
); );
@@ -532,13 +532,13 @@ class WPSP_Login_Protection {
private function lockout_ip( $ip ) { private function lockout_ip( $ip ) {
global $wpdb; 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. // Store Unix timestamp to avoid timezone issues.
$lockout_until = time() + ( $duration * 60 ); $lockout_until = time() + ( $duration * 60 );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$wpdb->update( $wpdb->update(
$wpdb->prefix . 'wpsp_lockouts', $wpdb->prefix . 'arksp_lockouts',
array( array(
'lockout_until' => $lockout_until, 'lockout_until' => $lockout_until,
'updated_at' => current_time( 'mysql' ), 'updated_at' => current_time( 'mysql' ),
@@ -549,13 +549,13 @@ class WPSP_Login_Protection {
); );
// Log the lockout. // Log the lockout.
WPSP_Activity_Log::log( ARKSP_Activity_Log::log(
WPSP_Activity_Log::EVENT_LOCKOUT, ARKSP_Activity_Log::EVENT_LOCKOUT,
$ip, $ip,
null, null,
sprintf( sprintf(
/* translators: %d: Duration in minutes */ /* translators: %d: Duration in minutes */
__( 'Locked out for %d minutes', 'security-pack' ), __( 'Locked out for %d minutes', 'arkhost-security-pack' ),
$duration $duration
) )
); );
@@ -573,11 +573,11 @@ class WPSP_Login_Protection {
* @param string $ip IP address. * @param string $ip IP address.
*/ */
private function maybe_auto_blacklist( $ip ) { 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; return;
} }
$threshold = (int) Security_Pack::get_setting( 'auto_blacklist_threshold', 3 ); $threshold = (int) ARKSP_Plugin::get_setting( 'auto_blacklist_threshold', 3 );
if ( $threshold < 1 ) { if ( $threshold < 1 ) {
return; return;
} }
@@ -589,13 +589,13 @@ class WPSP_Login_Protection {
$this->add_ip_to_blacklist( $ip ); $this->add_ip_to_blacklist( $ip );
// Log the auto-blacklist. // Log the auto-blacklist.
WPSP_Activity_Log::log( ARKSP_Activity_Log::log(
WPSP_Activity_Log::EVENT_IP_BLOCKED, ARKSP_Activity_Log::EVENT_IP_BLOCKED,
$ip, $ip,
null, null,
sprintf( sprintf(
/* translators: %d: Number of lockouts */ /* translators: %d: Number of lockouts */
__( 'Auto-blacklisted after %d lockouts', 'security-pack' ), __( 'Auto-blacklisted after %d lockouts', 'arkhost-security-pack' ),
$lockout_count $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$count = $wpdb->get_var( $count = $wpdb->get_var(
$wpdb->prepare( $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, $ip,
WPSP_Activity_Log::EVENT_LOCKOUT ARKSP_Activity_Log::EVENT_LOCKOUT
) )
); );
@@ -632,7 +632,7 @@ class WPSP_Login_Protection {
* @param string $ip IP address. * @param string $ip IP address.
*/ */
private function add_ip_to_blacklist( $ip ) { 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. // Check if IP is already in the blacklist.
$blacklist_array = array_filter( array_map( 'trim', explode( "\n", $current_blacklist ) ) ); $blacklist_array = array_filter( array_map( 'trim', explode( "\n", $current_blacklist ) ) );
@@ -644,10 +644,10 @@ class WPSP_Login_Protection {
$blacklist_array[] = $ip; $blacklist_array[] = $ip;
$new_blacklist = implode( "\n", $blacklist_array ); $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. // Clear the IP control cache.
$ip_control = wpsp()->get_component( 'ip_control' ); $ip_control = arksp()->get_component( 'ip_control' );
if ( $ip_control ) { if ( $ip_control ) {
$ip_control->clear_cache(); $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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
return $wpdb->get_row( return $wpdb->get_row(
$wpdb->prepare( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s",
$ip $ip
) )
); );
@@ -698,7 +698,7 @@ class WPSP_Login_Protection {
if ( $is_wp_admin && ! $is_ajax ) { if ( $is_wp_admin && ! $is_ajax ) {
// Redirect to a non-existent URL to trigger WordPress's themed 404 page. // Redirect to a non-existent URL to trigger WordPress's themed 404 page.
$home = get_option( 'home', '' ); $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 ); header( 'Location: ' . $fake_url, true, 302 );
exit; exit;
} }
@@ -725,22 +725,14 @@ class WPSP_Login_Protection {
// Set a flag that user accessed via custom login slug. // Set a flag that user accessed via custom login slug.
// This will be checked by restrict_wp_login on subsequent requests. // This will be checked by restrict_wp_login on subsequent requests.
$cookie_value = wp_hash( 'wpsp_login_' . $this->custom_login_slug ); $cookie_value = wp_hash( 'arksp_login_' . $this->custom_login_slug );
// Define cookie constants if not already defined.
if ( ! defined( 'COOKIEPATH' ) ) {
define( 'COOKIEPATH', '/' );
}
if ( ! defined( 'COOKIE_DOMAIN' ) ) {
define( 'COOKIE_DOMAIN', '' );
}
setcookie( setcookie(
$this->cookie_name, $this->cookie_name,
$cookie_value, $cookie_value,
time() + DAY_IN_SECONDS, time() + DAY_IN_SECONDS,
COOKIEPATH, defined( 'COOKIEPATH' ) ? COOKIEPATH : '/',
COOKIE_DOMAIN, defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '',
is_ssl(), is_ssl(),
true true
); );
@@ -759,17 +751,17 @@ class WPSP_Login_Protection {
* @return bool * @return bool
*/ */
private function is_ip_locked_out_early() { 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; return false;
} }
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
if ( ! $ip ) { if ( ! $ip ) {
return false; return false;
} }
// Check whitelist first. // 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 ) ) { if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) {
return false; 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. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
$lockout = $wpdb->get_row( $lockout = $wpdb->get_row(
$wpdb->prepare( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wpsp_lockouts WHERE ip_address = %s", "SELECT * FROM {$wpdb->prefix}arksp_lockouts WHERE ip_address = %s",
$ip $ip
) )
); );
@@ -789,8 +781,8 @@ class WPSP_Login_Protection {
return false; return false;
} }
$max_attempts = (int) Security_Pack::get_setting( 'login_max_attempts', 5 ); $max_attempts = (int) ARKSP_Plugin::get_setting( 'login_max_attempts', 5 );
$lockout_minutes = (int) Security_Pack::get_setting( 'login_lockout_duration', 15 ); $lockout_minutes = (int) ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 );
$current_time = time(); $current_time = time();
// Check if IP has reached max attempts and is still within lockout window. // Check if IP has reached max attempts and is still within lockout window.
@@ -823,10 +815,10 @@ class WPSP_Login_Protection {
wp_die( wp_die(
sprintf( sprintf(
/* translators: %d: Lockout duration in minutes */ /* translators: %d: Lockout duration in minutes */
esc_html__( 'Too many failed login attempts. Please try again in %d minutes.', 'security-pack' ), esc_html__( 'Too many failed login attempts. Please try again in %d minutes.', 'arkhost-security-pack' ),
absint( Security_Pack::get_setting( 'login_lockout_duration', 15 ) ) absint( ARKSP_Plugin::get_setting( 'login_lockout_duration', 15 ) )
), ),
esc_html__( 'Access Denied', 'security-pack' ), esc_html__( 'Access Denied', 'arkhost-security-pack' ),
array( array(
'response' => 403, 'response' => 403,
'back_link' => false, 'back_link' => false,
@@ -873,7 +865,7 @@ class WPSP_Login_Protection {
} }
// Allow if user has the access cookie (came through custom login slug before). // 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 ] ) ) : ''; $cookie_value = isset( $_COOKIE[ $this->cookie_name ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ $this->cookie_name ] ) ) : '';
if ( $cookie_value === $expected_value ) { if ( $cookie_value === $expected_value ) {
@@ -912,7 +904,7 @@ class WPSP_Login_Protection {
$home_url = home_url( '/' ); $home_url = home_url( '/' );
// Use a non-existent URL to trigger WordPress 404. // 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 ); status_header( 404 );
nocache_headers(); 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. // 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. // This prevents /wp-admin from revealing the custom login URL.
// Use get_option directly to avoid any potential issues with class method. // 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'] ); $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; $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; include $template;
} else { } else {
wp_die( wp_die(
esc_html__( 'Page not found.', 'security-pack' ), esc_html__( 'Page not found.', 'arkhost-security-pack' ),
esc_html__( '404 Not Found', 'security-pack' ), esc_html__( '404 Not Found', 'arkhost-security-pack' ),
array( 'response' => 404 ) array( 'response' => 404 )
); );
} }
@@ -1041,9 +1033,9 @@ class WPSP_Login_Protection {
public function add_honeypot_field() { public function add_honeypot_field() {
// Honeypot field - hidden via CSS, bots will fill it. // Honeypot field - hidden via CSS, bots will fill it.
?> ?>
<p class="wpsp-hp-field" style="position:absolute;left:-9999px;top:-9999px;"> <p class="arksp-hp-field" style="position:absolute;left:-9999px;top:-9999px;">
<label for="wpsp_hp_email"><?php esc_html_e( 'Leave this field empty', 'security-pack' ); ?></label> <label for="arksp_hp_email"><?php esc_html_e( 'Leave this field empty', 'arkhost-security-pack' ); ?></label>
<input type="text" name="wpsp_hp_email" id="wpsp_hp_email" value="" tabindex="-1" autocomplete="off" /> <input type="text" name="arksp_hp_email" id="arksp_hp_email" value="" tabindex="-1" autocomplete="off" />
</p> </p>
<?php <?php
} }
@@ -1058,26 +1050,26 @@ class WPSP_Login_Protection {
*/ */
public function check_honeypot( $user, $username, $password ) { public function check_honeypot( $user, $username, $password ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing // phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! empty( $_POST['wpsp_hp_email'] ) ) { if ( ! empty( $_POST['arksp_hp_email'] ) ) {
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
WPSP_Activity_Log::log( ARKSP_Activity_Log::log(
WPSP_Activity_Log::EVENT_LOGIN_FAILED, ARKSP_Activity_Log::EVENT_LOGIN_FAILED,
$ip, $ip,
$username, $username,
__( 'Honeypot triggered', 'security-pack' ) __( 'Honeypot triggered', 'arkhost-security-pack' )
); );
// Auto-block this IP using configurable duration. // Auto-block this IP using configurable duration.
$ip_control = wpsp()->get_component( 'ip_control' ); $ip_control = arksp()->get_component( 'ip_control' );
$ban_duration = (int) Security_Pack::get_setting( 'honeypot_ban_duration', 60 ); $ban_duration = (int) ARKSP_Plugin::get_setting( 'honeypot_ban_duration', 60 );
if ( $ip_control ) { 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( return new WP_Error(
'wpsp_honeypot', 'arksp_honeypot',
__( 'Authentication failed.', 'security-pack' ) __( 'Authentication failed.', 'arkhost-security-pack' )
); );
} }
@@ -1094,18 +1086,18 @@ class WPSP_Login_Protection {
*/ */
public function check_honeypot_registration( $errors, $sanitized_user_login, $user_email ) { public function check_honeypot_registration( $errors, $sanitized_user_login, $user_email ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing // phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! empty( $_POST['wpsp_hp_email'] ) ) { if ( ! empty( $_POST['arksp_hp_email'] ) ) {
$errors->add( $errors->add(
'wpsp_honeypot', 'arksp_honeypot',
__( 'Registration failed.', 'security-pack' ) __( 'Registration failed.', 'arkhost-security-pack' )
); );
// Auto-block this IP using configurable duration. // Auto-block this IP using configurable duration.
$ip = WPSP_Helper::get_client_ip(); $ip = ARKSP_Helper::get_client_ip();
$ip_control = wpsp()->get_component( 'ip_control' ); $ip_control = arksp()->get_component( 'ip_control' );
$ban_duration = (int) Security_Pack::get_setting( 'honeypot_ban_duration', 60 ); $ban_duration = (int) ARKSP_Plugin::get_setting( 'honeypot_ban_duration', 60 );
if ( $ip_control && $ip ) { 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). * @param int $attempts Number of attempts (optional).
*/ */
private function maybe_send_alert( $type, $ip, $username = '', $attempts = 0 ) { 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; 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 ) ) { if ( empty( $email ) ) {
return; return;
} }
@@ -1135,23 +1127,23 @@ class WPSP_Login_Protection {
switch ( $type ) { switch ( $type ) {
case 'failed_login': case 'failed_login':
$threshold = Security_Pack::get_setting( 'email_alert_threshold', 3 ); $threshold = ARKSP_Plugin::get_setting( 'email_alert_threshold', 3 );
if ( $attempts < $threshold ) { if ( $attempts < $threshold ) {
return; return;
} }
$subject = sprintf( $subject = sprintf(
/* translators: %s: Site name */ /* translators: %s: Site name */
__( '[%s] Multiple Failed Login Attempts', 'security-pack' ), __( '[%s] Multiple Failed Login Attempts', 'arkhost-security-pack' ),
$site_name $site_name
); );
$message = sprintf( $message = sprintf(
/* translators: 1: Number of attempts, 2: IP address, 3: Username, 4: Site URL */ /* 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, $attempts,
$ip, $ip,
$username ? $username : __( '(empty)', 'security-pack' ), $username ? $username : __( '(empty)', 'arkhost-security-pack' ),
$site_url $site_url
); );
break; break;
@@ -1159,13 +1151,13 @@ class WPSP_Login_Protection {
case 'lockout': case 'lockout':
$subject = sprintf( $subject = sprintf(
/* translators: %s: Site name */ /* translators: %s: Site name */
__( '[%s] IP Address Locked Out', 'security-pack' ), __( '[%s] IP Address Locked Out', 'arkhost-security-pack' ),
$site_name $site_name
); );
$message = sprintf( $message = sprintf(
/* translators: 1: IP address, 2: Site URL */ /* 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, $ip,
$site_url $site_url
); );
@@ -1174,13 +1166,13 @@ class WPSP_Login_Protection {
case 'auto_blacklist': case 'auto_blacklist':
$subject = sprintf( $subject = sprintf(
/* translators: %s: Site name */ /* translators: %s: Site name */
__( '[%s] IP Permanently Blacklisted', 'security-pack' ), __( '[%s] IP Permanently Blacklisted', 'arkhost-security-pack' ),
$site_name $site_name
); );
$message = sprintf( $message = sprintf(
/* translators: 1: IP address, 2: Number of lockouts, 3: Site URL */ /* 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, $ip,
$attempts, $attempts,
$site_url $site_url
@@ -2,7 +2,7 @@
/** /**
* Malware scanner for Security Pack. * Malware scanner for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -19,35 +19,35 @@ if ( ! defined( 'ABSPATH' ) ) {
* *
* Only scans plugins, themes, and uploads - NOT WordPress core. * Only scans plugins, themes, and uploads - NOT WordPress core.
*/ */
class WPSP_Malware_Scanner { class ARKSP_Malware_Scanner {
/** /**
* Option key for scan results. * Option key for scan results.
* *
* @var string * @var string
*/ */
const RESULTS_OPTION = 'wpsp_malware_scan_results'; const RESULTS_OPTION = 'arksp_malware_scan_results';
/** /**
* Option key for last scan time. * Option key for last scan time.
* *
* @var string * @var string
*/ */
const LAST_SCAN_OPTION = 'wpsp_malware_last_scan'; const LAST_SCAN_OPTION = 'arksp_malware_last_scan';
/** /**
* Option key for malware hash database. * Option key for malware hash database.
* *
* @var string * @var string
*/ */
const HASH_DB_OPTION = 'wpsp_malware_hashes'; const HASH_DB_OPTION = 'arksp_malware_hashes';
/** /**
* Option key for hash database last update. * Option key for hash database last update.
* *
* @var string * @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). * Malware signatures (patterns to detect).
@@ -70,7 +70,7 @@ class WPSP_Malware_Scanner {
*/ */
private $skip_paths = array( private $skip_paths = array(
// This plugin's own files. // This plugin's own files.
'security-pack', 'arkhost-security-pack',
// Common libraries that legitimately use shell/network functions. // Common libraries that legitimately use shell/network functions.
'phpseclib', 'phpseclib',
@@ -93,15 +93,15 @@ class WPSP_Malware_Scanner {
$this->load_signatures(); $this->load_signatures();
$this->load_malware_hashes(); $this->load_malware_hashes();
if ( ! Security_Pack::get_setting( 'malware_scan_enabled', true ) ) { if ( ! ARKSP_Plugin::get_setting( 'malware_scan_enabled', true ) ) {
return; return;
} }
// Schedule weekly scan. // 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' ) ) { if ( ! wp_next_scheduled( 'arksp_weekly_malware_scan' ) ) {
wp_schedule_event( time(), 'weekly', 'wpsp_weekly_malware_scan' ); wp_schedule_event( time(), 'weekly', 'arksp_weekly_malware_scan' );
} }
} }
@@ -110,14 +110,14 @@ class WPSP_Malware_Scanner {
* *
* Hashes can come from: * Hashes can come from:
* 1. Local database (user-added) * 1. Local database (user-added)
* 2. Custom filter (wpsp_malware_hashes) * 2. Custom filter (arksp_malware_hashes)
*/ */
private function load_malware_hashes() { private function load_malware_hashes() {
// Load from database (user-added or previously downloaded). // Load from database (user-added or previously downloaded).
$this->malware_hashes = get_option( self::HASH_DB_OPTION, array() ); $this->malware_hashes = get_option( self::HASH_DB_OPTION, array() );
// Allow adding custom hashes via filter. // 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 ) ) { if ( empty( $source_url ) ) {
// Default: Could be a GitHub raw URL or your own endpoint. // Default: Could be a GitHub raw URL or your own endpoint.
// For now, just return - users can provide their own source. // 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 ) ); $response = wp_remote_get( $source_url, array( 'timeout' => 30 ) );
@@ -173,7 +173,7 @@ class WPSP_Malware_Scanner {
$data = json_decode( $body, true ); $data = json_decode( $body, true );
if ( ! is_array( $data ) ) { 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. // Merge with existing hashes.
@@ -510,7 +510,7 @@ class WPSP_Malware_Scanner {
); );
// Allow adding custom signatures via filter. // 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. // Save scan stats.
update_option( self::LAST_SCAN_OPTION, time() ); update_option( self::LAST_SCAN_OPTION, time() );
update_option( update_option(
'wpsp_malware_scan_stats', 'arksp_malware_scan_stats',
array( array(
'files_scanned' => $file_count, 'files_scanned' => $file_count,
'duration' => round( $duration, 2 ), 'duration' => round( $duration, 2 ),
@@ -693,11 +693,11 @@ class WPSP_Malware_Scanner {
* @param array $results Scan results. * @param array $results Scan results.
*/ */
private function send_alert( $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; 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_name = get_bloginfo( 'name' );
$site_url = home_url(); $site_url = home_url();
@@ -717,34 +717,34 @@ class WPSP_Malware_Scanner {
$subject = sprintf( $subject = sprintf(
/* translators: %s: Site name */ /* 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 $site_name
); );
$message = sprintf( $message = sprintf(
/* translators: %s: Site URL */ /* 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 $site_url
); );
$message .= __( "Summary:\n", 'security-pack' ); $message .= __( "Summary:\n", 'arkhost-security-pack' );
/* translators: %d: Number of critical severity issues */ /* 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 */ /* 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 */ /* 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 */ /* 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; $count = 0;
foreach ( $results as $file => $findings ) { foreach ( $results as $file => $findings ) {
if ( $count >= 20 ) { if ( $count >= 20 ) {
$message .= sprintf( $message .= sprintf(
/* translators: %d: Number of additional files */ /* translators: %d: Number of additional files */
__( "... and %d more files\n", 'security-pack' ), __( "... and %d more files\n", 'arkhost-security-pack' ),
count( $results ) - 20 count( $results ) - 20
); );
break; break;
@@ -760,8 +760,8 @@ class WPSP_Malware_Scanner {
$count++; $count++;
} }
$message .= "\n" . __( "Please review these files in your WordPress admin panel under Settings > Security Pack.", '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.", '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 ); wp_mail( $email, $subject, $message );
} }
@@ -809,7 +809,7 @@ class WPSP_Malware_Scanner {
*/ */
public function get_quarantine_dir() { public function get_quarantine_dir() {
$upload_dir = wp_upload_dir(); $upload_dir = wp_upload_dir();
$quarantine = $upload_dir['basedir'] . '/wpsp-quarantine'; $quarantine = $upload_dir['basedir'] . '/arkhost-security-pack/quarantine';
if ( ! file_exists( $quarantine ) ) { if ( ! file_exists( $quarantine ) ) {
wp_mkdir_p( $quarantine ); wp_mkdir_p( $quarantine );
@@ -839,12 +839,12 @@ class WPSP_Malware_Scanner {
*/ */
public function quarantine_file( $file_path ) { public function quarantine_file( $file_path ) {
if ( ! file_exists( $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. // Security: Only allow quarantining files within wp-content.
if ( strpos( realpath( $file_path ), realpath( WP_CONTENT_DIR ) ) !== 0 ) { 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(); $quarantine_dir = $this->get_quarantine_dir();
@@ -876,7 +876,7 @@ class WPSP_Malware_Scanner {
// Move file to quarantine. // Move file to quarantine.
// phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename
if ( ! rename( $file_path, $quarantine_path ) ) { 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. // Save metadata.
@@ -885,9 +885,9 @@ class WPSP_Malware_Scanner {
file_put_contents( $meta_file, wp_json_encode( $metadata, JSON_PRETTY_PRINT ) ); file_put_contents( $meta_file, wp_json_encode( $metadata, JSON_PRETTY_PRINT ) );
// Update quarantine list in options. // Update quarantine list in options.
$quarantined = get_option( 'wpsp_quarantined_files', array() ); $quarantined = get_option( 'arksp_quarantined_files', array() );
$quarantined[ $quarantine_name ] = $metadata; $quarantined[ $quarantine_name ] = $metadata;
update_option( 'wpsp_quarantined_files', $quarantined ); update_option( 'arksp_quarantined_files', $quarantined );
// Remove from scan results if present. // Remove from scan results if present.
$results = get_option( self::RESULTS_OPTION, array() ); $results = get_option( self::RESULTS_OPTION, array() );
@@ -915,7 +915,7 @@ class WPSP_Malware_Scanner {
$meta_file = $quarantine_path . '.meta'; $meta_file = $quarantine_path . '.meta';
if ( ! file_exists( $quarantine_path ) ) { 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. // Get metadata.
@@ -926,7 +926,7 @@ class WPSP_Malware_Scanner {
} }
if ( empty( $metadata['original_path'] ) ) { 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']; $original_path = $metadata['original_path'];
@@ -940,7 +940,7 @@ class WPSP_Malware_Scanner {
// Restore file. // Restore file.
// phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename
if ( ! rename( $quarantine_path, $original_path ) ) { 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. // Clean up metadata file.
@@ -950,10 +950,10 @@ class WPSP_Malware_Scanner {
} }
// Update quarantine list. // Update quarantine list.
$quarantined = get_option( 'wpsp_quarantined_files', array() ); $quarantined = get_option( 'arksp_quarantined_files', array() );
if ( isset( $quarantined[ $quarantine_name ] ) ) { if ( isset( $quarantined[ $quarantine_name ] ) ) {
unset( $quarantined[ $quarantine_name ] ); unset( $quarantined[ $quarantine_name ] );
update_option( 'wpsp_quarantined_files', $quarantined ); update_option( 'arksp_quarantined_files', $quarantined );
} }
return array( return array(
@@ -974,13 +974,13 @@ class WPSP_Malware_Scanner {
$meta_file = $quarantine_path . '.meta'; $meta_file = $quarantine_path . '.meta';
if ( ! file_exists( $quarantine_path ) ) { 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. // Delete file.
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
if ( ! unlink( $quarantine_path ) ) { 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. // Clean up metadata file.
@@ -990,10 +990,10 @@ class WPSP_Malware_Scanner {
} }
// Update quarantine list. // Update quarantine list.
$quarantined = get_option( 'wpsp_quarantined_files', array() ); $quarantined = get_option( 'arksp_quarantined_files', array() );
if ( isset( $quarantined[ $quarantine_name ] ) ) { if ( isset( $quarantined[ $quarantine_name ] ) ) {
unset( $quarantined[ $quarantine_name ] ); unset( $quarantined[ $quarantine_name ] );
update_option( 'wpsp_quarantined_files', $quarantined ); update_option( 'arksp_quarantined_files', $quarantined );
} }
return array( 'success' => true ); return array( 'success' => true );
@@ -1005,7 +1005,7 @@ class WPSP_Malware_Scanner {
* @return array * @return array
*/ */
public function get_quarantined_files() { public function get_quarantined_files() {
return get_option( 'wpsp_quarantined_files', array() ); return get_option( 'arksp_quarantined_files', array() );
} }
/** /**
@@ -2,7 +2,7 @@
/** /**
* Two-Factor Authentication for Security Pack. * Two-Factor Authentication for Security Pack.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
// Prevent direct access. // Prevent direct access.
@@ -13,28 +13,28 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Two-Factor Authentication class using TOTP. * Two-Factor Authentication class using TOTP.
*/ */
class WPSP_Two_Factor { class ARKSP_Two_Factor {
/** /**
* User meta key for 2FA secret. * User meta key for 2FA secret.
* *
* @var string * @var string
*/ */
const SECRET_META_KEY = '_wpsp_2fa_secret'; const SECRET_META_KEY = '_arksp_2fa_secret';
/** /**
* User meta key for 2FA enabled status. * User meta key for 2FA enabled status.
* *
* @var string * @var string
*/ */
const ENABLED_META_KEY = '_wpsp_2fa_enabled'; const ENABLED_META_KEY = '_arksp_2fa_enabled';
/** /**
* User meta key for backup codes. * User meta key for backup codes.
* *
* @var string * @var string
*/ */
const BACKUP_CODES_META_KEY = '_wpsp_2fa_backup_codes'; const BACKUP_CODES_META_KEY = '_arksp_2fa_backup_codes';
/** /**
* TOTP code length. * TOTP code length.
@@ -54,7 +54,7 @@ class WPSP_Two_Factor {
* Constructor. * Constructor.
*/ */
public function __construct() { public function __construct() {
if ( ! Security_Pack::get_setting( 'two_factor_enabled', false ) ) { if ( ! ARKSP_Plugin::get_setting( 'two_factor_enabled', false ) ) {
return; return;
} }
@@ -62,8 +62,8 @@ class WPSP_Two_Factor {
add_filter( 'authenticate', array( $this, 'check_2fa_on_authenticate' ), 100, 3 ); add_filter( 'authenticate', array( $this, 'check_2fa_on_authenticate' ), 100, 3 );
// Handle the 2FA verification form. // Handle the 2FA verification form.
// Use login_form_wpsp_2fa for standard wp-login.php. // Use login_form_arksp_2fa for standard wp-login.php.
add_action( 'login_form_wpsp_2fa', array( $this, 'render_2fa_form' ) ); add_action( 'login_form_arksp_2fa', array( $this, 'render_2fa_form' ) );
// Also check on login_init for custom login URLs. // Also check on login_init for custom login URLs.
add_action( 'login_init', array( $this, 'maybe_render_2fa_form' ), 5 ); 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' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_profile_scripts' ) );
// AJAX handlers. // AJAX handlers.
add_action( 'wp_ajax_wpsp_generate_2fa_secret', array( $this, 'ajax_generate_secret' ) ); add_action( 'wp_ajax_arksp_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_arksp_verify_2fa_setup', array( $this, 'ajax_verify_setup' ) );
add_action( 'wp_ajax_wpsp_disable_2fa', array( $this, 'ajax_disable_2fa' ) ); add_action( 'wp_ajax_arksp_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_regenerate_backup_codes', array( $this, 'ajax_regenerate_backup_codes' ) );
// Enforce 2FA for admins. // 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' ) ); add_action( 'admin_init', array( $this, 'enforce_admin_2fa' ) );
} }
} }
@@ -98,13 +98,41 @@ class WPSP_Two_Factor {
return; 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( wp_enqueue_script(
'wpsp-qrcode', 'arksp-qrcode',
WPSP_PLUGIN_URL . 'assets/js/qrcode.min.js', ARKSP_PLUGIN_URL . 'assets/js/qrcode.min.js',
array(), array(),
WPSP_VERSION, ARKSP_VERSION,
true 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. // Nonce not possible during 2FA authentication flow; transient token validates the session.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$redirect = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : admin_url(); $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, 'user_id' => $user->ID,
'redirect' => $redirect, 'redirect' => $redirect,
), 5 * MINUTE_IN_SECONDS ); ), 5 * MINUTE_IN_SECONDS );
// Redirect to 2FA form immediately. // Redirect to 2FA form immediately.
wp_safe_redirect( add_query_arg( array( wp_safe_redirect( add_query_arg( array(
'action' => 'wpsp_2fa', 'action' => 'arksp_2fa',
'token' => $token, 'token' => $token,
), wp_login_url() ) ); ), wp_login_url() ) );
exit; exit;
@@ -174,7 +202,7 @@ class WPSP_Two_Factor {
} }
// Redirect to profile page with notice. // 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; exit;
} }
@@ -183,7 +211,7 @@ class WPSP_Two_Factor {
*/ */
public function maybe_render_2fa_form() { public function maybe_render_2fa_form() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended // 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(); $this->render_2fa_form();
} }
} }
@@ -200,7 +228,7 @@ class WPSP_Two_Factor {
exit; exit;
} }
$data = get_transient( 'wpsp_2fa_' . $token ); $data = get_transient( 'arksp_2fa_' . $token );
if ( ! $data ) { if ( ! $data ) {
wp_safe_redirect( wp_login_url() ); wp_safe_redirect( wp_login_url() );
@@ -210,20 +238,20 @@ class WPSP_Two_Factor {
$error = ''; $error = '';
// Handle form submission - 2FA verification uses transient token instead of nonce; user already authenticated via password. // 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. // Check TOTP code first, then backup code.
$code = ''; $code = '';
if ( ! empty( $_POST['wpsp_2fa_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['wpsp_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['wpsp_backup_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['wpsp_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']; $user_id = $data['user_id'];
if ( ! empty( $code ) && $this->verify_code( $user_id, $code ) ) { if ( ! empty( $code ) && $this->verify_code( $user_id, $code ) ) {
// Delete the token. // Delete the token.
delete_transient( 'wpsp_2fa_' . $token ); delete_transient( 'arksp_2fa_' . $token );
// Log the user in. // Log the user in.
wp_set_auth_cookie( $user_id, false ); wp_set_auth_cookie( $user_id, false );
@@ -234,38 +262,38 @@ class WPSP_Two_Factor {
exit; exit;
} }
$error = __( 'Invalid verification code.', 'security-pack' ); $error = __( 'Invalid verification code.', 'arkhost-security-pack' );
} }
// Render the form. // Render the form.
login_header( __( 'Two-Factor Authentication', 'security-pack' ) ); login_header( __( 'Two-Factor Authentication', 'arkhost-security-pack' ) );
?> ?>
<form name="wpsp_2fa_form" id="wpsp_2fa_form" action="" method="post"> <form name="arksp_2fa_form" id="arksp_2fa_form" action="" method="post">
<?php if ( $error ) : ?> <?php if ( $error ) : ?>
<div id="login_error"><?php echo esc_html( $error ); ?></div> <div id="login_error"><?php echo esc_html( $error ); ?></div>
<?php endif; ?> <?php endif; ?>
<p><?php esc_html_e( 'Enter the verification code from your authenticator app.', 'security-pack' ); ?></p> <p><?php esc_html_e( 'Enter the verification code from your authenticator app.', 'arkhost-security-pack' ); ?></p>
<p> <p>
<label for="wpsp_2fa_code"><?php esc_html_e( 'Verification Code', 'security-pack' ); ?></label> <label for="arksp_2fa_code"><?php esc_html_e( 'Verification Code', 'arkhost-security-pack' ); ?></label>
<input type="text" name="wpsp_2fa_code" id="wpsp_2fa_code" class="input" size="20" autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" autofocus /> <input type="text" name="arksp_2fa_code" id="arksp_2fa_code" class="input" size="20" autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" autofocus />
</p> </p>
<p class="submit"> <p class="submit">
<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php esc_attr_e( 'Verify', 'security-pack' ); ?>" /> <input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php esc_attr_e( 'Verify', 'arkhost-security-pack' ); ?>" />
</p> </p>
<p class="wpsp-backup-code-link"> <p class="arksp-backup-code-link">
<a href="#" onclick="document.getElementById('wpsp-backup-field').style.display='block';this.style.display='none';return false;"> <a href="#" onclick="document.getElementById('arksp-backup-field').style.display='block';this.style.display='none';return false;">
<?php esc_html_e( 'Use a backup code', 'security-pack' ); ?> <?php esc_html_e( 'Use a backup code', 'arkhost-security-pack' ); ?>
</a> </a>
</p> </p>
<div id="wpsp-backup-field" style="display:none;"> <div id="arksp-backup-field" style="display:none;">
<p><?php esc_html_e( 'Or enter a backup code:', 'security-pack' ); ?></p> <p><?php esc_html_e( 'Or enter a backup code:', 'arkhost-security-pack' ); ?></p>
<p> <p>
<input type="text" name="wpsp_backup_code" id="wpsp_backup_code" class="input" size="20" /> <input type="text" name="arksp_backup_code" id="arksp_backup_code" class="input" size="20" />
</p> </p>
</div> </div>
</form> </form>
@@ -280,7 +308,7 @@ class WPSP_Two_Factor {
* @param WP_User $user User object. * @param WP_User $user User object.
*/ */
public function show_user_2fa_settings( $user ) { public function show_user_2fa_settings( $user ) {
if ( ! Security_Pack::get_setting( 'two_factor_enabled', false ) ) { if ( ! ARKSP_Plugin::get_setting( 'two_factor_enabled', false ) ) {
return; return;
} }
@@ -288,147 +316,67 @@ class WPSP_Two_Factor {
// Show notice if 2FA is required. // Show notice if 2FA is required.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['wpsp_2fa_required'] ) && ! $is_enabled ) : if ( isset( $_GET['arksp_2fa_required'] ) && ! $is_enabled ) :
?> ?>
<div class="notice notice-warning" style="margin-bottom: 20px;"> <div class="notice notice-warning" style="margin-bottom: 20px;">
<p><strong><?php esc_html_e( 'Two-Factor Authentication Required', 'security-pack' ); ?></strong></p> <p><strong><?php esc_html_e( 'Two-Factor Authentication Required', 'arkhost-security-pack' ); ?></strong></p>
<p><?php esc_html_e( 'You must set up 2FA to access the dashboard.', 'security-pack' ); ?></p> <p><?php esc_html_e( 'You must set up 2FA to access the dashboard.', 'arkhost-security-pack' ); ?></p>
</div> </div>
<?php <?php
endif; endif;
?> ?>
<h2><?php esc_html_e( 'Two-Factor Authentication', 'security-pack' ); ?></h2> <h2><?php esc_html_e( 'Two-Factor Authentication', 'arkhost-security-pack' ); ?></h2>
<table class="form-table" role="presentation"> <table class="form-table" role="presentation">
<tr> <tr>
<th scope="row"><?php esc_html_e( 'Status', 'security-pack' ); ?></th> <th scope="row"><?php esc_html_e( 'Status', 'arkhost-security-pack' ); ?></th>
<td> <td>
<?php if ( $is_enabled ) : ?> <?php if ( $is_enabled ) : ?>
<?php $remaining_codes = absint( $this->get_backup_codes_count( $user->ID ) ); ?> <?php $remaining_codes = absint( $this->get_backup_codes_count( $user->ID ) ); ?>
<span class="wpsp-2fa-status wpsp-2fa-enabled"><?php esc_html_e( 'Enabled', 'security-pack' ); ?></span> <span class="arksp-2fa-status arksp-2fa-enabled"><?php esc_html_e( 'Enabled', 'arkhost-security-pack' ); ?></span>
<p> <p>
<button type="button" class="button" id="wpsp-disable-2fa"><?php esc_html_e( 'Disable 2FA', 'security-pack' ); ?></button> <button type="button" class="button" id="arksp-disable-2fa"><?php esc_html_e( 'Disable 2FA', 'arkhost-security-pack' ); ?></button>
<button type="button" class="button" id="wpsp-regenerate-backup-codes"><?php esc_html_e( 'Regenerate Backup Codes', 'security-pack' ); ?></button> <button type="button" class="button" id="arksp-regenerate-backup-codes"><?php esc_html_e( 'Regenerate Backup Codes', 'arkhost-security-pack' ); ?></button>
</p> </p>
<p class="description"> <p class="description">
<?php <?php
printf( printf(
/* translators: %d: number of remaining backup codes */ /* translators: %d: number of remaining backup codes */
esc_html__( 'You have %d backup codes remaining.', 'security-pack' ), esc_html__( 'You have %d backup codes remaining.', 'arkhost-security-pack' ),
intval( $remaining_codes ) intval( $remaining_codes )
); );
?> ?>
<?php if ( $remaining_codes < 3 ) : ?> <?php if ( $remaining_codes < 3 ) : ?>
<strong style="color: #dc3232;"><?php esc_html_e( 'Consider regenerating your backup codes.', 'security-pack' ); ?></strong> <strong style="color: #dc3232;"><?php esc_html_e( 'Consider regenerating your backup codes.', 'arkhost-security-pack' ); ?></strong>
<?php endif; ?> <?php endif; ?>
</p> </p>
<div id="wpsp-backup-codes-display" style="display:none; background: #f6f7f7; padding: 15px; margin-top: 10px; border-radius: 4px;"> <div id="arksp-backup-codes-display" style="display:none; background: #f6f7f7; padding: 15px; margin-top: 10px; border-radius: 4px;">
<p><strong><?php esc_html_e( 'New Backup Codes:', 'security-pack' ); ?></strong></p> <p><strong><?php esc_html_e( 'New Backup Codes:', 'arkhost-security-pack' ); ?></strong></p>
<pre id="wpsp-new-backup-codes" style="background: #fff; padding: 10px;"></pre> <pre id="arksp-new-backup-codes" style="background: #fff; padding: 10px;"></pre>
<p class="description" style="color: #dc3232;"> <p class="description" style="color: #dc3232;">
<strong><?php esc_html_e( 'IMPORTANT: Save these codes now!', 'security-pack' ); ?></strong><br> <strong><?php esc_html_e( 'IMPORTANT: Save these codes now!', 'arkhost-security-pack' ); ?></strong><br>
<?php esc_html_e( 'These codes will NOT be shown again. Store them in a safe place.', 'security-pack' ); ?> <?php esc_html_e( 'These codes will NOT be shown again. Store them in a safe place.', 'arkhost-security-pack' ); ?>
</p> </p>
</div> </div>
<?php else : ?> <?php else : ?>
<span class="wpsp-2fa-status wpsp-2fa-disabled"><?php esc_html_e( 'Disabled', 'security-pack' ); ?></span> <span class="arksp-2fa-status arksp-2fa-disabled"><?php esc_html_e( 'Disabled', 'arkhost-security-pack' ); ?></span>
<p> <p>
<button type="button" class="button button-primary" id="wpsp-setup-2fa"><?php esc_html_e( 'Set Up 2FA', 'security-pack' ); ?></button> <button type="button" class="button button-primary" id="arksp-setup-2fa"><?php esc_html_e( 'Set Up 2FA', 'arkhost-security-pack' ); ?></button>
</p> </p>
<div id="wpsp-2fa-setup" style="display:none;"> <div id="arksp-2fa-setup" style="display:none;">
<p><?php esc_html_e( 'Scan this QR code with your authenticator app:', 'security-pack' ); ?></p> <p><?php esc_html_e( 'Scan this QR code with your authenticator app:', 'arkhost-security-pack' ); ?></p>
<div id="wpsp-2fa-qr"></div> <div id="arksp-2fa-qr"></div>
<p><strong><?php esc_html_e( 'Manual entry key:', 'security-pack' ); ?></strong> <code id="wpsp-2fa-secret"></code></p> <p><strong><?php esc_html_e( 'Manual entry key:', 'arkhost-security-pack' ); ?></strong> <code id="arksp-2fa-secret"></code></p>
<p> <p>
<label for="wpsp-2fa-verify-code"><?php esc_html_e( 'Enter verification code to confirm:', 'security-pack' ); ?></label> <label for="arksp-2fa-verify-code"><?php esc_html_e( 'Enter verification code to confirm:', 'arkhost-security-pack' ); ?></label>
<input type="text" id="wpsp-2fa-verify-code" class="regular-text" autocomplete="off" /> <input type="text" id="arksp-2fa-verify-code" class="regular-text" autocomplete="off" />
<button type="button" class="button button-primary" id="wpsp-verify-2fa-setup"><?php esc_html_e( 'Verify & Enable', 'security-pack' ); ?></button> <button type="button" class="button button-primary" id="arksp-verify-2fa-setup"><?php esc_html_e( 'Verify & Enable', 'arkhost-security-pack' ); ?></button>
</p> </p>
<div id="wpsp-2fa-setup-result"></div> <div id="arksp-2fa-setup-result"></div>
</div> </div>
<?php endif; ?> <?php endif; ?>
</td> </td>
</tr> </tr>
</table> </table>
<script>
jQuery(document).ready(function($) {
$('#wpsp-setup-2fa').on('click', function() {
$('#wpsp-2fa-setup').show();
$(this).hide();
// Generate new secret.
$.post(ajaxurl, {
action: 'wpsp_generate_2fa_secret',
user_id: <?php echo (int) $user->ID; ?>,
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_2fa_setup' ) ); ?>'
}, function(response) {
if (response.success) {
$('#wpsp-2fa-secret').text(response.data.secret);
// Generate QR code client-side.
var qr = qrcode(0, 'M');
qr.addData(response.data.otpauth);
qr.make();
$('#wpsp-2fa-qr').html(qr.createSvgTag(5, 0));
}
});
});
$('#wpsp-verify-2fa-setup').on('click', function() {
var code = $('#wpsp-2fa-verify-code').val();
$.post(ajaxurl, {
action: 'wpsp_verify_2fa_setup',
user_id: <?php echo (int) $user->ID; ?>,
code: code,
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_2fa_setup' ) ); ?>'
}, function(response) {
if (response.success && response.data.backup_codes) {
// Show backup codes - user MUST save these.
var codesHtml = '<div style="background:#d4edda;border:1px solid #c3e6cb;padding:20px;margin:10px 0;border-radius:4px;">';
codesHtml += '<h3 style="margin-top:0;color:#155724;"><?php echo esc_js( __( '2FA Enabled! Save Your Backup Codes', 'security-pack' ) ); ?></h3>';
codesHtml += '<p style="color:#dc3232;font-weight:bold;"><?php echo esc_js( __( 'IMPORTANT: These codes will NOT be shown again!', 'security-pack' ) ); ?></p>';
codesHtml += '<pre style="background:#fff;padding:15px;font-size:14px;line-height:1.8;">' + response.data.backup_codes.join('\n') + '</pre>';
codesHtml += '<p><?php echo esc_js( __( 'Store these codes in a safe place. Each code can only be used once.', 'security-pack' ) ); ?></p>';
codesHtml += '<button type="button" class="button button-primary" onclick="location.reload();"><?php echo esc_js( __( 'I have saved my codes', 'security-pack' ) ); ?></button>';
codesHtml += '</div>';
$('#wpsp-2fa-setup').html(codesHtml);
} else if (response.success) {
location.reload();
} else {
$('#wpsp-2fa-setup-result').html('<p style="color:red;">' + response.data.message + '</p>');
}
});
});
$('#wpsp-disable-2fa').on('click', function() {
if (confirm('<?php echo esc_js( __( 'Are you sure you want to disable 2FA?', 'security-pack' ) ); ?>')) {
$.post(ajaxurl, {
action: 'wpsp_disable_2fa',
user_id: <?php echo (int) $user->ID; ?>,
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_2fa_setup' ) ); ?>'
}, function(response) {
if (response.success) {
location.reload();
}
});
}
});
$('#wpsp-regenerate-backup-codes').on('click', function() {
if (confirm('<?php echo esc_js( __( 'This will invalidate all existing backup codes. Are you sure?', 'security-pack' ) ); ?>')) {
$.post(ajaxurl, {
action: 'wpsp_regenerate_backup_codes',
user_id: <?php echo (int) $user->ID; ?>,
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'wpsp_2fa_setup' ) ); ?>'
}, function(response) {
if (response.success) {
$('#wpsp-new-backup-codes').text(response.data.codes.join('\n'));
$('#wpsp-backup-codes-display').show();
}
});
}
});
});
</script>
<?php <?php
} }
@@ -445,12 +393,12 @@ class WPSP_Two_Factor {
* Generate new 2FA secret via AJAX. * Generate new 2FA secret via AJAX.
*/ */
public function ajax_generate_secret() { public function ajax_generate_secret() {
check_ajax_referer( 'wpsp_2fa_setup' ); check_ajax_referer( 'arksp_2fa_setup' );
$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
if ( ! current_user_can( 'edit_user', $user_id ) ) { if ( ! current_user_can( 'edit_user', $user_id ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'security-pack' ) ) ); wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) );
} }
$secret = $this->generate_secret(); $secret = $this->generate_secret();
@@ -480,24 +428,24 @@ class WPSP_Two_Factor {
* Verify 2FA setup via AJAX. * Verify 2FA setup via AJAX.
*/ */
public function ajax_verify_setup() { public function ajax_verify_setup() {
check_ajax_referer( 'wpsp_2fa_setup' ); check_ajax_referer( 'arksp_2fa_setup' );
$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
$code = isset( $_POST['code'] ) ? sanitize_text_field( wp_unslash( $_POST['code'] ) ) : ''; $code = isset( $_POST['code'] ) ? sanitize_text_field( wp_unslash( $_POST['code'] ) ) : '';
if ( ! current_user_can( 'edit_user', $user_id ) ) { if ( ! current_user_can( 'edit_user', $user_id ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'security-pack' ) ) ); wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) );
} }
$pending_secret = get_user_meta( $user_id, self::SECRET_META_KEY . '_pending', true ); $pending_secret = get_user_meta( $user_id, self::SECRET_META_KEY . '_pending', true );
if ( empty( $pending_secret ) ) { if ( empty( $pending_secret ) ) {
wp_send_json_error( array( 'message' => __( 'No pending setup found.', 'security-pack' ) ) ); wp_send_json_error( array( 'message' => __( 'No pending setup found.', 'arkhost-security-pack' ) ) );
} }
// Verify the code. // Verify the code.
if ( ! $this->verify_totp( $pending_secret, $code ) ) { if ( ! $this->verify_totp( $pending_secret, $code ) ) {
wp_send_json_error( array( 'message' => __( 'Invalid code. Please try again.', 'security-pack' ) ) ); wp_send_json_error( array( 'message' => __( 'Invalid code. Please try again.', 'arkhost-security-pack' ) ) );
} }
// Enable 2FA. // Enable 2FA.
@@ -511,7 +459,7 @@ class WPSP_Two_Factor {
update_user_meta( $user_id, self::BACKUP_CODES_META_KEY, $hashed_codes ); update_user_meta( $user_id, self::BACKUP_CODES_META_KEY, $hashed_codes );
wp_send_json_success( array( wp_send_json_success( array(
'message' => __( '2FA enabled successfully. Save your backup codes now - they will not be shown again!', 'security-pack' ), 'message' => __( '2FA enabled successfully. Save your backup codes now - they will not be shown again!', 'arkhost-security-pack' ),
'backup_codes' => $plain_codes, 'backup_codes' => $plain_codes,
'show_codes' => true, 'show_codes' => true,
) ); ) );
@@ -521,31 +469,31 @@ class WPSP_Two_Factor {
* Disable 2FA via AJAX. * Disable 2FA via AJAX.
*/ */
public function ajax_disable_2fa() { public function ajax_disable_2fa() {
check_ajax_referer( 'wpsp_2fa_setup' ); check_ajax_referer( 'arksp_2fa_setup' );
$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
if ( ! current_user_can( 'edit_user', $user_id ) ) { if ( ! current_user_can( 'edit_user', $user_id ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'security-pack' ) ) ); wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) );
} }
delete_user_meta( $user_id, self::SECRET_META_KEY ); delete_user_meta( $user_id, self::SECRET_META_KEY );
delete_user_meta( $user_id, self::ENABLED_META_KEY ); delete_user_meta( $user_id, self::ENABLED_META_KEY );
delete_user_meta( $user_id, self::BACKUP_CODES_META_KEY ); delete_user_meta( $user_id, self::BACKUP_CODES_META_KEY );
wp_send_json_success( array( 'message' => __( '2FA disabled.', 'security-pack' ) ) ); wp_send_json_success( array( 'message' => __( '2FA disabled.', 'arkhost-security-pack' ) ) );
} }
/** /**
* Regenerate backup codes via AJAX. * Regenerate backup codes via AJAX.
*/ */
public function ajax_regenerate_backup_codes() { public function ajax_regenerate_backup_codes() {
check_ajax_referer( 'wpsp_2fa_setup' ); check_ajax_referer( 'arksp_2fa_setup' );
$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
if ( ! current_user_can( 'edit_user', $user_id ) ) { if ( ! current_user_can( 'edit_user', $user_id ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'security-pack' ) ) ); wp_send_json_error( array( 'message' => __( 'Permission denied.', 'arkhost-security-pack' ) ) );
} }
// Generate new codes. // Generate new codes.
@@ -558,7 +506,7 @@ class WPSP_Two_Factor {
// Return plain codes to show user ONCE. // Return plain codes to show user ONCE.
wp_send_json_success( array( wp_send_json_success( array(
'codes' => $plain_codes, 'codes' => $plain_codes,
'message' => __( 'Backup codes regenerated. Save these codes now - they will not be shown again.', 'security-pack' ), 'message' => __( 'Backup codes regenerated. Save these codes now - they will not be shown again.', 'arkhost-security-pack' ),
) ); ) );
} }
@@ -2,5 +2,5 @@
/** /**
* Silence is golden. * Silence is golden.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
@@ -2,5 +2,5 @@
/** /**
* Silence is golden. * Silence is golden.
* *
* @package Security_Pack * @package ArkHost_Security_Pack
*/ */
@@ -0,0 +1,6 @@
<?php
/**
* Silence is golden.
*
* @package ArkHost_Security_Pack
*/
@@ -1,4 +1,4 @@
=== Security Pack === === ArkHost Security Pack ===
Contributors: arkhost Contributors: arkhost
Tags: security, firewall, login, 2fa, malware Tags: security, firewall, login, 2fa, malware
Requires at least: 5.0 Requires at least: 5.0
@@ -81,9 +81,35 @@ External connections:
* WordPress.org API (core file checksums) * WordPress.org API (core file checksums)
* IP2Location (database download, only when you click it) * IP2Location (database download, only when you click it)
== External services ==
This plugin connects to the following external services under specific circumstances:
= WordPress.org Checksums API =
* Service: api.wordpress.org/core/checksums/1.0/
* Used for: Verifying WordPress core file integrity by comparing local files against official checksums
* Data sent: WordPress version and locale
* When: During daily scheduled file integrity scans and when manually triggered by the admin
* Privacy policy: https://wordpress.org/about/privacy/
= IP Detection Services =
* Services: api.ipify.org, ifconfig.me, icanhazip.com
* Used for: Detecting the server's public IP address for the "Whitelist My IP" tool
* Data sent: Standard HTTP request (no personal data)
* When: Only when an admin uses the "Whitelist My IP" feature in the Tools tab
* Terms: https://www.ipify.org/ / https://ifconfig.me/ / https://icanhazip.com/
= IP2Location =
* Service: download.ip2location.com
* Used for: Downloading the free IP2Location LITE geolocation database for country-based blocking
* Data sent: Standard HTTP request (optional: user's download token if configured)
* When: Only when an admin clicks "Download IP2Location Database" in the IP Control tab
* Terms of service: https://www.ip2location.com/terms
* Privacy policy: https://www.ip2location.com/privacy
== Installation == == Installation ==
1. Upload the plugin files to `/wp-content/plugins/security-pack/` 1. Upload the plugin files to `/wp-content/plugins/arkhost-security-pack/`
2. Activate the plugin through the 'Plugins' screen 2. Activate the plugin through the 'Plugins' screen
3. Configure under the Security menu 3. Configure under the Security menu
+55
View File
@@ -0,0 +1,55 @@
<?php
/**
* ArkHost Security Pack Uninstaller.
*
* @package ArkHost_Security_Pack
*/
// If uninstall not called from WordPress, exit.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
// Load the database class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-arksp-db.php';
// Run uninstall.
ARKSP_DB::uninstall();
// Clear any remaining transients.
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup during uninstall, caching not applicable.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_arksp_%'" );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup during uninstall, caching not applicable.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_arksp_%'" );
// Clear scheduled events.
wp_clear_scheduled_hook( 'arksp_daily_cleanup' );
wp_clear_scheduled_hook( 'arksp_daily_file_scan' );
wp_clear_scheduled_hook( 'arksp_weekly_malware_scan' );
// Delete user meta for 2FA.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup during uninstall, caching not applicable.
$wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE '_arksp_%'" );
// Clean up uploads directory (geo-database and quarantine files).
$arksp_upload_dir = wp_upload_dir();
$arksp_plugin_data = $arksp_upload_dir['basedir'] . '/arkhost-security-pack';
if ( is_dir( $arksp_plugin_data ) ) {
// Recursively delete all files and subdirectories.
$arksp_iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( $arksp_plugin_data, RecursiveDirectoryIterator::SKIP_DOTS ),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ( $arksp_iterator as $arksp_item ) {
if ( $arksp_item->isDir() ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- Uninstall cleanup.
rmdir( $arksp_item->getPathname() );
} else {
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink -- Uninstall cleanup.
unlink( $arksp_item->getPathname() );
}
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- Uninstall cleanup.
rmdir( $arksp_plugin_data );
}

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 242 KiB

Before

Width:  |  Height:  |  Size: 312 KiB

After

Width:  |  Height:  |  Size: 312 KiB

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 227 KiB

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Before

Width:  |  Height:  |  Size: 734 KiB

After

Width:  |  Height:  |  Size: 734 KiB

-103
View File
@@ -1,103 +0,0 @@
# Security Pack
WordPress security without the bullshit. No upsells, no premium tier, no fake threat counters.
## Requirements
- WordPress 5.0+
- PHP 7.4+
## What It Does
### Login Protection
- Blocks IPs after failed login attempts
- Custom login URL (hides wp-login.php)
- Hides wp-admin from logged-out users
- Honeypot field for bots
- Hides login errors (stops username enumeration)
- Email alerts for admin logins from new IPs
- Country/IP restrictions on login page
### IP Control
- Whitelist and blacklist
- Auto-blacklist after repeated lockouts
- IPv4, IPv6, CIDR supported
### Geo Blocking
- Block countries
- Uses free IP2Location LITE database
- One-click download
### Hardening
- Disable XML-RPC
- Disable dashboard file editing
- Disable application passwords
- Restrict REST API to logged-in users
- Remove WordPress version
- Block user enumeration (?author=1 and REST API)
- Disable pingbacks/trackbacks
### Security Headers
X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy, Content-Security-Policy, HSTS
### Two-Factor Authentication
- TOTP (Google Authenticator, Authy, etc.)
- Backup codes
- Enforce for admins
### File Integrity Monitoring
- Checks WordPress core files against official checksums
- Daily scans
- Email alerts on changes
### Malware Scanner
- Scans plugins, themes, uploads
- Pattern-based detection
- Weekly scans
- Does not scan core files (that's what File Integrity is for)
### Activity Log
- Login attempts, lockouts, blocks
- IP, country, username, timestamp
- Configurable retention
- CSV export
### Tools
- Export/import settings
- Force logout all users
- Test email
## Installation
1. Upload to `/wp-content/plugins/security-pack/`
2. Activate
3. Configure under Security menu
## FAQ
**Premium version?**
No. This is it.
**Will it slow my site?**
No. Checks run on login and admin access, not frontend loads.
**Locked myself out?**
Rename the plugin folder via FTP/SSH. Log in. Fix settings.
**Geo-blocking without database?**
Won't work. Download IP2Location LITE from settings. It's free.
**Use with other security plugins?**
Possible but likely conflicts. Pick one.
## Privacy
No tracking. No analytics. No telemetry.
External connections:
- WordPress.org API (core file checksums)
- IP2Location (database download, only when you click it)
## License
GPLv2 or later
-524
View File
@@ -1,524 +0,0 @@
/**
* WP Security Pack Admin Styles
*
* Clean, modern design - no scary dashboards or fake threat counters.
*/
/* CSS Custom Properties */
:root {
--wpsp-primary: #2271b1;
--wpsp-primary-hover: #135e96;
--wpsp-success: #00a32a;
--wpsp-success-bg: #edfaef;
--wpsp-warning: #dba617;
--wpsp-warning-bg: #fcf9e8;
--wpsp-danger: #d63638;
--wpsp-danger-bg: #fcf0f1;
--wpsp-info: #72aee6;
--wpsp-info-bg: #f0f6fc;
--wpsp-border: #e0e0e0;
--wpsp-border-light: #f0f0f1;
--wpsp-text: #1d2327;
--wpsp-text-light: #646970;
--wpsp-bg: #fff;
--wpsp-bg-alt: #f6f7f7;
--wpsp-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
--wpsp-shadow-hover: 0 2px 8px rgba(0, 0, 0, 0.08);
--wpsp-radius: 8px;
--wpsp-radius-sm: 4px;
--wpsp-transition: 0.15s ease;
}
/* Main wrapper */
.wpsp-wrap {
max-width: 1200px;
}
.wpsp-wrap h1 {
font-size: 24px;
font-weight: 600;
margin-bottom: 1.5em;
padding: 0;
color: var(--wpsp-text);
}
/* Tabs */
.wpsp-tabs {
margin-bottom: 24px;
border-bottom: 1px solid var(--wpsp-border);
display: flex;
flex-wrap: wrap;
gap: 2px;
}
.wpsp-tabs .nav-tab {
margin-left: 0;
margin-right: 0;
border-radius: var(--wpsp-radius-sm) var(--wpsp-radius-sm) 0 0;
transition: background var(--wpsp-transition), color var(--wpsp-transition);
padding: 8px 16px;
font-weight: 500;
}
.wpsp-tabs .nav-tab:hover {
background: var(--wpsp-bg-alt);
}
.wpsp-tabs .nav-tab-active {
background: var(--wpsp-bg);
border-bottom-color: var(--wpsp-bg);
}
/* Sections */
.wpsp-section {
background: var(--wpsp-bg);
border: 1px solid var(--wpsp-border);
border-radius: var(--wpsp-radius);
padding: 24px;
margin-bottom: 20px;
box-shadow: var(--wpsp-shadow);
transition: box-shadow var(--wpsp-transition);
}
.wpsp-section:hover {
box-shadow: var(--wpsp-shadow-hover);
}
.wpsp-section h2 {
margin: 0 0 16px 0;
padding: 0 0 12px 0;
font-size: 1.2em;
font-weight: 600;
border-bottom: 1px solid var(--wpsp-border-light);
color: var(--wpsp-text);
}
.wpsp-section h3 {
margin-top: 1.5em;
font-size: 1em;
font-weight: 600;
color: var(--wpsp-text);
}
/* Form tables */
.wpsp-section .form-table {
margin-top: 0;
}
.wpsp-section .form-table th {
width: 220px;
padding-left: 0;
font-weight: 500;
color: var(--wpsp-text);
}
.wpsp-section .form-table td {
padding-top: 12px;
padding-bottom: 12px;
}
/* Status indicators */
.wpsp-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;
}
.wpsp-status-ok {
background: var(--wpsp-success-bg);
color: var(--wpsp-success);
}
.wpsp-status-warning {
background: var(--wpsp-warning-bg);
color: #8a6d00;
}
.wpsp-status-error {
background: var(--wpsp-danger-bg);
color: var(--wpsp-danger);
}
/* Stats */
.wpsp-stats {
display: flex;
gap: 16px;
margin-bottom: 24px;
flex-wrap: wrap;
}
.wpsp-stat {
background: var(--wpsp-bg);
border: 1px solid var(--wpsp-border);
padding: 20px 28px;
border-radius: var(--wpsp-radius);
text-align: center;
min-width: 130px;
transition: transform var(--wpsp-transition), box-shadow var(--wpsp-transition);
}
.wpsp-stat:hover {
transform: translateY(-2px);
box-shadow: var(--wpsp-shadow-hover);
}
.wpsp-stat-value {
display: block;
font-size: 32px;
font-weight: 700;
color: var(--wpsp-text);
line-height: 1.2;
}
.wpsp-stat-label {
display: block;
font-size: 11px;
font-weight: 500;
color: var(--wpsp-text-light);
margin-top: 6px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Stat color variations */
.wpsp-stat-success {
border-left: 4px solid var(--wpsp-success);
}
.wpsp-stat-success .wpsp-stat-value {
color: var(--wpsp-success);
}
.wpsp-stat-warning {
border-left: 4px solid var(--wpsp-warning);
}
.wpsp-stat-warning .wpsp-stat-value {
color: #8a6d00;
}
.wpsp-stat-danger {
border-left: 4px solid var(--wpsp-danger);
}
.wpsp-stat-danger .wpsp-stat-value {
color: var(--wpsp-danger);
}
.wpsp-stat-info {
border-left: 4px solid var(--wpsp-primary);
}
.wpsp-stat-info .wpsp-stat-value {
color: var(--wpsp-primary);
}
/* Tables */
.wpsp-section .widefat {
margin-top: 16px;
border-radius: var(--wpsp-radius-sm);
overflow: hidden;
}
.wpsp-section .widefat td,
.wpsp-section .widefat th {
padding: 12px 14px;
}
.wpsp-section .widefat thead th {
font-weight: 600;
background: var(--wpsp-bg-alt);
}
.wpsp-section .widefat tbody tr {
transition: background var(--wpsp-transition);
}
.wpsp-section .widefat tbody tr:hover {
background: var(--wpsp-bg-alt);
}
/* Country select */
.wpsp-country-select {
min-width: 300px;
height: 200px !important;
border-radius: var(--wpsp-radius-sm);
}
/* Event type colors */
.wpsp-event-login_success {
color: var(--wpsp-success);
}
.wpsp-event-login_failed {
color: #8a6d00;
}
.wpsp-event-lockout,
.wpsp-event-ip_blocked,
.wpsp-event-geo_blocked {
color: var(--wpsp-danger);
}
/* Country badge */
.wpsp-country {
display: inline-block;
background: var(--wpsp-bg-alt);
padding: 2px 8px;
border-radius: 50px;
font-size: 11px;
font-weight: 500;
margin-left: 6px;
}
/* Severity badges */
.wpsp-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 */
.wpsp-footer {
margin-top: 32px;
padding-top: 20px;
border-top: 1px solid var(--wpsp-border);
color: var(--wpsp-text-light);
font-size: 13px;
}
.wpsp-footer a {
color: var(--wpsp-primary);
text-decoration: none;
transition: color var(--wpsp-transition);
}
.wpsp-footer a:hover {
color: var(--wpsp-primary-hover);
text-decoration: underline;
}
/* 2FA status */
.wpsp-2fa-status {
display: inline-flex;
align-items: center;
padding: 6px 14px;
border-radius: 50px;
font-weight: 600;
font-size: 13px;
}
.wpsp-2fa-enabled {
background: var(--wpsp-success-bg);
color: var(--wpsp-success);
}
.wpsp-2fa-disabled {
background: var(--wpsp-danger-bg);
color: var(--wpsp-danger);
}
/* Responsive */
@media screen and (max-width: 782px) {
.wpsp-stats {
flex-direction: column;
}
.wpsp-stat {
min-width: auto;
}
.wpsp-section .form-table th {
width: auto;
}
.wpsp-country-select {
min-width: 100%;
}
.wpsp-tabs {
gap: 4px;
}
.wpsp-tabs .nav-tab {
padding: 6px 12px;
font-size: 13px;
}
}
/* Code blocks */
.wpsp-section code {
background: var(--wpsp-bg-alt);
padding: 3px 8px;
border-radius: var(--wpsp-radius-sm);
font-size: 13px;
}
/* Button spacing & styling */
.wpsp-section .button {
transition: all var(--wpsp-transition);
}
.wpsp-section .button:hover {
transform: translateY(-1px);
}
.wpsp-section .button + .button {
margin-left: 8px;
}
/* Tablenav */
.wpsp-section .tablenav {
margin: 16px 0;
}
.wpsp-section .tablenav .actions {
padding: 0;
}
/* Alert boxes */
.wpsp-alert {
padding: 14px 18px;
border-radius: var(--wpsp-radius);
margin-bottom: 16px;
border-left: 4px solid;
}
.wpsp-alert-info {
background: var(--wpsp-info-bg);
border-left-color: var(--wpsp-info);
color: #0a4b78;
}
.wpsp-alert-warning {
background: var(--wpsp-warning-bg);
border-left-color: var(--wpsp-warning);
color: #6e5600;
}
.wpsp-alert-error {
background: var(--wpsp-danger-bg);
border-left-color: var(--wpsp-danger);
color: #8a1f1f;
}
/* Loading states */
.wpsp-loading {
opacity: 0.5;
pointer-events: none;
}
/* QR code container */
#wpsp-2fa-qr img,
#wpsp-2fa-qr svg {
border: 1px solid var(--wpsp-border);
padding: 12px;
background: var(--wpsp-bg);
border-radius: var(--wpsp-radius);
margin: 12px 0;
}
/* Backup codes display */
#wpsp-backup-codes-display pre,
#wpsp-new-backup-codes {
background: var(--wpsp-bg-alt);
padding: 16px;
border-radius: var(--wpsp-radius-sm);
font-family: 'SF Mono', Monaco, Consolas, monospace;
font-size: 14px;
line-height: 2;
border: 1px solid var(--wpsp-border);
}
/* Scan results */
.wpsp-scan-results {
max-height: 400px;
overflow-y: auto;
}
/* Checkbox styling */
.wpsp-section input[type="checkbox"] {
margin-right: 10px;
width: 18px;
height: 18px;
}
/* Description text */
.wpsp-section .description {
color: var(--wpsp-text-light);
font-style: normal;
margin-top: 6px;
font-size: 13px;
line-height: 1.5;
}
/* Tab intro descriptions */
.wpsp-section p.wpsp-description {
color: var(--wpsp-text-light);
font-style: normal;
font-size: 14px;
margin: -8px 0 20px 0;
line-height: 1.6;
}
/* Import/export section */
#wpsp-import-file {
margin-right: 12px;
}
#wpsp-import-status {
margin-left: 12px;
color: var(--wpsp-success);
font-weight: 500;
}
/* System info table */
.wpsp-section .widefat td:first-child {
font-weight: 600;
width: 200px;
color: var(--wpsp-text);
}
/* Status checklist icons */
.wpsp-section .widefat td span[style*="color: #46b450"],
.wpsp-section .widefat td span[style*="color: #dc3232"] {
font-size: 20px;
line-height: 1;
}
/* Notice improvements within sections */
.wpsp-section .notice {
border-radius: var(--wpsp-radius-sm);
margin: 12px 0;
}
/* Input focus states */
.wpsp-section input[type="text"]:focus,
.wpsp-section input[type="number"]:focus,
.wpsp-section textarea:focus,
.wpsp-section select:focus {
border-color: var(--wpsp-primary);
box-shadow: 0 0 0 1px var(--wpsp-primary);
outline: none;
}
/* Textarea styling */
.wpsp-section textarea {
border-radius: var(--wpsp-radius-sm);
}
/* Select styling */
.wpsp-section select {
border-radius: var(--wpsp-radius-sm);
}
File diff suppressed because it is too large Load Diff
@@ -1,555 +0,0 @@
<?php
/**
* Helper functions for Security Pack.
*
* @package Security_Pack
*/
// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Helper class with utility functions.
*/
class WPSP_Helper {
/**
* Get the real client IP address.
*
* Handles proxies and CDNs like Cloudflare.
*
* @return string|null
*/
public static function get_client_ip() {
$ip = null;
// Priority order for IP detection.
$headers = array(
'HTTP_CF_CONNECTING_IP', // Cloudflare.
'HTTP_X_REAL_IP', // Nginx reverse proxy.
'HTTP_X_FORWARDED_FOR', // Generic proxy.
'REMOTE_ADDR', // Direct connection.
);
foreach ( $headers as $header ) {
if ( ! empty( $_SERVER[ $header ] ) ) {
// X-Forwarded-For can contain multiple IPs, get the first one.
$ip = sanitize_text_field( wp_unslash( $_SERVER[ $header ] ) );
if ( 'HTTP_X_FORWARDED_FOR' === $header && strpos( $ip, ',' ) !== false ) {
$ips = explode( ',', $ip );
$ip = trim( $ips[0] );
}
break;
}
}
// Validate IP.
if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return $ip;
}
return null;
}
/**
* Check if an IP is in a CIDR range.
*
* @param string $ip IP address to check.
* @param string $cidr CIDR notation (e.g., 192.168.1.0/24).
* @return bool
*/
public static function ip_in_cidr( $ip, $cidr ) {
// Handle exact IP match (no CIDR notation).
if ( strpos( $cidr, '/' ) === false ) {
return $ip === $cidr;
}
list( $network, $mask ) = explode( '/', $cidr );
// Detect IP version.
$ip_is_v6 = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
$network_is_v6 = filter_var( $network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
// Both must be same version.
if ( $ip_is_v6 !== $network_is_v6 ) {
return false;
}
if ( $ip_is_v6 ) {
return self::ipv6_in_cidr( $ip, $network, (int) $mask );
}
return self::ipv4_in_cidr( $ip, $network, (int) $mask );
}
/**
* Check if IPv4 is in CIDR range.
*
* @param string $ip IPv4 address.
* @param string $network Network address.
* @param int $mask CIDR mask.
* @return bool
*/
private static function ipv4_in_cidr( $ip, $network, $mask ) {
$ip_long = ip2long( $ip );
$network_long = ip2long( $network );
if ( false === $ip_long || false === $network_long ) {
return false;
}
// Calculate network mask.
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
$network_mask = ~( ( 1 << ( 32 - $mask ) ) - 1 );
return ( $ip_long & $network_mask ) === ( $network_long & $network_mask );
}
/**
* Check if IPv6 is in CIDR range.
*
* @param string $ip IPv6 address.
* @param string $network Network address.
* @param int $mask CIDR mask.
* @return bool
*/
private static function ipv6_in_cidr( $ip, $network, $mask ) {
$ip_bin = inet_pton( $ip );
$network_bin = inet_pton( $network );
if ( false === $ip_bin || false === $network_bin ) {
return false;
}
// Compare full bytes.
$full_bytes = (int) floor( $mask / 8 );
for ( $i = 0; $i < $full_bytes; $i++ ) {
if ( $ip_bin[ $i ] !== $network_bin[ $i ] ) {
return false;
}
}
// Compare remaining bits.
$remaining_bits = $mask % 8;
if ( $remaining_bits > 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', 'security-pack' ),
'AL' => __( 'Albania', 'security-pack' ),
'DZ' => __( 'Algeria', 'security-pack' ),
'AS' => __( 'American Samoa', 'security-pack' ),
'AD' => __( 'Andorra', 'security-pack' ),
'AO' => __( 'Angola', 'security-pack' ),
'AI' => __( 'Anguilla', 'security-pack' ),
'AQ' => __( 'Antarctica', 'security-pack' ),
'AG' => __( 'Antigua and Barbuda', 'security-pack' ),
'AR' => __( 'Argentina', 'security-pack' ),
'AM' => __( 'Armenia', 'security-pack' ),
'AW' => __( 'Aruba', 'security-pack' ),
'AU' => __( 'Australia', 'security-pack' ),
'AT' => __( 'Austria', 'security-pack' ),
'AZ' => __( 'Azerbaijan', 'security-pack' ),
'BS' => __( 'Bahamas', 'security-pack' ),
'BH' => __( 'Bahrain', 'security-pack' ),
'BD' => __( 'Bangladesh', 'security-pack' ),
'BB' => __( 'Barbados', 'security-pack' ),
'BY' => __( 'Belarus', 'security-pack' ),
'BE' => __( 'Belgium', 'security-pack' ),
'BZ' => __( 'Belize', 'security-pack' ),
'BJ' => __( 'Benin', 'security-pack' ),
'BM' => __( 'Bermuda', 'security-pack' ),
'BT' => __( 'Bhutan', 'security-pack' ),
'BO' => __( 'Bolivia', 'security-pack' ),
'BA' => __( 'Bosnia and Herzegovina', 'security-pack' ),
'BW' => __( 'Botswana', 'security-pack' ),
'BR' => __( 'Brazil', 'security-pack' ),
'BN' => __( 'Brunei', 'security-pack' ),
'BG' => __( 'Bulgaria', 'security-pack' ),
'BF' => __( 'Burkina Faso', 'security-pack' ),
'BI' => __( 'Burundi', 'security-pack' ),
'KH' => __( 'Cambodia', 'security-pack' ),
'CM' => __( 'Cameroon', 'security-pack' ),
'CA' => __( 'Canada', 'security-pack' ),
'CV' => __( 'Cape Verde', 'security-pack' ),
'KY' => __( 'Cayman Islands', 'security-pack' ),
'CF' => __( 'Central African Republic', 'security-pack' ),
'TD' => __( 'Chad', 'security-pack' ),
'CL' => __( 'Chile', 'security-pack' ),
'CN' => __( 'China', 'security-pack' ),
'CO' => __( 'Colombia', 'security-pack' ),
'KM' => __( 'Comoros', 'security-pack' ),
'CG' => __( 'Congo', 'security-pack' ),
'CD' => __( 'Congo (DRC)', 'security-pack' ),
'CR' => __( 'Costa Rica', 'security-pack' ),
'CI' => __( 'Ivory Coast', 'security-pack' ),
'HR' => __( 'Croatia', 'security-pack' ),
'CU' => __( 'Cuba', 'security-pack' ),
'CY' => __( 'Cyprus', 'security-pack' ),
'CZ' => __( 'Czech Republic', 'security-pack' ),
'DK' => __( 'Denmark', 'security-pack' ),
'DJ' => __( 'Djibouti', 'security-pack' ),
'DM' => __( 'Dominica', 'security-pack' ),
'DO' => __( 'Dominican Republic', 'security-pack' ),
'EC' => __( 'Ecuador', 'security-pack' ),
'EG' => __( 'Egypt', 'security-pack' ),
'SV' => __( 'El Salvador', 'security-pack' ),
'GQ' => __( 'Equatorial Guinea', 'security-pack' ),
'ER' => __( 'Eritrea', 'security-pack' ),
'EE' => __( 'Estonia', 'security-pack' ),
'ET' => __( 'Ethiopia', 'security-pack' ),
'FJ' => __( 'Fiji', 'security-pack' ),
'FI' => __( 'Finland', 'security-pack' ),
'FR' => __( 'France', 'security-pack' ),
'GA' => __( 'Gabon', 'security-pack' ),
'GM' => __( 'Gambia', 'security-pack' ),
'GE' => __( 'Georgia', 'security-pack' ),
'DE' => __( 'Germany', 'security-pack' ),
'GH' => __( 'Ghana', 'security-pack' ),
'GR' => __( 'Greece', 'security-pack' ),
'GL' => __( 'Greenland', 'security-pack' ),
'GD' => __( 'Grenada', 'security-pack' ),
'GU' => __( 'Guam', 'security-pack' ),
'GT' => __( 'Guatemala', 'security-pack' ),
'GN' => __( 'Guinea', 'security-pack' ),
'GW' => __( 'Guinea-Bissau', 'security-pack' ),
'GY' => __( 'Guyana', 'security-pack' ),
'HT' => __( 'Haiti', 'security-pack' ),
'HN' => __( 'Honduras', 'security-pack' ),
'HK' => __( 'Hong Kong', 'security-pack' ),
'HU' => __( 'Hungary', 'security-pack' ),
'IS' => __( 'Iceland', 'security-pack' ),
'IN' => __( 'India', 'security-pack' ),
'ID' => __( 'Indonesia', 'security-pack' ),
'IR' => __( 'Iran', 'security-pack' ),
'IQ' => __( 'Iraq', 'security-pack' ),
'IE' => __( 'Ireland', 'security-pack' ),
'IL' => __( 'Israel', 'security-pack' ),
'IT' => __( 'Italy', 'security-pack' ),
'JM' => __( 'Jamaica', 'security-pack' ),
'JP' => __( 'Japan', 'security-pack' ),
'JO' => __( 'Jordan', 'security-pack' ),
'KZ' => __( 'Kazakhstan', 'security-pack' ),
'KE' => __( 'Kenya', 'security-pack' ),
'KI' => __( 'Kiribati', 'security-pack' ),
'KP' => __( 'North Korea', 'security-pack' ),
'KR' => __( 'South Korea', 'security-pack' ),
'KW' => __( 'Kuwait', 'security-pack' ),
'KG' => __( 'Kyrgyzstan', 'security-pack' ),
'LA' => __( 'Laos', 'security-pack' ),
'LV' => __( 'Latvia', 'security-pack' ),
'LB' => __( 'Lebanon', 'security-pack' ),
'LS' => __( 'Lesotho', 'security-pack' ),
'LR' => __( 'Liberia', 'security-pack' ),
'LY' => __( 'Libya', 'security-pack' ),
'LI' => __( 'Liechtenstein', 'security-pack' ),
'LT' => __( 'Lithuania', 'security-pack' ),
'LU' => __( 'Luxembourg', 'security-pack' ),
'MO' => __( 'Macao', 'security-pack' ),
'MK' => __( 'North Macedonia', 'security-pack' ),
'MG' => __( 'Madagascar', 'security-pack' ),
'MW' => __( 'Malawi', 'security-pack' ),
'MY' => __( 'Malaysia', 'security-pack' ),
'MV' => __( 'Maldives', 'security-pack' ),
'ML' => __( 'Mali', 'security-pack' ),
'MT' => __( 'Malta', 'security-pack' ),
'MH' => __( 'Marshall Islands', 'security-pack' ),
'MR' => __( 'Mauritania', 'security-pack' ),
'MU' => __( 'Mauritius', 'security-pack' ),
'MX' => __( 'Mexico', 'security-pack' ),
'FM' => __( 'Micronesia', 'security-pack' ),
'MD' => __( 'Moldova', 'security-pack' ),
'MC' => __( 'Monaco', 'security-pack' ),
'MN' => __( 'Mongolia', 'security-pack' ),
'ME' => __( 'Montenegro', 'security-pack' ),
'MA' => __( 'Morocco', 'security-pack' ),
'MZ' => __( 'Mozambique', 'security-pack' ),
'MM' => __( 'Myanmar', 'security-pack' ),
'NA' => __( 'Namibia', 'security-pack' ),
'NR' => __( 'Nauru', 'security-pack' ),
'NP' => __( 'Nepal', 'security-pack' ),
'NL' => __( 'Netherlands', 'security-pack' ),
'NZ' => __( 'New Zealand', 'security-pack' ),
'NI' => __( 'Nicaragua', 'security-pack' ),
'NE' => __( 'Niger', 'security-pack' ),
'NG' => __( 'Nigeria', 'security-pack' ),
'NO' => __( 'Norway', 'security-pack' ),
'OM' => __( 'Oman', 'security-pack' ),
'PK' => __( 'Pakistan', 'security-pack' ),
'PW' => __( 'Palau', 'security-pack' ),
'PS' => __( 'Palestine', 'security-pack' ),
'PA' => __( 'Panama', 'security-pack' ),
'PG' => __( 'Papua New Guinea', 'security-pack' ),
'PY' => __( 'Paraguay', 'security-pack' ),
'PE' => __( 'Peru', 'security-pack' ),
'PH' => __( 'Philippines', 'security-pack' ),
'PL' => __( 'Poland', 'security-pack' ),
'PT' => __( 'Portugal', 'security-pack' ),
'PR' => __( 'Puerto Rico', 'security-pack' ),
'QA' => __( 'Qatar', 'security-pack' ),
'RO' => __( 'Romania', 'security-pack' ),
'RU' => __( 'Russia', 'security-pack' ),
'RW' => __( 'Rwanda', 'security-pack' ),
'SA' => __( 'Saudi Arabia', 'security-pack' ),
'SN' => __( 'Senegal', 'security-pack' ),
'RS' => __( 'Serbia', 'security-pack' ),
'SC' => __( 'Seychelles', 'security-pack' ),
'SL' => __( 'Sierra Leone', 'security-pack' ),
'SG' => __( 'Singapore', 'security-pack' ),
'SK' => __( 'Slovakia', 'security-pack' ),
'SI' => __( 'Slovenia', 'security-pack' ),
'SB' => __( 'Solomon Islands', 'security-pack' ),
'SO' => __( 'Somalia', 'security-pack' ),
'ZA' => __( 'South Africa', 'security-pack' ),
'SS' => __( 'South Sudan', 'security-pack' ),
'ES' => __( 'Spain', 'security-pack' ),
'LK' => __( 'Sri Lanka', 'security-pack' ),
'SD' => __( 'Sudan', 'security-pack' ),
'SR' => __( 'Suriname', 'security-pack' ),
'SZ' => __( 'Eswatini', 'security-pack' ),
'SE' => __( 'Sweden', 'security-pack' ),
'CH' => __( 'Switzerland', 'security-pack' ),
'SY' => __( 'Syria', 'security-pack' ),
'TW' => __( 'Taiwan', 'security-pack' ),
'TJ' => __( 'Tajikistan', 'security-pack' ),
'TZ' => __( 'Tanzania', 'security-pack' ),
'TH' => __( 'Thailand', 'security-pack' ),
'TL' => __( 'Timor-Leste', 'security-pack' ),
'TG' => __( 'Togo', 'security-pack' ),
'TO' => __( 'Tonga', 'security-pack' ),
'TT' => __( 'Trinidad and Tobago', 'security-pack' ),
'TN' => __( 'Tunisia', 'security-pack' ),
'TR' => __( 'Turkey', 'security-pack' ),
'TM' => __( 'Turkmenistan', 'security-pack' ),
'TV' => __( 'Tuvalu', 'security-pack' ),
'UG' => __( 'Uganda', 'security-pack' ),
'UA' => __( 'Ukraine', 'security-pack' ),
'AE' => __( 'United Arab Emirates', 'security-pack' ),
'GB' => __( 'United Kingdom', 'security-pack' ),
'US' => __( 'United States', 'security-pack' ),
'UY' => __( 'Uruguay', 'security-pack' ),
'UZ' => __( 'Uzbekistan', 'security-pack' ),
'VU' => __( 'Vanuatu', 'security-pack' ),
'VE' => __( 'Venezuela', 'security-pack' ),
'VN' => __( 'Vietnam', 'security-pack' ),
'YE' => __( 'Yemen', 'security-pack' ),
'ZM' => __( 'Zambia', 'security-pack' ),
'ZW' => __( 'Zimbabwe', '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( 'wpsp_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( 'wpsp_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;
}
}
-6
View File
@@ -1,6 +0,0 @@
<?php
/**
* Silence is golden.
*
* @package Security_Pack
*/
-6
View File
@@ -1,6 +0,0 @@
<?php
/**
* Silence is golden.
*
* @package Security_Pack
*/
-33
View File
@@ -1,33 +0,0 @@
<?php
/**
* Security Pack Uninstaller.
*
* @package Security_Pack
*/
// If uninstall not called from WordPress, exit.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
// Load the database class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-wpsp-db.php';
// Run uninstall.
WPSP_DB::uninstall();
// Clear any remaining transients.
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup during uninstall, caching not applicable.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_wpsp_%'" );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup during uninstall, caching not applicable.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_wpsp_%'" );
// Clear scheduled events.
wp_clear_scheduled_hook( 'wpsp_daily_cleanup' );
wp_clear_scheduled_hook( 'wpsp_daily_file_scan' );
wp_clear_scheduled_hook( 'wpsp_weekly_malware_scan' );
// Delete user meta for 2FA.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup during uninstall, caching not applicable.
$wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE '_wpsp_%'" );