Skip to content

d1 import: block DEFAULT/REFERENCES SQL injection into executed DDL#1304

Draft
mscoutermarsh wants to merge 3 commits into
mainfrom
cursor/d1-default-sql-injection-23b4
Draft

d1 import: block DEFAULT/REFERENCES SQL injection into executed DDL#1304
mscoutermarsh wants to merge 3 commits into
mainfrom
cursor/d1-default-sql-injection-23b4

Conversation

@mscoutermarsh

@mscoutermarsh mscoutermarsh commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Two related D1-import SQL injection paths let attacker-controlled dump fragments reach psql -f with the destination postgres role:

  1. DEFAULTconvertDefault echoed unrecognized typed defaults verbatim, so DEFAULT (0)); DROP TABLE users; -- could escape CREATE TABLE.
  2. REFERENCES tailconvertReferencesClause echoed the action tail verbatim, and parseTableBody used the last ) in RawDDL, so a dump like REFERENCES a(id) ON DELETE CASCADE); DROP TABLE users; CREATE TABLE dummy (z int ); pulled injected statements into the FK fragment and into executed DDL.

Fix

  • Emit DEFAULT only for mapped functions/casts or validated scalars; omit anything else.
  • Reject broken quoted literals / hostile UUID defaults.
  • Reconstruct REFERENCES with an allowlisted FK-action tail only; drop unparseable clauses and tails with ; / extra parentheses.
  • Parse CREATE TABLE bodies with matchingParenEnd (not LastIndex), and truncate RawDDL at that close during ParseDump.

Tests

  • DEFAULT injection payloads omitted; safe numeric literals kept
  • REFERENCES unit + end-to-end for the reported smuggle-after-CASCADE dump
  • parseTableBody ignores SQL after the balanced close

go test ./internal/import/d1/ passes.

Open in Web Open in Cursor 

cursoragent and others added 3 commits July 22, 2026 22:30
Unrecognized DEFAULT expressions on typed columns were spliced into
CREATE TABLE as-is, so a payload like DEFAULT (0)); DROP TABLE users; --
could escape the statement and run via psql. Restrict those defaults to
validated numeric/boolean/quoted literals (or omit them), harden UUID and
quoted-literal handling, and drop unsafe REFERENCES action tails the same way.

Co-authored-by: Mike Coutermarsh <coutermarsh.mike@gmail.com>
parseTableBody used the last ')' in RawDDL, so a dump that smuggled
"); DROP ...; CREATE TABLE ..." after a real REFERENCES close pulled
attacker SQL into the column/FK fragment. Match the opening paren
instead, truncate RawDDL at that close during ParseDump, and cover the
reported REFERENCES-tail injection end-to-end.

Co-authored-by: Mike Coutermarsh <coutermarsh.mike@gmail.com>
assertValidPostgresDDL calls t.Skip when no local Postgres is available,
which skipped later unit assertions in the DEFAULT injection regression.

Co-authored-by: Mike Coutermarsh <coutermarsh.mike@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants