feat: comment spam content checks (URL-in-email, link limits)

- check_comment_content(): new method called from validate_comment()
  - Detects URL in email field (binance.info/register?ref=... pattern)
  - Blocks comments with more URLs than max_links threshold
  - Blocks any link from first-time commenters (0 approved comments)
- New options: block_url_in_email, block_links_new_commenters, max_links
- Admin: new "Comment Spam Content" card in Honeypot tab with toggles
  and max_links numeric input

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 07:18:49 +02:00
parent 52af2d9931
commit 1b9504e5f9
3 changed files with 81 additions and 2 deletions

View File

@@ -193,9 +193,55 @@ class ITK_Honeypot {
if (!$this->check_honeypot('comment')) {
wp_die('Spam detected. Please go back and try again.', 'Spam Blocked', ['response' => 403]);
}
if (!$this->check_comment_content($comment_data)) {
wp_die('Your comment was flagged as spam. Please remove any links and try again.', 'Spam Blocked', ['response' => 403]);
}
return $comment_data;
}
/* ── Comment content spam checks ─────────────────────────── */
private function check_comment_content(array $data): bool {
if (ITK_Whitelist::allowed()) return true;
$email = trim($data['comment_author_email'] ?? '');
$content = $data['comment_content'] ?? '';
$url = trim($data['comment_author_url'] ?? '');
// 1. URL in email field — real emails never contain / or ?
if (!empty(self::$opts['block_url_in_email']) && $email !== '') {
$is_url_in_email = !filter_var($email, FILTER_VALIDATE_EMAIL)
&& preg_match('#(https?://|[a-z0-9-]+\.[a-z]{2,}/|[?&]ref=)#i', $email);
if ($is_url_in_email) {
$this->log_block('comment', 'URL in email field: ' . substr($email, 0, 100));
return false;
}
}
// 2. Count URLs in comment body
$url_count = preg_match_all('#https?://\S+#i', $content);
$max_links = (int)(self::$opts['max_links'] ?? 2);
if ($max_links > 0 && $url_count > $max_links) {
$this->log_block('comment', "Too many URLs in comment ({$url_count})");
return false;
}
// 3. Any URL from a first-time commenter (0 approved comments)
if (!empty(self::$opts['block_links_new_commenters']) && ($url_count > 0 || $url !== '')) {
$approved = (int) get_comments([
'author_email' => $email,
'status' => 'approve',
'count' => true,
]);
if ($approved === 0) {
$this->log_block('comment', 'Link from first-time commenter');
return false;
}
}
return true;
}
public function validate_login($user, string $username, string $password) {
if (empty(self::$opts['protect_login'])) return $user;
if (!empty($username) && !$this->check_honeypot('login')) {