Skip to content

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

Merged
mscoutermarsh merged 5 commits into
mainfrom
cursor/d1-default-sql-injection-23b4
Jul 23, 2026
Merged

d1 import: block DEFAULT/REFERENCES SQL injection into executed DDL#1304
mscoutermarsh merged 5 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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
@mscoutermarsh
mscoutermarsh marked this pull request as ready for review July 23, 2026 11:50
@mscoutermarsh
mscoutermarsh requested a review from a team as a code owner July 23, 2026 11:50

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8762d16. Configure here.

Comment thread internal/import/d1/constraints.go
@mscoutermarsh
mscoutermarsh merged commit 8adefea into main Jul 23, 2026
4 checks passed
@mscoutermarsh
mscoutermarsh deleted the cursor/d1-default-sql-injection-23b4 branch July 23, 2026 12:28
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.

3 participants