Add comment spam classification and captcha checks.
Score artwork comments with local signatures plus Together AI, and optionally require Turnstile before posting.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use cPad\Plugins\Forum\Services\Security\BotProtectionService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CommentCaptchaMiddleware
|
||||
{
|
||||
public function __construct(private readonly BotProtectionService $botProtection) {}
|
||||
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (! (bool) config('comment_spam.captcha.enabled', false)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$assessment = $this->botProtection->assess($request, 'comment_create');
|
||||
if ((bool) ($assessment['blocked'] ?? false)) {
|
||||
return response()->json(['message' => 'Suspicious activity detected.', 'errors' => ['bot' => ['Suspicious activity detected.']]], 429);
|
||||
}
|
||||
$threshold = (int) config('comment_spam.captcha.threshold', 40);
|
||||
if ((int) ($assessment['risk_score'] ?? 0) < $threshold) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$token = (string) ($request->input('comment-turnstile-response') ?: $request->header('X-Turnstile-Token', ''));
|
||||
$valid = false;
|
||||
if ($token !== '') {
|
||||
try {
|
||||
$valid = (bool) Http::asForm()->timeout(5)->post(
|
||||
(string) config('comment_spam.captcha.verify_url'),
|
||||
['secret' => (string) config('comment_spam.captcha.secret_key'), 'response' => $token, 'remoteip' => $request->ip()],
|
||||
)->json('success', false);
|
||||
} catch (\Throwable) {
|
||||
$valid = (bool) config('comment_spam.captcha.fail_open', false);
|
||||
}
|
||||
}
|
||||
|
||||
if ($valid) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'message' => 'Complete the captcha challenge to continue.',
|
||||
'errors' => ['captcha' => ['Complete the captcha challenge to continue.']],
|
||||
'requires_captcha' => true,
|
||||
'captcha' => [
|
||||
'provider' => 'turnstile',
|
||||
'siteKey' => (string) config('comment_spam.captcha.site_key'),
|
||||
'inputName' => 'comment-turnstile-response',
|
||||
'scriptUrl' => (string) config('comment_spam.captcha.script_url'),
|
||||
],
|
||||
'captcha_provider' => 'turnstile',
|
||||
'captcha_site_key' => (string) config('comment_spam.captcha.site_key'),
|
||||
'captcha_input' => 'comment-turnstile-response',
|
||||
'captcha_script_url' => (string) config('comment_spam.captcha.script_url'),
|
||||
];
|
||||
|
||||
return response()->json($payload, 422);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user