Skip to content

fix: sanitize page URLs recorded into telemetry - #729

Open
mayberryzane wants to merge 2 commits into
mainfrom
sanitize-page-urls-in-telemetry
Open

fix: sanitize page URLs recorded into telemetry#729
mayberryzane wants to merge 2 commits into
mainfrom
sanitize-page-urls-in-telemetry

Conversation

@mayberryzane

@mayberryzane mayberryzane commented Aug 14, 2026

Copy link
Copy Markdown

Summary

ErrorMessage.url was set from window.location.href verbatim, so any secret sitting in the query string or fragment (OAuth access_token/id_token, magic links, password reset tokens) was uploaded to the backend with the error event.

sanitizeUrl already redacts sensitive query params and strips token-bearing fragments — that landed in #595 — but it was only ever wired into the network listener. The error path is separate code (client/index.tsx, not network-sanitizer.ts) and was never covered.

This adds sanitizedLocationHref() next to sanitizeUrl and routes every telemetry-bound page URL through it.

What changed

Error URLs

  • Highlight._recordErrorMessageclient/index.tsx
  • ObserveSDK error recording, both call sites — sdk/observe.ts
  • ErrorListenerwindow.onerror and unhandled rejections
  • the console.error capture in FirstLoadListeners

Span attributes and names

  • url.full and event.url on user-interaction spans
  • url.full on ld.track spans
  • url.full, page_view.url, page_view.previous_url on the page-view span — this one fires on every SPA navigation, including OAuth callbacks
  • the history-navigation span name built in _updateInteractionName

Metric group attributes across viewport, device, web-vital, performance, and network-performance gauges, in both SDK generations (client/index.tsx and sdk/observe.ts) plus H.recordMetric

Custom events and session properties

  • Navigate / Reload custom events
  • the Referrer custom event and referrer session property (document.referrer carries the previous page's query string)
  • the jank listener's emitted newLocation

Deliberately unchanged

  • Comparisons and change detection still read the raw window.location.href (LocationChangeInstrumentation._lastUrl, jankState.location, the pre/post URLs in _patchHistoryMethod). Sanitizing those would let redaction collapse two distinct URLs into one and silently drop legitimate page views. Only the recorded value is sanitized.
  • window.location.pathname sites — no query or fragment to leak.
  • SegmentIntegrationListener's initial callback(window.location.href). Its consumers branch on obj.type, so a bare string is dropped and never recorded.

How did you test this change?

  • New error-listener.test.ts drives the real window.onerror handler with ?access_token=… and #id_token=… in the URL and asserts the secret is absent from the recorded ErrorMessage.url, plus a case confirming benign paths, params, and anchors survive.
  • New sanitizedLocationHref cases alongside the existing sanitizeUrl suite.
  • Verified the new tests are genuine regression coverage: stashed the fix, confirmed they fail on the unsanitized URL, restored, confirmed they pass.
  • yarn turbo run lint enforce-size test --filter highlight.run — 25 files, 452 tests pass; bundle 169.83 kB brotli against the 256 kB limit.
  • yarn format-check and yarn dedupe --check clean.

Are there any deployment considerations?

Patch-level. One behavior change worth flagging: metric group values and page-view URLs are now redacted, so aggregation keys shift for any URL that contained sensitive params. Metrics from such URLs will group under the redacted form rather than the raw one.

🤖 Generated with Claude Code


Note

Overview
Stops the browser SDK from uploading OAuth tokens, magic links, and similar secrets that live in the page query string or fragment.

Adds sanitizedLocationHref() (wrapping existing sanitizeUrl) and uses it for every telemetry-bound page URL: error url fields, metric groups, span attributes (url.full, page-view, user-interaction, ld.track), Navigate/Reload/Referrer custom events, and jank newLocation. URL comparisons still use the raw href so redaction cannot collapse distinct navigations.

Also rebuilds history-patch URLs as pathname + search + hash so query tokens are not treated as part of the fragment. Tests cover error URLs, sanitizedLocationHref, and history span names. Metric groups for URLs that contained secrets will now aggregate under the redacted form.

Reviewed by Cursor Bugbot for commit b9a95bb. Bugbot is set up for automated code reviews on this repo. Configure here.

@mayberryzane
mayberryzane force-pushed the sanitize-page-urls-in-telemetry branch from 770cc64 to dc66458 Compare August 14, 2026 18:53
@mayberryzane
mayberryzane marked this pull request as ready for review August 14, 2026 19:13
@mayberryzane
mayberryzane requested a review from a team as a code owner August 14, 2026 19:13

@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 dc66458. Configure here.

Comment thread sdk/highlight-run/src/client/otel/user-interaction.ts
mayberryzane and others added 2 commits August 20, 2026 19:24
`ErrorMessage.url` was set from `window.location.href` verbatim, so any
secret in the query string or fragment — OAuth `access_token`/`id_token`,
magic links, password reset tokens — was uploaded with the error event.

`sanitizeUrl` already redacts sensitive query params and strips
token-bearing fragments (#595), but it was only wired into the network
listener, never the error path.

Add `sanitizedLocationHref()` and route every telemetry-bound page URL
through it:

- error URLs: `Highlight._recordErrorMessage`, `ObserveSDK` (x2),
  `ErrorListener` (window.onerror / unhandled rejection), and the
  `console.error` capture in `FirstLoadListeners`
- span attributes: `url.full` on user-interaction and `ld.track` spans,
  `event.url`, and the page-view span's `url.full` /
  `page_view.url` / `page_view.previous_url`
- the history-navigation span name in `_updateInteractionName`
- metric `group` attributes across viewport, device, web-vital,
  performance, and network-performance gauges in both SDK generations
- `Navigate` / `Reload` / `Referrer` custom events and the `referrer`
  session property
- the jank listener's emitted `newLocation`

Change detection and comparisons keep reading the raw
`window.location.href` so redaction never collapses two distinct URLs;
only the recorded value is sanitized. `window.location.pathname` sites
are left alone — they carry no query or fragment.

Note: metric `group` values and page-view URLs are now redacted, so
aggregation keys change for URLs that contained sensitive params.
`_patchHistoryMethod` composed the URL as `pathname + hash + search`,
which is not a well-formed URL. Passing it to `sanitizeUrl` made the URL
parser absorb the query into the fragment, defeating redaction in both
directions:

- a token-bearing fragment survived whenever a benign query was also
  present, because `sanitizeFragment` split on the `?` that had been
  appended after the hash and only inspected the trailing query params
- sensitive query keys that are not also fragment-sensitive (`sig`,
  `signature`, `awsaccesskeyid`) were never redacted when a fragment was
  present, since `urlObject.search` came back empty

Composing `pathname + search + hash` fixes both. The comparison that
gates the rename still uses the raw values and both sides use the same
order, so change detection is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Vadman97
Vadman97 force-pushed the sanitize-page-urls-in-telemetry branch from 09f73d4 to b9a95bb Compare August 20, 2026 23:24
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