Skip to content

fix(server-utils): Always sanitize inline literals out of db.query.text - #24089

Open
s1gr1d wants to merge 6 commits into
developfrom
sig/dc-database-query-data
Open

fix(server-utils): Always sanitize inline literals out of db.query.text#24089
s1gr1d wants to merge 6 commits into
developfrom
sig/dc-database-query-data

Conversation

@s1gr1d

@s1gr1d s1gr1d commented Sep 4, 2026

Copy link
Copy Markdown
Member

mysql, mysql2, pg, tedious, knex, the Nuxt DB helper and Cloudflare D1 wrote the raw statement to db.query.text, so inline literals like WHERE email = 'jane@example.com' shipped as span data. postgres-js and the mysql2 channel subscriber already sanitized; the rest did not.

Per OTel, query text may be collected by default only when literals are sanitized to ?, and parameterized text passes through untouched ($n and ? placeholders survive the sanitizer). Our own docs say databaseQueryData does not control db.query.text, so this is not gated on an option. Each site now sanitizes once and reuses the result for the attribute, the summary, and the non-streamed span name, which is less work than before (the summary path already sanitized). The D1 breadcrumb message gets the same treatment.

An earlier revision of this PR gated the raw text on databaseQueryData instead; that read the option as covering query text, which the docs explicitly carve out. Relay-side scrubbing for this attribute is being enabled separately in getsentry/sentry-conventions#625.

This resolves the two TODO: (fix) notes in the tedious integration test.

Fixes #24084

…erals

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.8 kB - -
@sentry/browser - with treeshaking flags 27.11 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27 kB - -
@sentry/browser (incl. Tracing) 49.22 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 49.22 kB - -
@sentry/browser (incl. Tracing, Profiling) 52.12 kB - -
@sentry/browser (incl. Tracing, Replay) 88.76 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.95 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 93.44 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 106.37 kB - -
@sentry/browser (incl. Feedback) 46.3 kB - -
@sentry/browser (incl. sendFeedback) 33.86 kB - -
@sentry/browser (incl. FeedbackAsync) 38.97 kB - -
@sentry/browser (incl. Metrics) 29.82 kB - -
@sentry/browser (incl. Logs) 30.09 kB - -
@sentry/browser (incl. Metrics & Logs) 30.75 kB - -
@sentry/react 30.55 kB - -
@sentry/react (incl. Tracing) 51.56 kB - -
@sentry/vue 36.05 kB - -
@sentry/vue (incl. Tracing) 51.48 kB - -
@sentry/svelte 28.83 kB - -
CDN Bundle 30.55 kB - -
CDN Bundle (incl. Tracing) 49.74 kB - -
CDN Bundle (incl. Logs, Metrics) 32.82 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 51.7 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.48 kB - -
CDN Bundle (incl. Tracing, Replay) 87.29 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 89.18 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 93.22 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 95.21 kB - -
CDN Bundle - uncompressed 90.46 kB - -
CDN Bundle (incl. Tracing) - uncompressed 148.17 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.03 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 154.14 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 226.29 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 267.76 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 273.71 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 281.46 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 287.4 kB - -
@sentry/nextjs (client) 54 kB - -
@sentry/sveltekit (client) 49.65 kB - -
@sentry/core/server 36.99 kB - -
@sentry/core/browser 13.55 kB - -
@sentry/node 127.85 kB +0.05% +60 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 81.61 kB - -
@sentry/node - without tracing 88.73 kB +0.03% +21 B 🔺
@sentry/node - without channel injection 107.09 kB +0.06% +58 B 🔺
@sentry/aws-serverless 97.12 kB +0.03% +26 B 🔺
@sentry/cloudflare (withSentry) - minified 201.99 kB +0.01% +8 B 🔺
@sentry/cloudflare (withSentry) 502.83 kB +0.04% +153 B 🔺

View base workflow run

Comment thread packages/nuxt/src/runtime/utils/instrumentDatabase.ts Outdated
…ery-data

