.itk-hp-field{display:none!important;visibility:hidden!important;opacity:0!important;position:absolute!important;left:-9999px!important;top:-9999px!important;}' . "\n"; } /* ── JS token (HMAC-based anti-CSRF) ─────────────────────── */ public function enqueue_token_script(): void { $secret = $this->get_page_secret(); wp_add_inline_script('jquery-core', " (function(){ var s='" . esc_js($secret) . "',n='" . esc_js(wp_create_nonce('itk_hp')) . "'; document.querySelectorAll('." . self::TOKEN_FIELD . "').forEach(function(f){ f.value=btoa(s+'|'+Date.now()+'|'+n); }); document.querySelectorAll('." . self::TIME_FIELD . "').forEach(function(f){ f.value=Math.floor(Date.now()/1000); }); })(); ", 'after'); } /* ── Field HTML generator ─────────────────────────────────── */ private function honeypot_html(string $form_type = ''): string { $field = self::FIELD_PREFIX . substr(md5(uniqid()), 0, 8); $ts = time(); $label = ['Your email address', 'Website URL', 'Full name'][array_rand(['a','b','c'])]; return sprintf( '', esc_html($label), esc_attr($field), self::TOKEN_FIELD, self::TOKEN_FIELD, self::TIME_FIELD, self::TIME_FIELD, $ts ); } /* ── Injectors ────────────────────────────────────────────── */ public function inject_generic(): void { if (empty(self::$opts['enabled'])) return; echo $this->honeypot_html(); // phpcs:ignore } public function inject_comment(): void { if (empty(self::$opts['protect_comments'])) return; echo $this->honeypot_html('comment'); // phpcs:ignore } public function inject_search(string $form): string { return $form . $this->honeypot_html('search'); } public function inject_cf7(string $content): string { return $content . $this->honeypot_html('cf7'); } public function inject_gravity(string $tag, array $form): string { return $tag . $this->honeypot_html('gravity'); } public function elementor_enqueue(): void { // Elementor injects via JS – add hidden fields via wp_footer add_action('wp_footer', [$this, 'inject_generic']); } /* ── Validators ───────────────────────────────────────────── */ private function check_honeypot(string $form_type): bool { // 1. Honeypot field must be empty foreach ($_POST as $key => $val) { if (strpos($key, self::FIELD_PREFIX) === 0 && !empty($val)) { $this->log_block($form_type, 'Honeypot field filled'); return false; } } // 2. Timing check $opts = get_option('itk_honeypot', []); $min_t = max(1, (int)($opts['min_time'] ?? 3)); $max_t = max(60, (int)($opts['max_time'] ?? 7200)); $ts = (int)($_POST[self::TIME_FIELD] ?? 0); $elapsed = time() - $ts; if ($ts > 0 && $elapsed < $min_t) { $this->log_block($form_type, "Submitted too fast ({$elapsed}s)"); return false; } if ($ts > 0 && $elapsed > $max_t) { $this->log_block($form_type, "Submitted too slow ({$elapsed}s > {$max_t}s)"); return false; } return true; } private function log_block(string $form_type, string $reason): void { $event = [ 'ip' => $this->get_ip(), 'form' => $form_type, 'reason' => $reason, 'uri' => $_SERVER['REQUEST_URI'] ?? '', 'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '', ]; ITK_Database::log_honeypot($event); ITK_HP_API::queue($event); } public function validate_comment(array $comment_data): array { if (empty(self::$opts['protect_comments'])) return $comment_data; if (!$this->check_honeypot('comment')) { wp_die('Spam detected. Please go back and try again.', 'Spam Blocked', ['response' => 403]); } return $comment_data; } public function validate_login($user, string $username, string $password) { if (empty(self::$opts['protect_login'])) return $user; if (!empty($username) && !$this->check_honeypot('login')) { return new WP_Error('honeypot_blocked', 'Access denied.'); } return $user; } public function validate_register($sanitized_user_login, $user_email, \WP_Error $errors): void { if (empty(self::$opts['protect_register'])) return; if (!$this->check_honeypot('register')) { $errors->add('honeypot_blocked', 'Spam registration detected.'); } } public function validate_lost_password(\WP_Error $errors): void { if (empty(self::$opts['protect_lost_password'])) return; if (!$this->check_honeypot('lostpassword')) { $errors->add('honeypot_blocked', 'Access denied.'); } } public function validate_woo_checkout(): void { if (!$this->check_honeypot('woo_checkout')) { wc_add_notice('Spam submission detected. Please refresh and try again.', 'error'); } } public function validate_woo_registration(\WP_Error $errors, $username, $email): \WP_Error { if (!$this->check_honeypot('woo_register')) { $errors->add('honeypot_blocked', 'Spam registration detected.'); } return $errors; } public function validate_cf7($contact_form, &$abort, $submission): void { if (!$this->check_honeypot('cf7')) { $abort = true; $contact_form->set_status('spam'); } } public function validate_elementor($record, $ajax_handler): void { if (!$this->check_honeypot('elementor')) { $ajax_handler->add_error_message('Spam detected. Please try again.'); } } public function validate_gravity(array $validation_result): array { if (!$this->check_honeypot('gravity')) { $validation_result['is_valid'] = false; foreach ($validation_result['form']['fields'] as &$field) { $field->failed_validation = true; $field->validation_message = 'Spam detected.'; } } return $validation_result; } /* ── Helpers ──────────────────────────────────────────────── */ private function get_ip(): string { $keys = [ 'HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP','HTTP_FORWARDED_FOR','HTTP_FORWARDED','REMOTE_ADDR', ]; foreach ($keys as $k) { if (!empty($_SERVER[$k])) { $ip = trim(explode(',', $_SERVER[$k])[0]); if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip; } } return 'UNKNOWN'; } private function get_page_secret(): string { $secret = get_option('itk_hp_secret'); if (!$secret) { $secret = wp_generate_password(32, false); update_option('itk_hp_secret', $secret); } return $secret; } }