- Add .distignore (operator-private files excluded) - Add build.sh for WordPress-installable release ZIPs - Update CLAUDE.md references (now operator-private only)
644 lines
23 KiB
PHP
644 lines
23 KiB
PHP
<?php
|
|
/**
|
|
* TigerStyle Scent Security Logging System
|
|
* Comprehensive security event logging and monitoring for threat detection
|
|
*
|
|
* @package TigerStyle Scent
|
|
*/
|
|
|
|
defined('ABSPATH') or die('Direct access forbidden.');
|
|
|
|
class TigerStyleScent_SecurityLogger {
|
|
|
|
/**
|
|
* Security event severity levels
|
|
*/
|
|
const SEVERITY_LOW = 1;
|
|
const SEVERITY_MEDIUM = 2;
|
|
const SEVERITY_HIGH = 3;
|
|
const SEVERITY_CRITICAL = 4;
|
|
|
|
/**
|
|
* Security event types
|
|
*/
|
|
const EVENT_AUTH_FAILURE = 'auth_failure';
|
|
const EVENT_AUTH_SUCCESS = 'auth_success';
|
|
const EVENT_RATE_LIMIT = 'rate_limit_exceeded';
|
|
const EVENT_VALIDATION_FAILURE = 'validation_failure';
|
|
const EVENT_INJECTION_ATTEMPT = 'injection_attempt';
|
|
const EVENT_SUSPICIOUS_REQUEST = 'suspicious_request';
|
|
const EVENT_TOKEN_ISSUED = 'token_issued';
|
|
const EVENT_TOKEN_REVOKED = 'token_revoked';
|
|
const EVENT_CLIENT_CREATED = 'client_created';
|
|
const EVENT_SECURITY_VIOLATION = 'security_violation';
|
|
|
|
/**
|
|
* 🔐 SECURITY: Log comprehensive security event
|
|
*
|
|
* @param string $event_type Type of security event
|
|
* @param int $severity Severity level (1-4)
|
|
* @param string $message Human-readable message
|
|
* @param array $context Additional context data
|
|
* @param string $client_id OAuth2 client ID if applicable
|
|
* @param int $user_id WordPress user ID if applicable
|
|
*/
|
|
public static function log_security_event(
|
|
string $event_type,
|
|
int $severity,
|
|
string $message,
|
|
array $context = [],
|
|
string $client_id = null,
|
|
int $user_id = null
|
|
): void {
|
|
|
|
// Prepare comprehensive event data
|
|
$event_data = [
|
|
'timestamp' => current_time('mysql'),
|
|
'timestamp_utc' => gmdate('Y-m-d H:i:s'),
|
|
'event_type' => $event_type,
|
|
'severity' => $severity,
|
|
'severity_name' => self::get_severity_name($severity),
|
|
'message' => $message,
|
|
'context' => $context,
|
|
'client_id' => $client_id,
|
|
'user_id' => $user_id,
|
|
'session_id' => self::get_session_id(),
|
|
'request_data' => self::get_sanitized_request_data(),
|
|
'client_info' => self::get_client_info(),
|
|
'wordpress_info' => self::get_wordpress_info(),
|
|
];
|
|
|
|
// Store in database for structured querying
|
|
self::store_security_event($event_data);
|
|
|
|
// Log to WordPress error log for immediate visibility
|
|
self::log_to_wordpress($event_data);
|
|
|
|
// Send real-time alerts for critical events
|
|
if ($severity >= self::SEVERITY_HIGH) {
|
|
self::send_security_alert($event_data);
|
|
}
|
|
|
|
// Trigger WordPress action for external integrations
|
|
do_action('tigerstyle_scent_security_event', $event_data);
|
|
|
|
// Check for attack patterns and auto-block threats
|
|
self::analyze_attack_patterns($event_data);
|
|
|
|
// Real-time threat analysis
|
|
self::perform_threat_analysis($event_data);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Store security event in database
|
|
*/
|
|
private static function store_security_event(array $event_data): void {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_scent_security_log';
|
|
|
|
// Create table if it doesn't exist
|
|
self::ensure_security_log_table();
|
|
|
|
$wpdb->insert(
|
|
$table_name,
|
|
[
|
|
'timestamp' => $event_data['timestamp'],
|
|
'event_type' => $event_data['event_type'],
|
|
'severity' => $event_data['severity'],
|
|
'message' => $event_data['message'],
|
|
'context_data' => json_encode($event_data['context']),
|
|
'client_id' => $event_data['client_id'],
|
|
'user_id' => $event_data['user_id'],
|
|
'session_id' => $event_data['session_id'],
|
|
'client_ip' => $event_data['client_info']['ip'],
|
|
'user_agent' => substr($event_data['client_info']['user_agent'], 0, 255),
|
|
'request_uri' => $event_data['request_data']['uri'],
|
|
'request_method' => $event_data['request_data']['method'],
|
|
'full_event_data' => json_encode($event_data)
|
|
],
|
|
[
|
|
'%s', '%s', '%d', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s'
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Ensure security log table exists
|
|
*/
|
|
private static function ensure_security_log_table(): void {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_scent_security_log';
|
|
|
|
$charset_collate = $wpdb->get_charset_collate();
|
|
|
|
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
|
|
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
timestamp datetime NOT NULL,
|
|
event_type varchar(50) NOT NULL,
|
|
severity tinyint(1) NOT NULL,
|
|
message text NOT NULL,
|
|
context_data longtext,
|
|
client_id varchar(255),
|
|
user_id bigint(20) unsigned,
|
|
session_id varchar(64),
|
|
client_ip varchar(45) NOT NULL,
|
|
user_agent varchar(255),
|
|
request_uri varchar(255),
|
|
request_method varchar(10),
|
|
full_event_data longtext,
|
|
created_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (id),
|
|
KEY event_type (event_type),
|
|
KEY severity (severity),
|
|
KEY timestamp (timestamp),
|
|
KEY client_id (client_id),
|
|
KEY user_id (user_id),
|
|
KEY client_ip (client_ip)
|
|
) $charset_collate;";
|
|
|
|
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
|
dbDelta($sql);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Log to WordPress error log
|
|
*/
|
|
private static function log_to_wordpress(array $event_data): void {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
$log_message = sprintf(
|
|
'[TigerStyle Scent Security] %s [%s] %s - IP: %s, UA: %s',
|
|
$event_data['severity_name'],
|
|
$event_data['event_type'],
|
|
$event_data['message'],
|
|
$event_data['client_info']['ip'],
|
|
substr($event_data['client_info']['user_agent'], 0, 50)
|
|
);
|
|
|
|
error_log($log_message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Send real-time security alerts
|
|
*/
|
|
private static function send_security_alert(array $event_data): void {
|
|
// Email alerts for critical security events
|
|
if ($event_data['severity'] >= self::SEVERITY_CRITICAL) {
|
|
$admin_email = get_option('admin_email');
|
|
$site_name = get_bloginfo('name');
|
|
|
|
$subject = "[CRITICAL] Security Alert - {$site_name}";
|
|
$message = "Critical security event detected:\n\n";
|
|
$message .= "Event: {$event_data['event_type']}\n";
|
|
$message .= "Message: {$event_data['message']}\n";
|
|
$message .= "Time: {$event_data['timestamp']}\n";
|
|
$message .= "IP: {$event_data['client_info']['ip']}\n";
|
|
$message .= "User Agent: {$event_data['client_info']['user_agent']}\n\n";
|
|
$message .= "Please review your security logs immediately.";
|
|
|
|
wp_mail($admin_email, $subject, $message);
|
|
}
|
|
|
|
// WordPress admin notices for high severity events
|
|
if ($event_data['severity'] >= self::SEVERITY_HIGH) {
|
|
set_transient('tigerstyle_scent_security_alert', $event_data, 3600);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Analyze attack patterns
|
|
*/
|
|
private static function analyze_attack_patterns(array $event_data): void {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_scent_security_log';
|
|
$client_ip = $event_data['client_info']['ip'];
|
|
|
|
// Check for repeated attacks from same IP in last hour
|
|
$recent_attacks = $wpdb->get_var(
|
|
$wpdb->prepare(
|
|
"SELECT COUNT(*) FROM $table_name
|
|
WHERE client_ip = %s
|
|
AND severity >= %d
|
|
AND timestamp > DATE_SUB(NOW(), INTERVAL 1 HOUR)",
|
|
$client_ip,
|
|
self::SEVERITY_MEDIUM
|
|
)
|
|
);
|
|
|
|
// Auto-block IPs with excessive attacks
|
|
if ($recent_attacks >= 5) {
|
|
self::log_security_event(
|
|
self::EVENT_SECURITY_VIOLATION,
|
|
self::SEVERITY_CRITICAL,
|
|
"IP auto-blocked for repeated security violations: {$client_ip}",
|
|
['attack_count' => $recent_attacks, 'auto_blocked' => true],
|
|
null,
|
|
$event_data['user_id']
|
|
);
|
|
|
|
// Store in transient for rate limiter to check
|
|
set_transient("tigerstyle_scent_auto_block_{$client_ip}", time(), DAY_IN_SECONDS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get sanitized client information
|
|
*/
|
|
private static function get_client_info(): array {
|
|
return [
|
|
'ip' => self::get_client_ip(),
|
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
|
|
'accept_language' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '',
|
|
'accept_encoding' => $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '',
|
|
'referer' => $_SERVER['HTTP_REFERER'] ?? '',
|
|
'connection' => $_SERVER['HTTP_CONNECTION'] ?? '',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get client IP address
|
|
*/
|
|
private static function get_client_ip(): string {
|
|
$ip_headers = [
|
|
'HTTP_CF_CONNECTING_IP',
|
|
'HTTP_X_REAL_IP',
|
|
'HTTP_X_FORWARDED_FOR',
|
|
'HTTP_X_FORWARDED',
|
|
'HTTP_X_CLUSTER_CLIENT_IP',
|
|
'HTTP_FORWARDED_FOR',
|
|
'HTTP_FORWARDED',
|
|
'REMOTE_ADDR'
|
|
];
|
|
|
|
foreach ($ip_headers as $header) {
|
|
if (!empty($_SERVER[$header])) {
|
|
$ip = $_SERVER[$header];
|
|
if (strpos($ip, ',') !== false) {
|
|
$ip = trim(explode(',', $ip)[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';
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get sanitized request data
|
|
*/
|
|
private static function get_sanitized_request_data(): array {
|
|
return [
|
|
'method' => $_SERVER['REQUEST_METHOD'] ?? '',
|
|
'uri' => $_SERVER['REQUEST_URI'] ?? '',
|
|
'query_string' => $_SERVER['QUERY_STRING'] ?? '',
|
|
'protocol' => $_SERVER['SERVER_PROTOCOL'] ?? '',
|
|
'https' => is_ssl(),
|
|
'port' => $_SERVER['SERVER_PORT'] ?? '',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get WordPress environment info
|
|
*/
|
|
private static function get_wordpress_info(): array {
|
|
return [
|
|
'wp_version' => get_bloginfo('version'),
|
|
'php_version' => PHP_VERSION,
|
|
'plugin_version' => TIGERSTYLE_SCENT_VERSION,
|
|
'is_admin' => is_admin(),
|
|
'is_user_logged_in' => is_user_logged_in(),
|
|
'current_user_id' => get_current_user_id(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get session identifier
|
|
*/
|
|
private static function get_session_id(): string {
|
|
if (session_id()) {
|
|
return session_id();
|
|
}
|
|
|
|
// Create session identifier from request characteristics
|
|
$session_data = [
|
|
self::get_client_ip(),
|
|
$_SERVER['HTTP_USER_AGENT'] ?? '',
|
|
$_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '',
|
|
date('Y-m-d H') // Hour-based session
|
|
];
|
|
|
|
return substr(hash('sha256', json_encode($session_data)), 0, 32);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get severity name
|
|
*/
|
|
private static function get_severity_name(int $severity): string {
|
|
switch ($severity) {
|
|
case self::SEVERITY_LOW: return 'LOW';
|
|
case self::SEVERITY_MEDIUM: return 'MEDIUM';
|
|
case self::SEVERITY_HIGH: return 'HIGH';
|
|
case self::SEVERITY_CRITICAL: return 'CRITICAL';
|
|
default: return 'UNKNOWN';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get security events from database
|
|
*
|
|
* @param array $filters Filtering options
|
|
* @param int $limit Number of events to retrieve
|
|
* @param int $offset Offset for pagination
|
|
* @return array Security events
|
|
*/
|
|
public static function get_security_events(array $filters = [], int $limit = 50, int $offset = 0): array {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_scent_security_log';
|
|
|
|
$where_conditions = ['1=1'];
|
|
$where_values = [];
|
|
|
|
if (!empty($filters['severity'])) {
|
|
$where_conditions[] = 'severity >= %d';
|
|
$where_values[] = $filters['severity'];
|
|
}
|
|
|
|
if (!empty($filters['event_type'])) {
|
|
$where_conditions[] = 'event_type = %s';
|
|
$where_values[] = $filters['event_type'];
|
|
}
|
|
|
|
if (!empty($filters['client_ip'])) {
|
|
$where_conditions[] = 'client_ip = %s';
|
|
$where_values[] = $filters['client_ip'];
|
|
}
|
|
|
|
if (!empty($filters['date_from'])) {
|
|
$where_conditions[] = 'timestamp >= %s';
|
|
$where_values[] = $filters['date_from'];
|
|
}
|
|
|
|
if (!empty($filters['date_to'])) {
|
|
$where_conditions[] = 'timestamp <= %s';
|
|
$where_values[] = $filters['date_to'];
|
|
}
|
|
|
|
$where_clause = implode(' AND ', $where_conditions);
|
|
|
|
$query = "SELECT * FROM $table_name WHERE $where_clause ORDER BY timestamp DESC LIMIT %d OFFSET %d";
|
|
$where_values[] = $limit;
|
|
$where_values[] = $offset;
|
|
|
|
if (!empty($where_values)) {
|
|
$prepared_query = $wpdb->prepare($query, $where_values);
|
|
} else {
|
|
$prepared_query = $query;
|
|
}
|
|
|
|
return $wpdb->get_results($prepared_query, ARRAY_A);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Real-time threat analysis with automatic blocking
|
|
*/
|
|
private static function perform_threat_analysis(array $event_data): void {
|
|
$client_ip = $event_data['client_info']['ip'];
|
|
$event_type = $event_data['event_type'];
|
|
$severity = $event_data['severity'];
|
|
|
|
// Immediate threat indicators
|
|
$immediate_threats = [
|
|
self::EVENT_INJECTION_ATTEMPT,
|
|
self::EVENT_SECURITY_VIOLATION
|
|
];
|
|
|
|
if (in_array($event_type, $immediate_threats)) {
|
|
self::initiate_emergency_response($event_data);
|
|
return;
|
|
}
|
|
|
|
// Analyze recent activity patterns
|
|
$recent_violations = self::get_recent_violations($client_ip, 60); // Last 60 minutes
|
|
|
|
if (count($recent_violations) >= 5) {
|
|
// Escalate to high severity and trigger auto-block
|
|
self::escalate_threat_level($event_data, $recent_violations);
|
|
}
|
|
|
|
// Check for distributed attack patterns
|
|
self::analyze_distributed_threats($event_data);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Enhanced attack pattern analysis
|
|
*/
|
|
private static function analyze_attack_patterns(array $event_data): void {
|
|
$client_ip = $event_data['client_info']['ip'];
|
|
|
|
// Define attack patterns
|
|
$attack_patterns = [
|
|
'brute_force' => [
|
|
'events' => [self::EVENT_AUTH_FAILURE],
|
|
'threshold' => 10,
|
|
'window' => 300, // 5 minutes
|
|
'action' => 'temporary_block'
|
|
],
|
|
'rate_limit_abuse' => [
|
|
'events' => [self::EVENT_RATE_LIMIT],
|
|
'threshold' => 3,
|
|
'window' => 600, // 10 minutes
|
|
'action' => 'escalated_block'
|
|
],
|
|
'validation_bombing' => [
|
|
'events' => [self::EVENT_VALIDATION_FAILURE],
|
|
'threshold' => 20,
|
|
'window' => 300, // 5 minutes
|
|
'action' => 'investigation_required'
|
|
]
|
|
];
|
|
|
|
foreach ($attack_patterns as $pattern_name => $pattern) {
|
|
if (in_array($event_data['event_type'], $pattern['events'])) {
|
|
$violations = self::get_recent_violations_by_type(
|
|
$client_ip,
|
|
$pattern['events'],
|
|
$pattern['window']
|
|
);
|
|
|
|
if (count($violations) >= $pattern['threshold']) {
|
|
self::trigger_attack_response($pattern_name, $pattern['action'], $event_data, $violations);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Initiate emergency response for critical threats
|
|
*/
|
|
private static function initiate_emergency_response(array $event_data): void {
|
|
$client_ip = $event_data['client_info']['ip'];
|
|
|
|
// Immediate IP blocking
|
|
self::emergency_block_ip($client_ip, 'Critical security violation detected');
|
|
|
|
// Send immediate critical alert
|
|
self::send_critical_security_alert($event_data);
|
|
|
|
// Log emergency response
|
|
error_log(sprintf(
|
|
'[TIGERSTYLE SCENT EMERGENCY] Critical threat from IP %s: %s',
|
|
$client_ip,
|
|
$event_data['message']
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Emergency IP blocking
|
|
*/
|
|
private static function emergency_block_ip(string $ip, string $reason): void {
|
|
// Store in transient for immediate effect
|
|
set_transient("tigerstyle_scent_emergency_block_{$ip}", [
|
|
'blocked_at' => current_time('timestamp'),
|
|
'reason' => $reason,
|
|
'duration' => 24 * HOUR_IN_SECONDS, // 24 hours
|
|
'threat_level' => 'CRITICAL'
|
|
], 24 * HOUR_IN_SECONDS);
|
|
|
|
// Trigger WordPress action for external integrations (firewall plugins, etc.)
|
|
do_action('tigerstyle_scent_emergency_block', $ip, $reason);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Enhanced security alerting
|
|
*/
|
|
private static function send_security_alert(array $event_data): void {
|
|
// Skip alerts in development mode unless critical
|
|
if (defined('WP_DEBUG') && WP_DEBUG && $event_data['severity'] < self::SEVERITY_CRITICAL) {
|
|
return;
|
|
}
|
|
|
|
$admin_email = get_option('admin_email');
|
|
if (!$admin_email) return;
|
|
|
|
$subject = sprintf(
|
|
'[%s] %s Security Alert - %s',
|
|
get_bloginfo('name'),
|
|
strtoupper($event_data['severity_name']),
|
|
$event_data['event_type']
|
|
);
|
|
|
|
$message = self::format_security_alert_email($event_data);
|
|
|
|
// Use WordPress mail function with security headers
|
|
$headers = [
|
|
'Content-Type: text/html; charset=UTF-8',
|
|
'X-Priority: 1',
|
|
'X-MSMail-Priority: High'
|
|
];
|
|
|
|
wp_mail($admin_email, $subject, $message, $headers);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Format security alert email
|
|
*/
|
|
private static function format_security_alert_email(array $event_data): string {
|
|
$threat_level_colors = [
|
|
1 => '#28a745', // Green
|
|
2 => '#ffc107', // Yellow
|
|
3 => '#fd7e14', // Orange
|
|
4 => '#dc3545' // Red
|
|
];
|
|
|
|
$color = $threat_level_colors[$event_data['severity']] ?? '#6c757d';
|
|
|
|
return sprintf('
|
|
<html>
|
|
<body style="font-family: Arial, sans-serif; line-height: 1.6;">
|
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
<div style="background: %s; color: white; padding: 15px; border-radius: 5px; margin-bottom: 20px;">
|
|
<h2 style="margin: 0;">🚨 TigerStyle Scent Security Alert</h2>
|
|
<p style="margin: 5px 0 0 0;">Severity: <strong>%s</strong></p>
|
|
</div>
|
|
|
|
<div style="background: #f8f9fa; padding: 15px; border-radius: 5px; margin-bottom: 20px;">
|
|
<h3>Event Details</h3>
|
|
<ul>
|
|
<li><strong>Event Type:</strong> %s</li>
|
|
<li><strong>Time:</strong> %s</li>
|
|
<li><strong>Message:</strong> %s</li>
|
|
<li><strong>Client IP:</strong> %s</li>
|
|
<li><strong>User Agent:</strong> %s</li>
|
|
<li><strong>Request URI:</strong> %s</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style="background: #e9ecef; padding: 15px; border-radius: 5px;">
|
|
<h3>Recommended Actions</h3>
|
|
<ul>
|
|
<li>Review security logs for patterns</li>
|
|
<li>Consider blocking suspicious IPs</li>
|
|
<li>Monitor for escalated threats</li>
|
|
<li>Update security configurations if needed</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<p style="margin-top: 20px; font-size: 12px; color: #6c757d;">
|
|
This alert was generated by TigerStyle Scent OAuth2 Security Monitor
|
|
</p>
|
|
</div>
|
|
</body>
|
|
</html>',
|
|
$color,
|
|
$event_data['severity_name'],
|
|
$event_data['event_type'],
|
|
$event_data['timestamp'],
|
|
esc_html($event_data['message']),
|
|
$event_data['client_info']['ip'],
|
|
esc_html(substr($event_data['client_info']['user_agent'], 0, 100)),
|
|
esc_html($event_data['request_data']['uri'])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Get recent security violations
|
|
*/
|
|
private static function get_recent_violations(string $client_ip, int $minutes): array {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_scent_security_log';
|
|
|
|
return $wpdb->get_results($wpdb->prepare(
|
|
"SELECT * FROM $table_name
|
|
WHERE client_ip = %s
|
|
AND timestamp >= DATE_SUB(NOW(), INTERVAL %d MINUTE)
|
|
AND severity >= %d
|
|
ORDER BY timestamp DESC",
|
|
$client_ip,
|
|
$minutes,
|
|
self::SEVERITY_MEDIUM
|
|
), ARRAY_A);
|
|
}
|
|
|
|
/**
|
|
* 🔐 SECURITY: Clean up old security logs
|
|
*/
|
|
public static function cleanup_old_logs(int $days_to_keep = 90): void {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_scent_security_log';
|
|
|
|
$wpdb->query(
|
|
$wpdb->prepare(
|
|
"DELETE FROM $table_name WHERE timestamp < DATE_SUB(NOW(), INTERVAL %d DAY)",
|
|
$days_to_keep
|
|
)
|
|
);
|
|
}
|
|
} |