# Conflicts:
#	packages/cloudflare/src/instrumentations/worker/instrumentD1.ts
#	packages/core/src/server.ts
#	packages/nuxt/src/runtime/utils/instrumentDatabase.ts
#	packages/server-utils/src/integrations/knex.ts
#	packages/server-utils/src/integrations/mysql.ts
#	packages/server-utils/src/integrations/mysql2/index.ts
#	packages/server-utils/src/integrations/postgres.ts
#	packages/server-utils/src/integrations/tedious.ts
@s1gr1d
s1gr1d marked this pull request as ready for review September 7, 2026 08:41
@s1gr1d
s1gr1d requested review from a team as code owners September 7, 2026 08:41
@s1gr1d
s1gr1d requested review from isaacs, mydea and nicohrubec and removed request for a team September 7, 2026 08:41
Per OTel, query text may only be collected by default when literals are
sanitized, and our docs say `databaseQueryData` does not control
`db.query.text`. Sanitize unconditionally at every site that attached
raw SQL and drop the `filterCollectedDbQueryText` gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@s1gr1d s1gr1d changed the title fix(core): Apply dataCollection.databaseQueryData to inline SQL literals fix(server-utils): Always sanitize inline literals out of db.query.text Sep 7, 2026
Comment thread packages/nuxt/src/runtime/utils/instrumentDatabase.ts
@Lms24 Lms24 linked an issue Sep 8, 2026 that may be closed by this pull request
3 tasks

@Lms24 Lms24 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.

Thanks for fixing this!

One observation from this PR, but totally out of scope for it: Looks like we have very few tests that check proper SQL queries, e.g. with filter conditions. Queries like SELECT 1 + 1 are not super representative here. I'm fine with them being sanitized as well although it's IMHO not as critically necessary as literals in filter clauses or insertion values WHERE col = ?.
That said, some of the tedious tests now show that literals in filter conditions are also sanitized, so all good!
Again, nothing to change for this PR but rather something we should tackle sometime to have more representative tests.

…ery-data

# Conflicts:
#	dev-packages/e2e-tests/test-applications/astro-6-cf-workers/tests/db.test.ts
#	dev-packages/e2e-tests/test-applications/astro-7/tests/db.test.ts
#	dev-packages/e2e-tests/test-applications/node-express-esm-loader/tests/server.test.ts
#	dev-packages/e2e-tests/test-applications/node-express-v5/tests/mysql.test.ts
Comment on lines 134 to +136
function createStartSpanOptions(query: string, type: D1QueryType): StartSpanOptions {
const querySummary = query ? getSqlQuerySummary(sanitizeSqlQuery(query)) : undefined;
const queryText = sanitizeSqlQuery(query);
const querySummary = query ? getSqlQuerySummary(queryText) : undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: The D1 instrumentation sets db.query.text to 'Unknown SQL Query' for empty queries, unlike other integrations which omit the attribute, due to an unguarded call to sanitizeSqlQuery.
Severity: LOW

Suggested Fix

Add a guard before calling sanitizeSqlQuery to align with other database integrations. Change the line to const queryText = query ? sanitizeSqlQuery(query) : undefined;. This will ensure that for empty or falsy queries, the db.query.text attribute is omitted from the span.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/cloudflare/src/instrumentations/worker/instrumentD1.ts#L134-L136

Potential issue: In the D1 instrumentation, `sanitizeSqlQuery(query)` is called without
first checking if the `query` is a falsy value, such as an empty string. The
`sanitizeSqlQuery` function returns the string `'Unknown SQL Query'` for falsy inputs.
Consequently, if `D1PreparedStatement.prepare("")` is ever called, the resulting span
will have a `db.query.text` attribute set to `'Unknown SQL Query'`. This behavior is
inconsistent with other database integrations in the codebase, which use a guard to
ensure the attribute is omitted for empty or undefined queries. While this scenario is
unlikely in normal usage, it represents a deviation from the established pattern.

Also affects:

  • packages/cloudflare/src/instrumentations/worker/instrumentD1.ts:126~126

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.

SQL integrations attach unsanitized db.query.text Tedious SQL statements are unparameterized

2 participants