fix(api+schema): server-side validate schemaSnapshot to close DDL injection (HIGH-1) #14
No reviewers
Labels
No labels
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
javascript
question
wontfix
No milestone
No project
No assignees
1 participant
notification.notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
flndrn/briven!14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/security-sqli-schema-apply"
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?
Summary
Closes a HIGH-severity, cross-tenant DDL/DML injection in
POST/PATCH /v1/projects/:id/deployments. The route acceptedschemaSnapshotasz.record(z.string(), z.unknown())and handed it straight toservices/schema-apply.ts, which interpolated table names, column names, sqlType, default, FK references, and onDelete into rawCREATE/ALTER/DROPDDL run bytx.unsafe(...)on the shared data-plane superuser connection. The schema package'sisIdentifiercheck only fires inside the user-facing CLI builders, so a crafted JSON payload from any authenticated project owner could break out of an identifier or default literal and execute arbitrary DDL/DML across every tenant's schema.Two-part fix:
New
@briven/schema/wire.ts— Zod schema for the on-the-wire snapshot shape with strict refinements:IDENTIFIER_RE_briven_prefixsqlTypeis allowlisted (text|integer|bigint|boolean|timestamptz|jsonb|uuid|varchar(N)|vector(N))defaultis allowlisted (null/bool/numeric/'literal'/fn()/current_*)onDeleteis the existing enum;versionis a literal1.strict()so unknown keys fail closed@briven/cli/schemasub-export with no extra plumbing.apps/api/src/routes/deployments.ts— bothcreateSchema(POST) andpatchSchema(PATCH /latest) replace the permissivez.record(z.string(), z.unknown())withschemaSnapshotSchema. Malformed snapshots now return a structured 400 with Zod issues instead of reachingtx.unsafe.apps/api/src/services/schema-apply.ts— the_briven_migrationsinsert switched from string-concat + single-quote escape to bound-parametertx.unsafe(query, [deploymentId, deploymentId, json]). The values are server-generated today, but the raw interpolation here was a foot-gun for any future caller.Verification
varchar(N)/vector(N)types and the full safe-default vocabulary — still parse.pnpm --filter @briven/schema test: 18/18 pass (5 existing + 13 new)pnpm --filter @briven/api test: 9/9 passpnpm -w typecheck: 15/15 successfulpnpm -w build: 11/11 successful (CLI tarball still bundles)Rebased on top of
fix/security-hardening-phase-0(#13) cleanly — only the import block indeployments.tsconflicted, resolved by keeping phase-0'senvimport removal (sinceipHashno longer reads the pepper here) and adding the newschemaSnapshotSchemaimport.Test plan
/v1/projects/:id/deploymentswith a malicious table name likex" (id text); DROP SCHEMA "proj_other" CASCADE; --→ 400validation_failed, no DDL run_briven_migrationsinsert uses bound params (check viaEXPLAIN/server log)🤖 Generated with Claude Code