Forums Bug Reports Thread

Legacy AiController AI endpoints bypass the per-tenant plan gate, monthly spend cap, and per-user rate cap

Patrick Bass · Jun 6 · 10 · 1 Locked
[Major] [High Priority] [Bug Fixed] [Always Reproduces]
🚀 OP Jun 6, 2026 7:18pm

Area: mobieusAI (audit p11) · Surface: AI — mobieusAI community (legacy AiController) · Dimension: security · Severity: major

OWASP A04:2021 Insecure Design / lack of resource-consumption controls (and a business-logic access-control gap). The newer AI surfaces are explicitly designed so every Anthropic call passes AIFeatureGate (tier + monthly spend cap + per-user daily cap). The three legacy /api/ai/* endpoints skip that gate entirely, so any authenticated member can drive unbounded Anthropic spend on the tenant's BYOK key (each call hits api.anthropic.com), and the tenant admin's configured monthly_cap_cents / rate_per_user_per_day for these features is silently ineffective. searchAssist in particular runs an LLM synthesis on every POST with only a 200-char query length check.

Evidence

platform/src/Controllers/AiController.php gates only on AnthropicClient::enabled() + Features::granted() — e.g. searchAssist (lines 75-113), suggestTags (lines 59-72), summarizeThread (lines 41-52). AnthropicClient::enabled() (platform/src/Services/AnthropicClient.php:66-71) checks ONLY `$_ENV['AI_FEATURES_ENABLED']==='1'` and a non-empty `$_ENV['ANTHROPIC_API_KEY']` — no plan check, no spend cap, no rate cap. The canonical gate AIFeatureGate::check() (platform/src/Services/AI/AIFeatureGate.php:30-90) enforces tenant-plan eligibility (line 45-49), monthly_cap_cents (line 67-75) and rate_per_user_per_day (line 77-87) — none of which the legacy controller calls. The underlying legacy services confirm no gate: AiTagSuggester.php:20 and AiSearchAssist.php:23 each only check AnthropicClient::enabled(). The only throttle on these routes is the generic HTTP RateLimitMiddleware at 1800 req/min/path (platform/src/Middleware/RateLimitMiddleware.php:75), which is far above any sane LLM-cost budget.

Suggested fix. Route every legacy AI action through AIFeatureGate::check($featureKey, $userId) before invoking the service (return ai_disabled/gate_blocked on false), or decommission the legacy routes (routes.php:1094-1096) now that MemberAIController/AIAssistController enforce the gate. AISpendTracker::record() should also be called on these paths so spend is accounted.

Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.


Patrick Bass
@mobieus

🚀 Jun 7, 2026 5:25am

Resolved — fixed and deployed. Commit dd336ac47616, shipped dev-first then to all tenants on 2026-06-06.

Replaced the bare Features::granted() checks in the two catalog-backed legacy actions (summarizeThread -> ai_forum_summarize, suggestTags -> ai_forum_tag_suggest) with AIFeatureGate::check($key, (int)$user['id']), so the legacy paths now enforce the full gate (master switch + tenant plan eligibility + flag + monthly spend cap + per-user rate cap) instead of just the flag, matching MemberAI/AIAssist.

Status: fixed. Thread closed and locked.


Patrick Bass
@mobieus

Log in or register to reply to this thread.