Security [HIGH]: Assistant chat message content unbounded — AI cost/memory DoS #38

Closed
opened 2026-04-16 22:05:08 +02:00 by Hartmut · 1 comment
Owner

Problem

messages[].content and pageContext in assistant-procedure-support.ts have no .max(). An authenticated user can submit 50 MB per message × 200 messages ⇒ 10 GB of text → AI-cost DoS, memory blow-up during JSON parse & prompt assembly.

Evidence

  • packages/api/src/router/assistant-procedure-support.ts:47-54 — content: z.string() (no max)
  • packages/api/src/router/assistant-procedure-support.ts:54 — pageContext: z.string().optional() (no max)
  • packages/api/src/router/project-cover.ts:67-69 — DALL-E/Gemini prompt user-input concatenated directly, no injection-guard

Impact

Any authenticated user can: (a) burn thousands of dollars in AI-provider costs in minutes, (b) OOM the API process via JSON parsing, (c) inject directives into DALL-E/Gemini via project-cover prompt (no checkPromptInjection there).

Proposed Fix

content: z.string().max(10_000), pageContext: z.string().max(2_000). Compute aggregate message budget (e.g., 50 KB) and reject when exceeded. Apply checkPromptInjection() to project-cover.prompt.

Acceptance Criteria

  • All assistant input fields have explicit .max()
  • Unit test: 10 MB message rejected with 400
  • project-cover prompt path runs through checkPromptInjection()

Parent Epic: #1
Source: Full-Codebase Security Audit 2026-04-16 (B-2, B-15)

## Problem `messages[].content` and `pageContext` in assistant-procedure-support.ts have no `.max()`. An authenticated user can submit 50 MB per message × 200 messages ⇒ 10 GB of text → AI-cost DoS, memory blow-up during JSON parse & prompt assembly. ## Evidence - `packages/api/src/router/assistant-procedure-support.ts:47-54 — content: z.string() (no max)` - `packages/api/src/router/assistant-procedure-support.ts:54 — pageContext: z.string().optional() (no max)` - `packages/api/src/router/project-cover.ts:67-69 — DALL-E/Gemini prompt user-input concatenated directly, no injection-guard` ## Impact Any authenticated user can: (a) burn thousands of dollars in AI-provider costs in minutes, (b) OOM the API process via JSON parsing, (c) inject directives into DALL-E/Gemini via project-cover prompt (no `checkPromptInjection` there). ## Proposed Fix `content: z.string().max(10_000)`, `pageContext: z.string().max(2_000)`. Compute aggregate message budget (e.g., 50 KB) and reject when exceeded. Apply `checkPromptInjection()` to `project-cover.prompt`. ## Acceptance Criteria - [ ] All assistant input fields have explicit `.max()` - [ ] Unit test: 10 MB message rejected with 400 - [ ] project-cover prompt path runs through checkPromptInjection() --- Parent Epic: #1 Source: Full-Codebase Security Audit 2026-04-16 (B-2, B-15)
Hartmut added the security label 2026-04-16 22:05:08 +02:00
Author
Owner

Resolved. packages/api/src/router/assistant-procedure-support.ts:49-77 now enforces:

  • content: z.string().max(10_000) per message
  • pageContext: z.string().max(2_000)
  • conversationId: z.string().max(120)
  • Aggregate cap via superRefine: 200 KB across all messages + pageContext
  • .min(1).max(200) messages array

packages/api/src/router/project-cover.ts:37-56 now runs checkPromptInjection(input.prompt) on the user's additional-direction field and rejects with BAD_REQUEST on a hit.

Tests: packages/api/src/__tests__/assistant-input-bounds.test.ts covers all four limits with both accept + reject cases.

All acceptance criteria met — closing.

Resolved. `packages/api/src/router/assistant-procedure-support.ts:49-77` now enforces: - `content: z.string().max(10_000)` per message - `pageContext: z.string().max(2_000)` - `conversationId: z.string().max(120)` - Aggregate cap via `superRefine`: 200 KB across all messages + pageContext - `.min(1).max(200)` messages array `packages/api/src/router/project-cover.ts:37-56` now runs `checkPromptInjection(input.prompt)` on the user's additional-direction field and rejects with BAD_REQUEST on a hit. Tests: `packages/api/src/__tests__/assistant-input-bounds.test.ts` covers all four limits with both accept + reject cases. All acceptance criteria met — closing.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Hartmut/CapaKraken#38