Skip to content

fix(core): Apply dataCollection.urlQueryParams to url.full and url.query - #23061

Open
chargome wants to merge 6 commits into
fix/filter-url-query-utilfrom
fix/gate-url-query-params-on-data-collection
Open

fix(core): Apply dataCollection.urlQueryParams to url.full and url.query#23061
chargome wants to merge 6 commits into
fix/filter-url-query-utilfrom
fix/gate-url-query-params-on-data-collection

Conversation

@chargome

@chargome chargome commented Aug 5, 2026

Copy link
Copy Markdown
Member

urlQueryParams only applied to request.query_string and requestDataIntegration. Everywhere else, query strings went to Sentry unfiltered — a ?token=… was sent as-is.

We filter spans in one central place (captureSpan) instead of at the ~57 write sites, which span ~18 packages and mostly have no access to the client. One place also means a new integration cannot leak by forgetting to filter.

Breadcrumbs do not go through the span pipeline, so those are filtered separately.

closes #23049

@chargome chargome self-assigned this Aug 5, 2026
@chargome

chargome commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

bugborzer run

Comment thread packages/core/src/integrations/http/add-outgoing-request-breadcrumb.ts Outdated
Comment thread packages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 30.31 kB +0.64% +191 B 🔺
@sentry/browser - with treeshaking flags 28.52 kB +0.69% +195 B 🔺
@sentry/browser (incl. Tracing) 47.75 kB +0.45% +213 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 47.77 kB +0.48% +224 B 🔺
@sentry/browser (incl. Tracing, Profiling) 52.51 kB +0.42% +218 B 🔺
@sentry/browser (incl. Tracing, Replay) 87.14 kB +0.27% +228 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 76.56 kB +0.31% +234 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 91.83 kB +0.26% +231 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 104.49 kB +0.24% +241 B 🔺
@sentry/browser (incl. Feedback) 47.65 kB +0.43% +201 B 🔺
@sentry/browser (incl. sendFeedback) 35.15 kB +0.56% +193 B 🔺
@sentry/browser (incl. FeedbackAsync) 40.32 kB +0.54% +214 B 🔺
@sentry/browser (incl. Metrics) 31.39 kB +0.6% +187 B 🔺
@sentry/browser (incl. Logs) 31.61 kB +0.63% +196 B 🔺
@sentry/browser (incl. Metrics & Logs) 32.29 kB +0.58% +186 B 🔺
@sentry/react 32.12 kB +0.61% +193 B 🔺
@sentry/react (incl. Tracing) 50.01 kB +0.44% +219 B 🔺
@sentry/vue 35.4 kB +0.48% +166 B 🔺
@sentry/vue (incl. Tracing) 49.73 kB +0.39% +191 B 🔺
@sentry/svelte 30.34 kB +0.65% +194 B 🔺
CDN Bundle 32.29 kB +0.51% +161 B 🔺
CDN Bundle (incl. Tracing) 48.08 kB +0.56% +265 B 🔺
CDN Bundle (incl. Logs, Metrics) 33.85 kB +0.52% +174 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 49.4 kB +0.45% +217 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 73.19 kB +0.24% +174 B 🔺
CDN Bundle (incl. Tracing, Replay) 85.68 kB +0.28% +235 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.97 kB +0.23% +197 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 91.5 kB +0.25% +227 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.79 kB +0.22% +203 B 🔺
CDN Bundle - uncompressed 95.78 kB +0.54% +506 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 143.39 kB +0.44% +615 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 100.4 kB +0.51% +506 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 147.37 kB +0.42% +615 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 225.1 kB +0.23% +506 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 262.65 kB +0.24% +615 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 266.61 kB +0.24% +615 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 276.35 kB +0.23% +615 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 280.31 kB +0.22% +615 B 🔺
@sentry/nextjs (client) 52.56 kB +0.39% +200 B 🔺
@sentry/sveltekit (client) 48.2 kB +0.46% +216 B 🔺
@sentry/core/server 65.69 kB +0.21% +132 B 🔺
@sentry/core/browser 51.97 kB +0.28% +144 B 🔺
@sentry/node 120.65 kB +1.15% +1.37 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 0 B added added
@sentry/node - without tracing 84.23 kB +0.17% +137 B 🔺
@sentry/aws-serverless 92.75 kB +0.16% +144 B 🔺
@sentry/cloudflare (withSentry) - minified 218.3 kB +2.71% +5.75 kB 🔺
@sentry/cloudflare (withSentry) 538.42 kB +2.59% +13.58 kB 🔺

