roles) || in_array('hvac_master_trainer', $user->roles) || user_can($user, 'manage_options'); } /** * Check if user has HVAC master trainer role * * @param int|null $user_id User ID (null for current user) * @return bool */ public static function is_hvac_master_trainer($user_id = null) { $user = $user_id ? get_user_by('id', $user_id) : wp_get_current_user(); if (!$user) { return false; } return in_array('hvac_master_trainer', $user->roles) || user_can($user, 'manage_options'); } /** * Sanitize and validate superglobal input * * @param string $type 'GET', 'POST', 'REQUEST', 'COOKIE', 'SERVER' * @param string $key The key to retrieve * @param string $sanitize Sanitization function to use * @param mixed $default Default value if not set * @return mixed Sanitized value */ public static function get_input($type, $key, $sanitize = 'sanitize_text_field', $default = '') { $superglobal = null; switch (strtoupper($type)) { case 'GET': $superglobal = $_GET; break; case 'POST': $superglobal = $_POST; break; case 'REQUEST': $superglobal = $_REQUEST; break; case 'COOKIE': $superglobal = $_COOKIE; break; case 'SERVER': $superglobal = $_SERVER; break; default: return $default; } if (!isset($superglobal[$key])) { return $default; } $value = $superglobal[$key]; // Handle arrays if (is_array($value)) { return array_map($sanitize, $value); } // Apply sanitization switch ($sanitize) { case 'absint': return absint($value); case 'intval': return intval($value); case 'sanitize_email': return sanitize_email($value); case 'sanitize_url': case 'esc_url_raw': return esc_url_raw($value); case 'sanitize_text_field': return sanitize_text_field($value); case 'sanitize_textarea_field': return sanitize_textarea_field($value); case 'wp_kses_post': return wp_kses_post($value); case 'none': return $value; // Use with extreme caution default: return sanitize_text_field($value); } } /** * Validate file upload * * @param array $file $_FILES array element * @param array $allowed_types Allowed MIME types * @param int $max_size Maximum file size in bytes * @return bool|WP_Error True if valid, WP_Error on failure */ public static function validate_file_upload($file, $allowed_types = array(), $max_size = 5242880) { // Check if file was uploaded if (!isset($file) || $file['error'] !== UPLOAD_ERR_OK) { return new WP_Error('upload_error', 'File upload failed'); } // Security check if (!is_uploaded_file($file['tmp_name'])) { return new WP_Error('security_error', 'Invalid file upload'); } // Check file size if ($file['size'] > $max_size) { return new WP_Error('size_error', sprintf('File too large. Maximum size is %s', size_format($max_size))); } // Check file type if (!empty($allowed_types)) { $file_type = wp_check_filetype($file['name']); if (!in_array($file_type['type'], $allowed_types)) { return new WP_Error('type_error', 'Invalid file type'); } } return true; } /** * Generate secure nonce field * * @param string $action Nonce action * @param string $name Nonce field name * @return string HTML nonce field */ public static function nonce_field($action, $name = '_wpnonce') { return wp_nonce_field($action, $name, true, false); } /** * Verify nonce from request * * @param string $action Nonce action * @param string $name Nonce field name * @param string $type Request type (GET, POST) * @return bool */ public static function verify_nonce($action, $name = '_wpnonce', $type = 'POST') { $nonce = self::get_input($type, $name, 'sanitize_text_field', ''); return wp_verify_nonce($nonce, $action); } /** * Check AJAX referer with proper error handling * * @param string $action Nonce action * @param string $query_arg Nonce query argument * @return bool */ public static function check_ajax_nonce($action, $query_arg = 'nonce') { if (!check_ajax_referer($action, $query_arg, false)) { wp_send_json_error('Security check failed'); return false; } return true; } /** * Escape output based on context * * @param mixed $data Data to escape * @param string $context Context: 'html', 'attr', 'url', 'js', 'textarea' * @return string Escaped data */ public static function escape($data, $context = 'html') { if (is_array($data) || is_object($data)) { return ''; } switch ($context) { case 'html': return esc_html($data); case 'attr': return esc_attr($data); case 'url': return esc_url($data); case 'js': return esc_js($data); case 'textarea': return esc_textarea($data); default: return esc_html($data); } } /** * Validate and sanitize email * * @param string $email Email address * @return string|false Sanitized email or false if invalid */ public static function validate_email($email) { $email = sanitize_email($email); return is_email($email) ? $email : false; } /** * Add security headers */ public static function add_security_headers() { // Content Security Policy header("Content-Security-Policy: default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval'"); // X-Frame-Options header("X-Frame-Options: SAMEORIGIN"); // X-Content-Type-Options header("X-Content-Type-Options: nosniff"); // X-XSS-Protection header("X-XSS-Protection: 1; mode=block"); // Referrer Policy header("Referrer-Policy: strict-origin-when-cross-origin"); } /** * Rate limiting check * * @param string $action Action identifier * @param int $max_attempts Maximum attempts allowed * @param int $window Time window in seconds * @return bool True if allowed, false if rate limited */ public static function check_rate_limit($action, $max_attempts = 5, $window = 60) { $user_id = get_current_user_id(); $ip = self::get_client_ip(); $key = 'hvac_rate_limit_' . md5($action . '_' . $user_id . '_' . $ip); $attempts = get_transient($key); if ($attempts === false) { set_transient($key, 1, $window); return true; } if ($attempts >= $max_attempts) { return false; } set_transient($key, $attempts + 1, $window); return true; } /** * Get client IP address * * @return string */ public static function get_client_ip() { $ip_keys = array('HTTP_CF_CONNECTING_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'); foreach ($ip_keys as $key) { if (array_key_exists($key, $_SERVER) === true) { $ips = explode(',', $_SERVER[$key]); foreach ($ips as $ip) { $ip = trim($ip); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) { return $ip; } } } } return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; } /** * Log security events * * @param string $event Event type * @param array $data Event data */ public static function log_security_event($event, $data = array()) { $log_data = array( 'event' => $event, 'timestamp' => current_time('mysql'), 'user_id' => get_current_user_id(), 'ip' => self::get_client_ip(), 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '', 'data' => $data ); // Log to database or file error_log('[HVAC Security] ' . json_encode($log_data)); // You can also save to database if needed if (defined('HVAC_SECURITY_LOG_TO_DB') && HVAC_SECURITY_LOG_TO_DB) { global $wpdb; $table = $wpdb->prefix . 'hvac_security_log'; $wpdb->insert($table, array( 'event_type' => $event, 'event_data' => json_encode($data), 'user_id' => get_current_user_id(), 'ip_address' => self::get_client_ip(), 'created_at' => current_time('mysql') )); } } }