1. Switch str_contains() to exact match ($path === $route) in PUBLIC_ROUTES check to prevent substring-based route bypass attacks. 2. Remove blanket /api/admin/ bypass that was letting all admin endpoints through without authentication. 3. Add requireCronSecret() — cron/scheduled task endpoints now require a valid X-Cron-Secret header matching the PAYFRIT_CRON_SECRET env var. Uses hash_equals() for timing-safe comparison. Applied to: - cron/expireStaleChats.php - cron/expireTabs.php - api/admin/scheduledTasks/runDue.php
53 lines
1.6 KiB
PHP
53 lines
1.6 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../api/helpers.php';
|
|
requireCronSecret();
|
|
|
|
/**
|
|
* Expire stale chats (older than 20 minutes with no recent activity).
|
|
* Called every minute by cron.
|
|
*/
|
|
|
|
try {
|
|
$staleChats = queryTimed("
|
|
SELECT t.ID, t.CreatedOn,
|
|
(SELECT MAX(cm.CreatedOn) FROM ChatMessages cm WHERE cm.TaskID = t.ID) AS LastMessageOn
|
|
FROM Tasks t
|
|
WHERE t.TaskTypeID = 2
|
|
AND t.CompletedOn IS NULL
|
|
AND t.CreatedOn < DATE_SUB(NOW(), INTERVAL 20 MINUTE)
|
|
");
|
|
|
|
$expiredCount = 0;
|
|
$expiredIds = [];
|
|
|
|
foreach ($staleChats as $chat) {
|
|
$shouldExpire = false;
|
|
|
|
if (empty($chat['LastMessageOn'])) {
|
|
$shouldExpire = true;
|
|
} else {
|
|
$lastMsg = new DateTime($chat['LastMessageOn'], new DateTimeZone('UTC'));
|
|
$now = new DateTime('now', new DateTimeZone('UTC'));
|
|
$diffMinutes = ($now->getTimestamp() - $lastMsg->getTimestamp()) / 60;
|
|
if ($diffMinutes > 20) {
|
|
$shouldExpire = true;
|
|
}
|
|
}
|
|
|
|
if ($shouldExpire) {
|
|
queryTimed("UPDATE Tasks SET CompletedOn = NOW() WHERE ID = ?", [$chat['ID']]);
|
|
$expiredCount++;
|
|
$expiredIds[] = (int) $chat['ID'];
|
|
}
|
|
}
|
|
|
|
jsonResponse([
|
|
'OK' => true,
|
|
'MESSAGE' => "Expired {$expiredCount} stale chat(s)",
|
|
'EXPIRED_TASK_IDS' => $expiredIds,
|
|
'CHECKED_COUNT' => count($staleChats),
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => $e->getMessage()]);
|
|
}
|