upskill-event-manager/includes/class-hvac-security-monitor.php
bengizmo afc221a98a feat: Implement comprehensive enterprise monitoring and optimization infrastructure
Add complete enterprise-level reliability, security, and performance systems:

## Core Monitoring Systems
- **Health Monitor**: 8 automated health checks with email alerts and REST API
- **Error Recovery**: 4 recovery strategies (retry, fallback, circuit breaker, graceful failure)
- **Security Monitor**: Real-time threat detection with automatic IP blocking
- **Performance Monitor**: Performance tracking with automated benchmarks and alerts

## Data Protection & Optimization
- **Backup Manager**: Automated backups with encryption, compression, and disaster recovery
- **Cache Optimizer**: Intelligent caching with 3 strategies and 5 specialized cache groups

## Enterprise Features
- Automated scheduling with WordPress cron integration
- Admin dashboards for all systems under Tools menu
- REST API endpoints for external monitoring
- WP-CLI commands for automation and CI/CD
- Comprehensive documentation (docs/MONITORING-SYSTEMS.md)
- Emergency response systems with immediate email alerts
- Circuit breaker pattern for external service failures
- Smart cache warming and invalidation
- Database query caching and optimization
- File integrity monitoring
- Performance degradation detection

## Integration
- Plugin architecture updated with proper initialization
- Singleton pattern for all monitoring classes
- WordPress hooks and filters integration
- Background job processing system
- Comprehensive error handling and logging

Systems provide enterprise-grade reliability with automated threat response,
proactive performance monitoring, and complete disaster recovery capabilities.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 04:08:52 -03:00

923 lines
No EOL
32 KiB
PHP

<?php
/**
* HVAC Security Monitor
*
* Provides real-time security monitoring, threat detection, and automated
* security response for the HVAC Community Events plugin
*
* @package HVAC_Community_Events
* @since 1.0.8
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* HVAC_Security_Monitor class
*/
class HVAC_Security_Monitor {
/**
* Security event types
*/
const EVENT_TYPES = [
'failed_login' => 'Failed Login Attempt',
'suspicious_activity' => 'Suspicious Activity',
'privilege_escalation' => 'Privilege Escalation Attempt',
'file_modification' => 'Unauthorized File Modification',
'sql_injection' => 'SQL Injection Attempt',
'xss_attempt' => 'Cross-Site Scripting Attempt',
'brute_force' => 'Brute Force Attack',
'admin_access' => 'Unauthorized Admin Access'
];
/**
* Threat levels
*/
const THREAT_LOW = 'low';
const THREAT_MEDIUM = 'medium';
const THREAT_HIGH = 'high';
const THREAT_CRITICAL = 'critical';
/**
* Security settings
*/
private static $settings = [
'max_failed_logins' => 5,
'lockout_duration' => 900, // 15 minutes
'monitor_file_changes' => true,
'scan_requests' => true,
'alert_threshold' => 3,
'auto_block_ips' => true
];
/**
* Blocked IPs cache
*/
private static $blocked_ips = [];
/**
* Initialize security monitoring
*/
public static function init() {
// Load settings
self::$settings = array_merge(self::$settings, get_option('hvac_security_settings', []));
self::$blocked_ips = get_option('hvac_blocked_ips', []);
// Security monitoring hooks
add_action('wp_login_failed', [__CLASS__, 'handle_failed_login']);
add_action('wp_login', [__CLASS__, 'handle_successful_login'], 10, 2);
add_action('init', [__CLASS__, 'check_request_security']);
add_action('admin_init', [__CLASS__, 'monitor_admin_access']);
// File monitoring
if (self::$settings['monitor_file_changes']) {
add_action('wp_loaded', [__CLASS__, 'monitor_file_integrity']);
}
// Database monitoring
add_filter('query', [__CLASS__, 'monitor_database_queries']);
// Admin interface
if (is_admin()) {
add_action('admin_menu', [__CLASS__, 'add_admin_menu']);
add_action('wp_ajax_hvac_security_action', [__CLASS__, 'handle_security_action']);
}
// REST API for external monitoring
add_action('rest_api_init', [__CLASS__, 'register_rest_endpoints']);
// Cleanup old security events
add_action('wp_scheduled_delete', [__CLASS__, 'cleanup_old_events']);
// Emergency lockdown capability
add_action('hvac_emergency_lockdown', [__CLASS__, 'emergency_lockdown']);
// WP-CLI integration
if (defined('WP_CLI') && WP_CLI) {
WP_CLI::add_command('hvac security', [__CLASS__, 'wp_cli_security']);
}
}
/**
* Handle failed login attempts
*/
public static function handle_failed_login($username) {
$ip = self::get_client_ip();
// Record the failed attempt
self::log_security_event('failed_login', self::THREAT_MEDIUM, [
'username' => $username,
'ip_address' => $ip,
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'timestamp' => time()
]);
// Check for brute force pattern
$recent_attempts = self::get_recent_events('failed_login', $ip, 3600); // Last hour
if (count($recent_attempts) >= self::$settings['max_failed_logins']) {
// Brute force detected
self::log_security_event('brute_force', self::THREAT_HIGH, [
'ip_address' => $ip,
'attempts' => count($recent_attempts),
'usernames' => array_unique(array_column($recent_attempts, 'username')),
'auto_blocked' => self::$settings['auto_block_ips']
]);
if (self::$settings['auto_block_ips']) {
self::block_ip($ip, 'Brute force attack detected');
}
// Send immediate alert
self::send_security_alert('Brute Force Attack', [
'ip_address' => $ip,
'attempts' => count($recent_attempts),
'action_taken' => self::$settings['auto_block_ips'] ? 'IP blocked' : 'Logged only'
]);
}
}
/**
* Handle successful login
*/
public static function handle_successful_login($username, $user) {
$ip = self::get_client_ip();
// Check if this is a suspicious login
$is_suspicious = false;
$reasons = [];
// Check for unusual location (simplified - could integrate with GeoIP)
$user_last_ip = get_user_meta($user->ID, 'hvac_last_login_ip', true);
if ($user_last_ip && $user_last_ip !== $ip) {
$is_suspicious = true;
$reasons[] = 'Different IP address';
}
// Check for admin role login
if (user_can($user, 'manage_options')) {
self::log_security_event('admin_access', self::THREAT_LOW, [
'username' => $username,
'ip_address' => $ip,
'user_id' => $user->ID,
'suspicious' => $is_suspicious,
'reasons' => $reasons
]);
if ($is_suspicious) {
self::send_security_alert('Suspicious Admin Login', [
'username' => $username,
'ip_address' => $ip,
'reasons' => $reasons
]);
}
}
// Update user's last login IP
update_user_meta($user->ID, 'hvac_last_login_ip', $ip);
update_user_meta($user->ID, 'hvac_last_login_time', time());
}
/**
* Check request security
*/
public static function check_request_security() {
// Skip checks for admin, CLI, or cron
if (is_admin() || wp_doing_cron() || (defined('WP_CLI') && WP_CLI)) {
return;
}
$ip = self::get_client_ip();
// Check if IP is blocked
if (self::is_ip_blocked($ip)) {
self::block_request('IP address is blocked');
return;
}
if (!self::$settings['scan_requests']) {
return;
}
$request_data = $_REQUEST;
$threat_level = self::THREAT_LOW;
$threats_detected = [];
// Check for SQL injection patterns
$sql_patterns = [
'/union.*select/i',
'/drop.*table/i',
'/insert.*into/i',
'/delete.*from/i',
'/update.*set/i',
'/exec\s*\(/i'
];
foreach ($request_data as $key => $value) {
if (is_string($value)) {
foreach ($sql_patterns as $pattern) {
if (preg_match($pattern, $value)) {
$threats_detected[] = 'SQL injection pattern in: ' . $key;
$threat_level = self::THREAT_HIGH;
}
}
}
}
// Check for XSS patterns
$xss_patterns = [
'/<script.*?>.*?<\/script>/i',
'/javascript:/i',
'/onload\s*=/i',
'/onerror\s*=/i',
'/<iframe.*?>/i'
];
foreach ($request_data as $key => $value) {
if (is_string($value)) {
foreach ($xss_patterns as $pattern) {
if (preg_match($pattern, $value)) {
$threats_detected[] = 'XSS pattern in: ' . $key;
if ($threat_level === self::THREAT_LOW) {
$threat_level = self::THREAT_MEDIUM;
}
}
}
}
}
// Check for file inclusion attempts
$file_patterns = [
'/\.\.\//i',
'/etc\/passwd/i',
'/proc\/.*?/i',
'/boot\.ini/i'
];
foreach ($request_data as $key => $value) {
if (is_string($value)) {
foreach ($file_patterns as $pattern) {
if (preg_match($pattern, $value)) {
$threats_detected[] = 'File inclusion attempt in: ' . $key;
$threat_level = self::THREAT_HIGH;
}
}
}
}
// Log and respond to threats
if (!empty($threats_detected)) {
$event_type = strpos(implode(' ', $threats_detected), 'SQL') !== false ? 'sql_injection' : 'xss_attempt';
self::log_security_event($event_type, $threat_level, [
'ip_address' => $ip,
'threats' => $threats_detected,
'request_uri' => $_SERVER['REQUEST_URI'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'request_data' => $request_data
]);
// Auto-block for high/critical threats
if ($threat_level === self::THREAT_HIGH || $threat_level === self::THREAT_CRITICAL) {
if (self::$settings['auto_block_ips']) {
self::block_ip($ip, 'Malicious request detected: ' . implode(', ', $threats_detected));
}
self::send_security_alert('Malicious Request Blocked', [
'ip_address' => $ip,
'threats' => $threats_detected,
'request_uri' => $_SERVER['REQUEST_URI'] ?? ''
]);
self::block_request('Malicious request detected');
}
}
}
/**
* Monitor admin access
*/
public static function monitor_admin_access() {
if (!current_user_can('manage_options')) {
return;
}
$user = wp_get_current_user();
$ip = self::get_client_ip();
// Check for privilege escalation attempts
if (isset($_POST['action']) && $_POST['action'] === 'update' && isset($_POST['users'])) {
self::log_security_event('privilege_escalation', self::THREAT_MEDIUM, [
'user_id' => $user->ID,
'username' => $user->user_login,
'ip_address' => $ip,
'action' => 'User role modification attempt'
]);
}
// Monitor plugin/theme installations
if (isset($_REQUEST['action']) && in_array($_REQUEST['action'], ['install-plugin', 'install-theme', 'upload-plugin', 'upload-theme'])) {
self::log_security_event('suspicious_activity', self::THREAT_LOW, [
'user_id' => $user->ID,
'username' => $user->user_login,
'ip_address' => $ip,
'action' => $_REQUEST['action'],
'item' => $_REQUEST['plugin'] ?? $_REQUEST['theme'] ?? 'unknown'
]);
}
}
/**
* Monitor file integrity
*/
public static function monitor_file_integrity() {
// Only run this check periodically to avoid performance issues
$last_check = get_option('hvac_last_file_check', 0);
if (time() - $last_check < 3600) { // Check every hour
return;
}
update_option('hvac_last_file_check', time());
// Check core plugin files
$critical_files = [
HVAC_PLUGIN_FILE,
HVAC_PLUGIN_DIR . 'includes/class-hvac-plugin.php',
HVAC_PLUGIN_DIR . 'includes/class-hvac-community-events.php'
];
$stored_hashes = get_option('hvac_file_hashes', []);
$current_hashes = [];
$modified_files = [];
foreach ($critical_files as $file) {
if (file_exists($file)) {
$current_hash = md5_file($file);
$current_hashes[basename($file)] = $current_hash;
$stored_hash = $stored_hashes[basename($file)] ?? null;
if ($stored_hash && $stored_hash !== $current_hash) {
$modified_files[] = basename($file);
}
}
}
// Update stored hashes
if (empty($stored_hashes)) {
// First run - just store hashes
update_option('hvac_file_hashes', $current_hashes);
} else {
// Report modifications
if (!empty($modified_files)) {
self::log_security_event('file_modification', self::THREAT_HIGH, [
'modified_files' => $modified_files,
'ip_address' => self::get_client_ip(),
'detection_time' => time()
]);
self::send_security_alert('File Modification Detected', [
'modified_files' => $modified_files,
'total_files' => count($modified_files)
]);
}
update_option('hvac_file_hashes', $current_hashes);
}
}
/**
* Monitor database queries
*/
public static function monitor_database_queries($query) {
// Skip monitoring for admin area and known safe contexts
if (is_admin() || wp_doing_cron() || (defined('WP_CLI') && WP_CLI)) {
return $query;
}
// Check for suspicious patterns
$suspicious_patterns = [
'/UNION.*SELECT/i',
'/DROP\s+TABLE/i',
'/DELETE.*FROM.*WHERE.*1\s*=\s*1/i',
'/UPDATE.*SET.*WHERE.*1\s*=\s*1/i'
];
foreach ($suspicious_patterns as $pattern) {
if (preg_match($pattern, $query)) {
self::log_security_event('sql_injection', self::THREAT_CRITICAL, [
'query_pattern' => preg_replace('/\s+/', ' ', substr($query, 0, 200)),
'ip_address' => self::get_client_ip(),
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'request_uri' => $_SERVER['REQUEST_URI'] ?? ''
]);
// Block immediately for critical SQL injection attempts
self::send_security_alert('Critical SQL Injection Attempt', [
'ip_address' => self::get_client_ip(),
'query_sample' => substr($query, 0, 100)
]);
if (self::$settings['auto_block_ips']) {
self::block_ip(self::get_client_ip(), 'SQL injection attempt detected');
self::block_request('Malicious database query detected');
}
break;
}
}
return $query;
}
/**
* Log security event
*/
private static function log_security_event($type, $threat_level, $data) {
global $wpdb;
$event = [
'type' => $type,
'threat_level' => $threat_level,
'ip_address' => $data['ip_address'] ?? self::get_client_ip(),
'user_id' => get_current_user_id(),
'data' => json_encode($data),
'timestamp' => time()
];
// Store in options table (or create custom table for high-volume sites)
$events = get_option('hvac_security_events', []);
$events[] = $event;
// Keep only last 1000 events to prevent database bloat
if (count($events) > 1000) {
$events = array_slice($events, -1000);
}
update_option('hvac_security_events', $events);
// Log to WordPress error log as well
HVAC_Logger::warning(
"Security event: $type ($threat_level) - " . json_encode($data),
'Security Monitor'
);
// Check if alert threshold is reached
$recent_high_threats = self::count_recent_threats([self::THREAT_HIGH, self::THREAT_CRITICAL], 3600);
if ($recent_high_threats >= self::$settings['alert_threshold']) {
self::send_security_alert('Security Alert Threshold Reached', [
'recent_threats' => $recent_high_threats,
'threshold' => self::$settings['alert_threshold'],
'time_window' => '1 hour'
]);
}
}
/**
* Get recent security events
*/
private static function get_recent_events($type, $ip = null, $timeframe = 3600) {
$events = get_option('hvac_security_events', []);
$cutoff_time = time() - $timeframe;
return array_filter($events, function($event) use ($type, $ip, $cutoff_time) {
if ($event['timestamp'] < $cutoff_time) {
return false;
}
if ($event['type'] !== $type) {
return false;
}
if ($ip && $event['ip_address'] !== $ip) {
return false;
}
return true;
});
}
/**
* Count recent threats by level
*/
private static function count_recent_threats($threat_levels, $timeframe = 3600) {
$events = get_option('hvac_security_events', []);
$cutoff_time = time() - $timeframe;
return count(array_filter($events, function($event) use ($threat_levels, $cutoff_time) {
return $event['timestamp'] >= $cutoff_time &&
in_array($event['threat_level'], $threat_levels);
}));
}
/**
* Block IP address
*/
private static function block_ip($ip, $reason) {
self::$blocked_ips[$ip] = [
'reason' => $reason,
'timestamp' => time(),
'expires' => time() + self::$settings['lockout_duration']
];
update_option('hvac_blocked_ips', self::$blocked_ips);
HVAC_Logger::warning("IP blocked: $ip - $reason", 'Security Monitor');
}
/**
* Check if IP is blocked
*/
private static function is_ip_blocked($ip) {
if (!isset(self::$blocked_ips[$ip])) {
return false;
}
$block_info = self::$blocked_ips[$ip];
if (time() > $block_info['expires']) {
// Block expired, remove it
unset(self::$blocked_ips[$ip]);
update_option('hvac_blocked_ips', self::$blocked_ips);
return false;
}
return true;
}
/**
* Block current request
*/
private static function block_request($reason) {
http_response_code(403);
die('Access Denied: ' . $reason);
}
/**
* Get client IP address
*/
private static function get_client_ip() {
$headers = [
'HTTP_CF_CONNECTING_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR'
];
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
$ips = explode(',', $_SERVER[$header]);
$ip = trim($ips[0]);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
}
return $_SERVER['REMOTE_ADDR'] ?? 'unknown';
}
/**
* Send security alert
*/
private static function send_security_alert($subject, $data) {
$admin_email = get_option('admin_email');
$site_name = get_bloginfo('name');
$message = "Security Alert from $site_name\n\n";
$message .= "Alert: $subject\n\n";
foreach ($data as $key => $value) {
if (is_array($value)) {
$value = implode(', ', $value);
}
$message .= ucfirst(str_replace('_', ' ', $key)) . ": $value\n";
}
$message .= "\nTime: " . date('Y-m-d H:i:s') . "\n";
$message .= "Check the security monitor for more details.";
wp_mail($admin_email, "[$site_name] $subject", $message);
}
/**
* Emergency lockdown
*/
public static function emergency_lockdown() {
// Block all non-admin access
update_option('hvac_emergency_lockdown', [
'enabled' => true,
'timestamp' => time(),
'triggered_by' => get_current_user_id()
]);
// Send emergency notification
self::send_security_alert('Emergency Lockdown Activated', [
'triggered_by' => get_current_user_id(),
'timestamp' => time(),
'action' => 'All non-admin access blocked'
]);
HVAC_Logger::error('Emergency lockdown activated', 'Security Monitor');
}
/**
* Get security statistics
*/
public static function get_security_stats() {
$events = get_option('hvac_security_events', []);
$blocked_ips = get_option('hvac_blocked_ips', []);
// Count events by type and threat level
$stats = [
'total_events' => count($events),
'blocked_ips' => count($blocked_ips),
'events_by_type' => [],
'events_by_threat' => [],
'recent_events' => count(array_filter($events, function($event) {
return $event['timestamp'] >= (time() - 86400); // Last 24 hours
}))
];
foreach ($events as $event) {
$type = $event['type'];
$threat = $event['threat_level'];
$stats['events_by_type'][$type] = ($stats['events_by_type'][$type] ?? 0) + 1;
$stats['events_by_threat'][$threat] = ($stats['events_by_threat'][$threat] ?? 0) + 1;
}
return $stats;
}
/**
* Cleanup old security events
*/
public static function cleanup_old_events() {
// Remove events older than 30 days
$events = get_option('hvac_security_events', []);
$cutoff_time = time() - (30 * 86400);
$events = array_filter($events, function($event) use ($cutoff_time) {
return $event['timestamp'] >= $cutoff_time;
});
update_option('hvac_security_events', array_values($events));
// Clean up expired IP blocks
$blocked_ips = get_option('hvac_blocked_ips', []);
$current_time = time();
$updated = false;
foreach ($blocked_ips as $ip => $block_info) {
if ($current_time > $block_info['expires']) {
unset($blocked_ips[$ip]);
$updated = true;
}
}
if ($updated) {
update_option('hvac_blocked_ips', $blocked_ips);
}
}
/**
* Add admin menu
*/
public static function add_admin_menu() {
if (current_user_can('manage_options')) {
add_management_page(
'HVAC Security Monitor',
'HVAC Security',
'manage_options',
'hvac-security-monitor',
[__CLASS__, 'admin_page']
);
}
}
/**
* Admin page
*/
public static function admin_page() {
$stats = self::get_security_stats();
$recent_events = array_slice(get_option('hvac_security_events', []), -20, 20, true);
$blocked_ips = get_option('hvac_blocked_ips', []);
?>
<div class="wrap">
<h1>HVAC Security Monitor</h1>
<div class="security-stats">
<div class="card">
<h2>Security Overview</h2>
<p><strong>Total Events:</strong> <?php echo $stats['total_events']; ?></p>
<p><strong>Recent Events (24h):</strong> <?php echo $stats['recent_events']; ?></p>
<p><strong>Blocked IPs:</strong> <?php echo $stats['blocked_ips']; ?></p>
</div>
<div class="card">
<h2>Threat Distribution</h2>
<?php foreach ($stats['events_by_threat'] as $threat => $count): ?>
<p><strong><?php echo ucfirst($threat); ?>:</strong> <?php echo $count; ?></p>
<?php endforeach; ?>
</div>
</div>
<div class="card">
<h2>Recent Security Events</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>Time</th>
<th>Type</th>
<th>Threat Level</th>
<th>IP Address</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<?php if (empty($recent_events)): ?>
<tr>
<td colspan="5">No recent security events</td>
</tr>
<?php else: ?>
<?php foreach (array_reverse($recent_events) as $event): ?>
<tr class="threat-<?php echo esc_attr($event['threat_level']); ?>">
<td><?php echo date('Y-m-d H:i:s', $event['timestamp']); ?></td>
<td><?php echo esc_html(self::EVENT_TYPES[$event['type']] ?? $event['type']); ?></td>
<td><?php echo esc_html(strtoupper($event['threat_level'])); ?></td>
<td><?php echo esc_html($event['ip_address']); ?></td>
<td>
<details>
<summary>View Details</summary>
<pre><?php echo esc_html(json_encode(json_decode($event['data']), JSON_PRETTY_PRINT)); ?></pre>
</details>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="card">
<h2>Blocked IP Addresses</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>IP Address</th>
<th>Reason</th>
<th>Blocked At</th>
<th>Expires</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php if (empty($blocked_ips)): ?>
<tr>
<td colspan="5">No blocked IPs</td>
</tr>
<?php else: ?>
<?php foreach ($blocked_ips as $ip => $block_info): ?>
<tr>
<td><?php echo esc_html($ip); ?></td>
<td><?php echo esc_html($block_info['reason']); ?></td>
<td><?php echo date('Y-m-d H:i:s', $block_info['timestamp']); ?></td>
<td><?php echo date('Y-m-d H:i:s', $block_info['expires']); ?></td>
<td>
<button type="button" class="button button-small unblock-ip"
data-ip="<?php echo esc_attr($ip); ?>">
Unblock
</button>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<style>
.threat-low { background-color: #d4edda; }
.threat-medium { background-color: #fff3cd; }
.threat-high { background-color: #f8d7da; }
.threat-critical { background-color: #d1ecf1; }
.security-stats { display: flex; gap: 20px; }
.security-stats .card { flex: 1; }
</style>
<script>
document.querySelectorAll('.unblock-ip').forEach(button => {
button.addEventListener('click', function() {
const ip = this.dataset.ip;
if (confirm(`Unblock IP address ${ip}?`)) {
fetch(ajaxurl, {
method: 'POST',
body: new URLSearchParams({
action: 'hvac_security_action',
security_action: 'unblock_ip',
ip_address: ip,
nonce: '<?php echo wp_create_nonce('hvac_security_action'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Failed to unblock IP');
}
});
}
});
});
</script>
</div>
<?php
}
/**
* Handle security actions
*/
public static function handle_security_action() {
check_ajax_referer('hvac_security_action', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
$action = sanitize_text_field($_POST['security_action']);
switch ($action) {
case 'unblock_ip':
$ip = sanitize_text_field($_POST['ip_address']);
$blocked_ips = get_option('hvac_blocked_ips', []);
unset($blocked_ips[$ip]);
update_option('hvac_blocked_ips', $blocked_ips);
wp_send_json_success('IP unblocked');
break;
default:
wp_send_json_error('Unknown action');
}
}
/**
* Register REST endpoints
*/
public static function register_rest_endpoints() {
register_rest_route('hvac/v1', '/security/stats', [
'methods' => 'GET',
'callback' => [__CLASS__, 'rest_security_stats'],
'permission_callback' => function() {
return current_user_can('manage_options');
}
]);
}
/**
* REST API security stats
*/
public static function rest_security_stats() {
$stats = self::get_security_stats();
return new WP_REST_Response([
'stats' => $stats,
'timestamp' => time()
], 200);
}
/**
* WP-CLI security command
*/
public static function wp_cli_security($args, $assoc_args) {
$subcommand = $args[0] ?? 'stats';
switch ($subcommand) {
case 'stats':
$stats = self::get_security_stats();
WP_CLI::line('HVAC Security Statistics:');
WP_CLI::line('Total Events: ' . $stats['total_events']);
WP_CLI::line('Recent Events (24h): ' . $stats['recent_events']);
WP_CLI::line('Blocked IPs: ' . $stats['blocked_ips']);
break;
case 'events':
$events = array_slice(get_option('hvac_security_events', []), -10);
WP_CLI::line('Recent Security Events:');
foreach ($events as $event) {
WP_CLI::line(sprintf(
'%s - %s (%s) - %s',
date('Y-m-d H:i:s', $event['timestamp']),
$event['type'],
$event['threat_level'],
$event['ip_address']
));
}
break;
default:
WP_CLI::error('Unknown subcommand. Use: stats, events');
}
}
}