WizdomWeb/app/Utilities/SubmissionCheck.php

77 lines
3.1 KiB
PHP

<?php
/**
* File: SubmissionCheck.php
* Version: 1.3
* Purpose: Helper to detect and block repeated or abusive contact form submissions
* Project: Wizdom Networks Website
*/
namespace WizdomNetworks\WizeWeb\Utilities;
use PDO;
use WizdomNetworks\WizeWeb\Utilities\Logger;
class SubmissionCheck
{
private const LOOKBACK_DAYS = 30;
/**
* Evaluates whether a submission is likely spam or abuse.
*
* @param PDO $pdo
* @param string $email
* @param string|null $phone
* @param string|null $ip
* @return array [action: accept|flag|block|notify, reason: string, count: int]
*/
public static function evaluate(PDO $pdo, string $email, ?string $phone, ?string $ip): array
{
try {
// MySQL cannot bind inside INTERVAL, so we inject LOOKBACK_DAYS directly
$lookback = (int) self::LOOKBACK_DAYS;
$query = "
SELECT
(SELECT COUNT(*) FROM submission_logs WHERE email = :email AND created_at >= NOW() - INTERVAL $lookback DAY) AS email_hits,
(SELECT COUNT(*) FROM submission_logs WHERE phone = :phone AND created_at >= NOW() - INTERVAL $lookback DAY) AS phone_hits,
(SELECT COUNT(*) FROM submission_logs WHERE ip_address = :ip1 AND created_at >= NOW() - INTERVAL $lookback DAY) AS ip_hits,
(SELECT COUNT(*) FROM submission_logs WHERE ip_address = :ip2 AND created_at >= NOW() - INTERVAL 1 HOUR) AS ip_hourly
";
$stmt = $pdo->prepare($query);
$stmt->bindValue(':email', $email);
$stmt->bindValue(':phone', $phone);
$stmt->bindValue(':ip1', $ip);
$stmt->bindValue(':ip2', $ip);
$stmt->execute();
$data = $stmt->fetch(PDO::FETCH_ASSOC);
$emailHits = (int)($data['email_hits'] ?? 0);
$phoneHits = (int)($data['phone_hits'] ?? 0);
$ipHits = (int)($data['ip_hits'] ?? 0);
$ipHourly = (int)($data['ip_hourly'] ?? 0);
$totalScore = $emailHits + $phoneHits + $ipHits;
if ($emailHits >= 4 || $phoneHits >= 4 || $ipHits >= 5) {
return ['action' => 'block', 'reason' => 'IP/email/phone threshold exceeded', 'count' => $totalScore];
}
if ($ipHourly >= 3) {
return ['action' => 'flag', 'reason' => 'Multiple submissions from IP in last hour', 'count' => $ipHourly];
}
if ($totalScore >= 6) {
return ['action' => 'notify', 'reason' => 'Cumulative signal from all identifiers', 'count' => $totalScore];
}
if ($emailHits >= 2 || $phoneHits >= 2 || $ipHits >= 2) {
return ['action' => 'flag', 'reason' => 'Repeated pattern detected', 'count' => $totalScore];
}
return ['action' => 'accept', 'reason' => 'accepted', 'count' => $totalScore];
} catch (\Throwable $e) {
Logger::error("SubmissionCheck evaluation failed: " . $e->getMessage());
return ['action' => 'error', 'reason' => 'Evaluation error', 'count' => 0];
}
}
}