Security [MEDIUM]: AI-tool error messages leak Prisma schema details to LLM #53

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

Problem

Several tool helpers return error.message verbatim for BAD_REQUEST/CONFLICT cases. Prisma errors embed column names, FK relations, and offending values. An attacker can craft inputs that force these messages into the chat reply → schema enumeration, email-existence check, etc.

Evidence

  • packages/api/src/router/assistant-tools/helpers.ts:297,373,615,922,1222,1304 — return { error: error.message }
  • helpers.ts:997 — toAssistantUserMutationError surfaces CONFLICT email-exists

Impact

Information disclosure via AI channel: DB schema, column names, relation structure, existing email enumeration.

Proposed Fix

Whitelist user-safe error strings per error code. Map Prisma errors to fixed texts (e.g., USER_ALREADY_EXISTS, INVALID_INPUT). Never return raw error.message through AI.

Acceptance Criteria

  • All toAssistant*Error helpers return from whitelist only
  • Unit test: injected Prisma P2002 error → generic text
  • Server log still retains full detail

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

## Problem Several tool helpers return `error.message` verbatim for BAD_REQUEST/CONFLICT cases. Prisma errors embed column names, FK relations, and offending values. An attacker can craft inputs that force these messages into the chat reply → schema enumeration, email-existence check, etc. ## Evidence - `packages/api/src/router/assistant-tools/helpers.ts:297,373,615,922,1222,1304 — return { error: error.message }` - `helpers.ts:997 — toAssistantUserMutationError surfaces CONFLICT email-exists` ## Impact Information disclosure via AI channel: DB schema, column names, relation structure, existing email enumeration. ## Proposed Fix Whitelist user-safe error strings per error code. Map Prisma errors to fixed texts (e.g., `USER_ALREADY_EXISTS`, `INVALID_INPUT`). Never return raw `error.message` through AI. ## Acceptance Criteria - [ ] All toAssistant*Error helpers return from whitelist only - [ ] Unit test: injected Prisma P2002 error → generic text - [ ] Server log still retains full detail --- Parent Epic: #1 Source: Full-Codebase Security Audit 2026-04-16 (C-2)
Hartmut added the security label 2026-04-16 22:05:11 +02:00
Author
Owner

Fixed in commit 23c6e0e on branch security/audit-2026-04-17.

Approach. Added sanitizeAssistantErrorMessage() in packages/api/src/router/assistant-tools/helpers.ts (lines 22-55). The helper regex-detects Prisma + raw Postgres error signatures and replaces them with a generic "Invalid input" fallback:

  • Invalid \prisma.XXX.YYY()`` invocations
  • Unique constraint failed on the fields: … (P2002)
  • Foreign key constraint failed on the field: … (P2003)
  • An operation failed because it depends on … (P2025)
  • duplicate key value violates unique constraint (raw pg)
  • null value in column "x" of relation "Y" (raw pg)
  • violates (check|not-null|foreign key) constraint (raw pg)
  • Plus a 500-char length cap against stack-trace-like payloads.

Call-sites fixed. All five raw passthroughs (lines 297, 373, 615, 773, 1222 per the ticket) now call sanitizeAssistantErrorMessage(error.message). The AssistantVisibleError branch in normalizeAssistantExecutionError is left as-is because its strings are developer-constructed.

Coverage.

  • 11 new unit tests in packages/api/src/__tests__/assistant-tools-error-sanitiser.test.ts — including the acceptance-criterion P2002 case
  • 12 existing error tests across vacation, task-action, resource-creation, project-creation error paths all remain green → no regression in hand-crafted router messages.

Acceptance:

  • Injected Prisma P2002-shaped error → generic "Invalid input"
  • Short hand-crafted router messages ("Vacation cannot be approved …") pass through unchanged
  • Stack-trace-like payloads capped

Closing.

Fixed in commit `23c6e0e` on branch `security/audit-2026-04-17`. **Approach.** Added `sanitizeAssistantErrorMessage()` in `packages/api/src/router/assistant-tools/helpers.ts` (lines 22-55). The helper regex-detects Prisma + raw Postgres error signatures and replaces them with a generic `"Invalid input"` fallback: - `Invalid \`prisma.XXX.YYY()\`` invocations - `Unique constraint failed on the fields: …` (P2002) - `Foreign key constraint failed on the field: …` (P2003) - `An operation failed because it depends on …` (P2025) - `duplicate key value violates unique constraint` (raw pg) - `null value in column "x" of relation "Y"` (raw pg) - `violates (check|not-null|foreign key) constraint` (raw pg) - Plus a 500-char length cap against stack-trace-like payloads. **Call-sites fixed.** All five raw passthroughs (lines 297, 373, 615, 773, 1222 per the ticket) now call `sanitizeAssistantErrorMessage(error.message)`. The `AssistantVisibleError` branch in `normalizeAssistantExecutionError` is left as-is because its strings are developer-constructed. **Coverage.** - 11 new unit tests in `packages/api/src/__tests__/assistant-tools-error-sanitiser.test.ts` — including the acceptance-criterion P2002 case - 12 existing error tests across vacation, task-action, resource-creation, project-creation error paths all remain green → no regression in hand-crafted router messages. Acceptance: - [x] Injected Prisma P2002-shaped error → generic "Invalid input" - [x] Short hand-crafted router messages (`"Vacation cannot be approved …"`) pass through unchanged - [x] Stack-trace-like payloads capped Closing.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Hartmut/CapaKraken#53