From 1aa28916bd232f7b4c54589072bfd730d08c3df4 Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Mon, 12 Oct 2026 13:03:21 +0200 Subject: [PATCH] fix(browser): Apply `urlQueryParams` to breadcrumb and element timing URLs The fetch, XHR and navigation breadcrumbs and the `ui.element.url` attribute of `elementTimingIntegration` still sent sensitive query values such as `?token=...` to Sentry. Co-Authored-By: Claude Opus 5.5 --- .../fetch/withSensitiveQuery/subject.js | 3 ++ .../fetch/withSensitiveQuery/test.ts | 25 ++++++++++++++++ .../history/withSensitiveQuery/subject.js | 4 +++ .../history/withSensitiveQuery/test.ts | 29 +++++++++++++++++++ .../xhr/withSensitiveQuery/subject.js | 10 +++++++ .../xhr/withSensitiveQuery/test.ts | 25 ++++++++++++++++ .../src/performance/elementTiming.ts | 4 +-- .../test/performance/elementTiming.test.ts | 4 +-- .../browser/src/integrations/breadcrumbs.ts | 10 ++++--- 9 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/test.ts diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/subject.js new file mode 100644 index 000000000000..e8ce3f2f4d3b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/subject.js @@ -0,0 +1,3 @@ +fetch('http://sentry-test.io/foo?token=sensitive-token-value&page=1').then(() => { + Sentry.captureException('test error'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/test.ts new file mode 100644 index 000000000000..f4794f772eec --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/withSensitiveQuery/test.ts @@ -0,0 +1,25 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('filters sensitive query params in the fetch breadcrumb URL', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo?*', route => route.fulfill({ status: 200, body: '{}' })); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.breadcrumbs).toEqual([ + { + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://sentry-test.io/foo?token=[Filtered]&page=1', + }, + }, + ]); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/subject.js new file mode 100644 index 000000000000..3a8a0b883ce7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/subject.js @@ -0,0 +1,4 @@ +history.pushState({}, '', '/foo?jwt=sensitive-jwt-value&a=1'); +history.pushState({}, '', '/bar'); + +Sentry.captureException('test exception'); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/test.ts new file mode 100644 index 000000000000..95d073806165 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/withSensitiveQuery/test.ts @@ -0,0 +1,29 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('filters sensitive query params in navigation breadcrumbs', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.breadcrumbs).toEqual([ + { + category: 'navigation', + data: { + from: '/index.html', + to: '/foo?jwt=[Filtered]&a=1', + }, + timestamp: expect.any(Number), + }, + { + category: 'navigation', + data: { + from: '/foo?jwt=[Filtered]&a=1', + to: '/bar', + }, + timestamp: expect.any(Number), + }, + ]); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/subject.js new file mode 100644 index 000000000000..4599aa27ad14 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/subject.js @@ -0,0 +1,10 @@ +const xhr = new XMLHttpRequest(); + +xhr.open('GET', 'http://sentry-test.io/foo?token=sensitive-token-value&page=1'); +xhr.send(); + +xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + Sentry.captureException('test error'); + } +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/test.ts new file mode 100644 index 000000000000..ccb4c1206139 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/withSensitiveQuery/test.ts @@ -0,0 +1,25 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('filters sensitive query params in the XHR breadcrumb URL', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo?*', route => route.fulfill({ status: 200, body: '{}' })); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.breadcrumbs).toEqual([ + { + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://sentry-test.io/foo?token=[Filtered]&page=1', + }, + }, + ]); +}); diff --git a/packages/browser-utils/src/performance/elementTiming.ts b/packages/browser-utils/src/performance/elementTiming.ts index 6e2ac6e4e52f..5b8e5f01218e 100644 --- a/packages/browser-utils/src/performance/elementTiming.ts +++ b/packages/browser-utils/src/performance/elementTiming.ts @@ -1,5 +1,5 @@ import type { IntegrationFn } from '@sentry/core'; -import { browserPerformanceTimeOrigin, defineIntegration, metrics } from '@sentry/core'; +import { browserPerformanceTimeOrigin, defineIntegration, filterCollectedUrl, metrics } from '@sentry/core'; import { addPerformanceInstrumentationHandler } from '../instrumentation/performanceObserver'; import { getBrowserPerformanceAPI } from './utils'; @@ -58,7 +58,7 @@ const _elementTimingIntegration = (() => { } if (elementEntry.url) { - metricAttributes['ui.element.url'] = elementEntry.url; + metricAttributes['ui.element.url'] = filterCollectedUrl(elementEntry.url); } if (elementEntry.naturalWidth) { diff --git a/packages/browser-utils/test/performance/elementTiming.test.ts b/packages/browser-utils/test/performance/elementTiming.test.ts index 26ca3c8ee178..54fc3fba12da 100644 --- a/packages/browser-utils/test/performance/elementTiming.test.ts +++ b/packages/browser-utils/test/performance/elementTiming.test.ts @@ -98,7 +98,7 @@ describe('elementTimingIntegration', () => { identifier: 'hero-image', id: 'img1', element: { tagName: 'IMG' }, - url: 'https://example.com/hero.jpg', + url: 'https://example.com/hero.jpg?token=abc&w=1920', naturalWidth: 1920, naturalHeight: 1080, } as unknown as PerformanceEntry, @@ -112,7 +112,7 @@ describe('elementTimingIntegration', () => { 'ui.element.paint_type': 'image-paint', 'ui.element.id': 'img1', 'ui.element.type': 'img', - 'ui.element.url': 'https://example.com/hero.jpg', + 'ui.element.url': 'https://example.com/hero.jpg?token=[Filtered]&w=1920', 'ui.element.width': 1920, 'ui.element.height': 1080, }; diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index e28f2e0c0d12..aa7f438d770e 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -14,6 +14,7 @@ import { addFetchInstrumentationHandler, debug, defineIntegration, + filterCollectedUrl, getBreadcrumbLogLevelFromHttpStatusCode, getClient, getEventDescription, @@ -192,7 +193,7 @@ function _getXhrBreadcrumbHandler(client: Client): (handlerData: HandlerDataXhr) const data: XhrBreadcrumbData = { method, - url, + url: filterCollectedUrl(url), status_code, }; @@ -247,7 +248,7 @@ function _getFetchBreadcrumbHandler(client: Client): (handlerData: HandlerDataFe const breadcrumb = { category: 'fetch', - data: handlerData.fetchData, + data: { ...handlerData.fetchData, url: filterCollectedUrl(handlerData.fetchData.url) }, level: 'error', type: 'http', } satisfies Breadcrumb; @@ -259,6 +260,7 @@ function _getFetchBreadcrumbHandler(client: Client): (handlerData: HandlerDataFe const response = handlerData.response as Response | undefined; const data: FetchBreadcrumbData = { ...handlerData.fetchData, + url: filterCollectedUrl(handlerData.fetchData.url), status_code: response?.status, }; @@ -315,8 +317,8 @@ function _getHistoryBreadcrumbHandler(client: Client): (handlerData: HandlerData addBreadcrumb({ category: 'navigation', data: { - from, - to, + from: filterCollectedUrl(from), + to: filterCollectedUrl(to), }, }); };