Area: mobieusHelp (audit p7) · Surface: mobieusHelp /api/help/ai/* (AgentAIController) · Dimension: security · Severity: major
OWASP A01 / CSRF. These endpoints mutate state indirectly (categorize/auto-suggest spend tenant AI budget; AISpendTracker writes) and read sensitive ticket data, but accept cross-site POSTs from any origin while the victim agent is logged in, with no anti-CSRF token. An attacker page can force an authenticated agent's browser to fire these, draining the tenant Anthropic key and exfiltrating nothing directly but enabling forced-spend abuse.
Evidence
Routes /api/help/ai/reply-suggest, /summary, /categorize, /sentiment, /canned-generate, /audit-qa, /resolution-predict are POST (routes.php L756-762) registered in the group opened at routes.php L340: `$router->group('', [PerformanceMiddleware, ProbeDetectionMiddleware, IpBanMiddleware, LlmBotBlockMiddleware, MaintenanceMiddleware, AccessModeMiddleware, LurkerTrackerMiddleware, SuspensionGateMiddleware, GateMiddleware], ...)` — this list contains NO CsrfValidationMiddleware. AgentAIController has no $this->validateCsrf() call anywhere (it only checks $_SESSION['user_id'] in __construct, L39).
Suggested fix. Add CsrfValidationMiddleware to these route registrations (or move them into the authenticated group at routes.php L775 which includes CsrfValidationMiddleware), and/or call $this->validateCsrf() in AgentAIController. The app's app.min.js already injects X-CSRF-TOKEN on fetch, so legitimate callers are unaffected.
Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.
Patrick Bass
@mobieus