Security [MEDIUM]: AI-tool error messages leak Prisma schema details to LLM #53
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Several tool helpers return
error.messageverbatim 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-existsImpact
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 rawerror.messagethrough AI.Acceptance Criteria
Parent Epic: #1
Source: Full-Codebase Security Audit 2026-04-16 (C-2)
Fixed in commit
23c6e0eon branchsecurity/audit-2026-04-17.Approach. Added
sanitizeAssistantErrorMessage()inpackages/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()`` invocationsUnique 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)Call-sites fixed. All five raw passthroughs (lines 297, 373, 615, 773, 1222 per the ticket) now call
sanitizeAssistantErrorMessage(error.message). TheAssistantVisibleErrorbranch innormalizeAssistantExecutionErroris left as-is because its strings are developer-constructed.Coverage.
packages/api/src/__tests__/assistant-tools-error-sanitiser.test.ts— including the acceptance-criterion P2002 caseAcceptance:
"Vacation cannot be approved …") pass through unchangedClosing.