Skip to content

fix(core): Track quote state when sanitizing SQL literals - #23659

Open
Lms24 wants to merge 3 commits into
lms/feat-server-utils-low-card-db-span-names2from
lms/fix-core-sql-sanitizer-quote-state
Open

fix(core): Track quote state when sanitizing SQL literals#23659
Lms24 wants to merge 3 commits into
lms/feat-server-utils-low-card-db-span-names2from
lms/fix-core-sql-sanitizer-quote-state

Conversation

@Lms24

@Lms24 Lms24 commented Aug 26, 2026

Copy link
Copy Markdown
Member

This PR fixes a couple of edge cases, most prominently that double quotes can be used as string literals in MySQL ("...").

Changes:

statement (as the driver publishes it) db.query.summary before after
SELECT * FROM users WHERE name = 'O\'Brien from ACME' SELECT users ACME' SELECT users
SELECT * FROM users WHERE bio = "i come from Berlin and join clubs" SELECT users Berlin clubs" SELECT users
INSERT INTO t (c) VALUES ("select from s3cret-token") INSERT t select s3cret-token" INSERT t
SELECT * FROM t WHERE a = 'from secret--x' SELECT t secret SELECT t

Comments and literals are now stripped in one scanning pass rather than by regexes that each decide quote state on their own.

This also moves the sql sanitization logic to the utils/sql.ts file since it's now reused across multiple sql libraries (msql, postgres, cloudflare d1)

Stacked on #23601

`_sanitizeSqlQuery` stripped string literals with a single regex over `'...'`, which misses
two shapes MySQL produces. Double-quoted values are string literals in MySQL unless
`ANSI_QUOTES` is set, and backslashes escape the next character unless
`NO_BACKSLASH_ESCAPES` is set — and `mysql`/`mysql2` escape inlined values with backslashes,
so `WHERE name = ?` with `O'Brien` arrives as `'O\'Brien'`. In both cases the value survived
into `db.query.text`, and `getSqlQuerySummary` then read any `from`/`join` inside it as a
table name, putting it in `db.query.summary` and — with span streaming — in the span name.

Replace the literal and comment regexes with a single scanning pass, so quote state and
comment state are no longer decided independently: `--` inside a literal no longer truncates
it (which also leaked, in every dialect), and a literal's `X`/`B`/`E` prefix collapses into
the same `?`. Quoted identifiers are still preserved, since the summary is built from them.
The dialect is a parameter because the same characters mean different things per driver:
`"` quotes identifiers in PostgreSQL and SQLite, and backslash is literal there.

PostgreSQL dollar-quoted strings (`$$...$$`) are still unhandled — telling them apart from
`$n` placeholders is ambiguous, and existing behavior for `$1$2$3` is pinned by tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.57 kB added added
@sentry/browser - with treeshaking flags 26.92 kB added added
@sentry/browser - with treeshaking flags tracing without tracing 26.82 kB added added
@sentry/browser (incl. Tracing) 48.58 kB added added
@sentry/browser (incl. Tracing + Span Streaming) 48.6 kB added added
@sentry/browser (incl. Tracing, Profiling) 51.51 kB added added
@sentry/browser (incl. Tracing, Replay) 88.06 kB added added
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.46 kB added added
@sentry/browser (incl. Tracing, Replay with Canvas) 92.76 kB added added
@sentry/browser (incl. Tracing, Replay, Feedback) 105.47 kB added added
@sentry/browser (incl. Feedback) 45.81 kB added added
@sentry/browser (incl. sendFeedback) 33.36 kB added added
@sentry/browser (incl. FeedbackAsync) 38.46 kB added added
@sentry/browser (incl. Metrics) 29.51 kB added added
@sentry/browser (incl. Logs) 29.8 kB added added
@sentry/browser (incl. Metrics & Logs) 30.43 kB added added
@sentry/react 30.31 kB added added
@sentry/react (incl. Tracing) 50.79 kB added added
@sentry/vue 35.69 kB added added
@sentry/vue (incl. Tracing) 50.82 kB added added
@sentry/svelte 28.59 kB added added
CDN Bundle 30.36 kB added added
CDN Bundle (incl. Tracing) 49.07 kB added added
CDN Bundle (incl. Logs, Metrics) 32.56 kB added added
CDN Bundle (incl. Tracing, Logs, Metrics) 50.95 kB added added
CDN Bundle (incl. Replay, Logs, Metrics) 73 kB added added
CDN Bundle (incl. Tracing, Replay) 86.58 kB added added
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.46 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback) 92.34 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.27 kB added added
CDN Bundle - uncompressed 89.97 kB added added
CDN Bundle (incl. Tracing) - uncompressed 146.69 kB added added
CDN Bundle (incl. Logs, Metrics) - uncompressed 96.26 kB added added
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 152.38 kB added added
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 225.42 kB added added
CDN Bundle (incl. Tracing, Replay) - uncompressed 266.18 kB added added
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 271.86 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 279.88 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 285.55 kB added added
@sentry/nextjs (client) 53.33 kB added added
@sentry/sveltekit (client) 49.03 kB added added
@sentry/core/server 65.45 kB added added
@sentry/core/browser 52.35 kB added added
@sentry/node 123.04 kB added added
@sentry/node/import (ESM hook with diagnostics-channel injection) 85.22 kB added added
@sentry/node - without tracing 87.96 kB added added
@sentry/aws-serverless 96.14 kB added added
@sentry/cloudflare (withSentry) - minified 200.18 kB added added
@sentry/cloudflare (withSentry) 497.32 kB added added

@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 and found 1 potential issue.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit cb2b8f7. Configure here.

Comment thread packages/core/src/integrations/postgresjs.ts Outdated
Lms24 and others added 2 commits August 26, 2026 16:37
`_sanitizeSqlQuery` lived in the postgres.js integration, but it is dialect-generic and now
serves the mysql, mysql2, postgres, postgres.js and Cloudflare D1 paths. Move it next to
`getSqlQuerySummary`, which is its only downstream consumer and already lives there, so the
sanitize-then-summarize pipeline reads as one unit and D1 no longer reaches into a postgres
integration module for it.

Rename `_sanitizeSqlQuery` to `sanitizeSqlQuery` to match its neighbour; the public
`_INTERNAL_sanitizeSqlQuery` alias is unchanged, so nothing outside core moves. Tests move
with it, and the leak regressions can now assert the summary directly instead of only the
sanitized statement. No behavior change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24 marked this pull request as ready for review August 26, 2026 16:21
@Lms24
Lms24 requested a review from a team as a code owner August 26, 2026 16:21
@Lms24
Lms24 requested review from JPeer264, msonnb and stephanie-anderson and removed request for a team and stephanie-anderson August 26, 2026 16:21

@JPeer264 JPeer264 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

before

SELECT * FROM lgtm WHERE feedback = "Nice work"

after

SELECT lgtm

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