View base workflow run

@chargome

chargome commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

bugbot 📿

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b82b19e. Configure here.

chargome and others added 2 commits August 5, 2026 16:42
…rl.query`

`urlQueryParams` only applied to `request.query_string` and
`requestDataIntegration`. Everywhere else, query strings went to Sentry
unfiltered.

Spans are filtered in one central place (`captureSpan`) instead of at the ~57
write sites, which span ~18 packages and mostly have no access to the client.
The pass runs after the `processSpan` hooks so integration-set attributes are
covered, and before `beforeSendSpan` since explicitly user-attached data is not
gated by `dataCollection`.

Breadcrumbs do not go through the span pipeline, so those are filtered
separately at write time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Breadcrumb query filtering had no test coverage, so re-leaking a token would not
have failed CI. Adds cases for the default denylist, off mode, allowList and
extra deny terms on both outgoing request breadcrumb paths. The node fetch path
needs its own file because the existing test module mocks `getClient` without
`getDataCollectionOptions`.

Also fixes two span tests that claimed more than they asserted: one checks the
span name is untouched but never looked at it, and the other claimed to cover
attributes set after the span starts while passing them in at creation. The
latter now registers a `processSpan` subscriber, mirroring how
`requestDataIntegration` sets `url.full`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chargome
chargome force-pushed the fix/gate-url-query-params-on-data-collection branch from b82b19e to 0e39522 Compare August 5, 2026 14:42
@chargome
chargome marked this pull request as ready for review August 5, 2026 15:02
@chargome
chargome requested a review from a team as a code owner August 5, 2026 15:02
@chargome
chargome requested review from andreiborza, msonnb, nicohrubec and stephanie-anderson and removed request for a team and stephanie-anderson August 5, 2026 15:02

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

lgtm, costs some bundle size but I think it's worth it given the blast radius

@chargome

chargome commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

@nicohrubec yeah this was the tradeoff to adding it centrally to captureSpan

Comment thread packages/core/src/utils/data-collection/filterUrlSpanAttributes.ts Outdated
Comment thread packages/core/src/utils/data-collection/filterUrlSpanAttributes.ts Outdated
Comment thread packages/core/src/tracing/spans/captureSpan.ts Outdated
Comment thread packages/core/src/utils/data-collection/filterUrlSpanAttributes.ts Outdated
@chargome
chargome marked this pull request as draft August 6, 2026 11:00
Replace the central `captureSpan` filtering pass with a `filterCollectedUrl`
helper that every instrumentation site calls when it records a URL.

The central pass could not tell an SDK-set attribute from one a user set
themselves, so a `url.full` passed to `span.setAttribute()` was filtered too.
`dataCollection` is only meant to gate automatically collected data. Routing the
SDK's own URLs through a helper makes provenance structural: a URL the user
attaches never passes through it, so it is left alone.

The helper reads `urlQueryParams` from the client itself, so call sites only wrap
the value and `getHttpSpanDetailsFromUrlObject` keeps its signature. Attributes
copied in from third-party OTel instrumentation are filtered where the SDK
copies them, in `inferSpanData`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread packages/core/src/integrations/http/server-subscription.ts
`http.target` is the deprecated alias of `url.full` and carries the same
path and query string, so it bypassed `dataCollection.urlQueryParams` and sent
sensitive query params even though `url.full` was filtered.

Three sites were affected: both incoming server span integrations build it as
`pathname + search`, and the outgoing client span uses `request.path`, which
Node populates with the query string included.

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

chargome commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

@Lms24 @nicohrubec I updated the pr to gate at the internal call sites so now it's actually spec compliant. For regression safety we can add a custom lint rule maybe, need to look into it.

@chargome
chargome marked this pull request as ready for review August 6, 2026 13:30
@chargome
chargome requested review from a team as code owners August 6, 2026 13:30
@chargome
chargome requested review from Lms24, mydea, nicohrubec and s1gr1d and removed request for a team, mydea and s1gr1d August 6, 2026 13:30
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.

Audit URL data collection against the dataCollection spec (urlQueryParams)

3 participants