diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts index b4837ecbb687..48da49383823 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts @@ -35,3 +35,18 @@ sentryTest('httpContextIntegration filters sensitive query params in the URL', a expect(errorEvent.request?.url).toBe(`${url}?jwt=[Filtered]&page=1`); }); + +sentryTest( + 'httpContextIntegration filters sensitive query params in the Referer header', + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const errorEventPromise = waitForErrorRequest(page); + + await page.goto(url, { referer: `${url}?jwt=sensitive-jwt-value&page=1` }); + + const errorEvent = envelopeRequestParser(await errorEventPromise); + + expect(errorEvent.request?.headers?.Referer).toBe(`${url}?jwt=[Filtered]&page=1`); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts index 2408a08b6e53..2318b7c25e76 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts @@ -15,10 +15,11 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - await page.goto(`${url}?jwt=sensitive-jwt-value&page=1`); + await page.goto(`${url}?jwt=sensitive-jwt-value&page=1`, { referer: `${url}?jwt=sensitive-jwt-value&page=2` }); const eventData = envelopeRequestParser(await pageloadRequestPromise); expect(eventData.request?.url).toBe(`${url}?jwt=[Filtered]&page=1`); + expect(eventData.request?.headers?.Referer).toBe(`${url}?jwt=[Filtered]&page=2`); expect(eventData.spans?.find(({ op }) => op === 'browser.request')?.description).toBe( `${url}?jwt=[Filtered]&page=1`, ); diff --git a/packages/browser/src/helpers.ts b/packages/browser/src/helpers.ts index 5e31528b6dea..741ee08f15d4 100644 --- a/packages/browser/src/helpers.ts +++ b/packages/browser/src/helpers.ts @@ -1,9 +1,10 @@ -import type { Mechanism, WrappedFunction } from '@sentry/core/browser'; +import type { Client, Mechanism, WrappedFunction } from '@sentry/core/browser'; import { addExceptionMechanism, addExceptionTypeValue, addNonEnumerableProperty, captureException, + filterCollectedUrl, getLocationHref, getOriginalFunction, GLOBAL_OBJ, @@ -187,15 +188,18 @@ export function wrap( /** * Get HTTP request data from the current page. + * + * The `Referer` header has `dataCollection.urlQueryParams` of `client` applied. `url` is unfiltered, so + * callers must pass it through `filterCollectedUrl`. */ -export function getHttpRequestData(): { url: string; headers: Record } { +export function getHttpRequestData(client: Client): { url: string; headers: Record } { // grab as much info as exists and add it to the event const url = getLocationHref(); const { referrer } = WINDOW.document || {}; const { userAgent } = WINDOW.navigator || {}; const headers = { - ...(referrer && { Referer: referrer }), + ...(referrer && { Referer: filterCollectedUrl(referrer, client) }), ...(userAgent && { 'User-Agent': userAgent }), }; const request = { diff --git a/packages/browser/src/integrations/httpcontext.ts b/packages/browser/src/integrations/httpcontext.ts index d389e9236399..9bb62a4b1b2c 100644 --- a/packages/browser/src/integrations/httpcontext.ts +++ b/packages/browser/src/integrations/httpcontext.ts @@ -15,7 +15,7 @@ export const httpContextIntegration = defineIntegration(() => { return; } - const reqData = getHttpRequestData(); + const reqData = getHttpRequestData(client); const headers = { ...reqData.headers, ...event.request?.headers, @@ -29,13 +29,13 @@ export const httpContextIntegration = defineIntegration(() => { }; }, - processSpan(span) { + processSpan(span, client) { // if none of the information we want exists, don't bother if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) { return; } - const reqData = getHttpRequestData(); + const reqData = getHttpRequestData(client); safeSetSpanJSONAttributes(span, { // This attribute is used by the "Filter out events from legacy browsers and crawlers" features on the Sentry backend. diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts index b19d569654e3..405dec30f077 100644 --- a/packages/browser/src/tracing/browserTracingIntegration.ts +++ b/packages/browser/src/tracing/browserTracingIntegration.ts @@ -636,7 +636,7 @@ export const browserTracingIntegration = ((options: Partial { expect(event.request?.url).toBe('https://example.com/?jwt=[Filtered]&page=1'); }); + it('filters sensitive query params in the Referer header attached to events', () => { + const client = new BrowserClient(getDefaultBrowserClientOptions()); + const event: Event = {}; + + document.referrer = 'https://example.com/?jwt=baz&page=1'; + try { + httpContextIntegration().preprocessEvent!(event, {}, client); + } finally { + document.referrer = 'https://example.com'; + } + + expect(event.request?.headers?.Referer).toBe('https://example.com/?jwt=[Filtered]&page=1'); + }); + + it('removes the query from the Referer header attached to events when `urlQueryParams` is disabled', () => { + const client = new BrowserClient(getDefaultBrowserClientOptions({ dataCollection: { urlQueryParams: false } })); + const event: Event = {}; + + document.referrer = 'https://example.com/?jwt=baz&page=1'; + try { + httpContextIntegration().preprocessEvent!(event, {}, client); + } finally { + document.referrer = 'https://example.com'; + } + + expect(event.request?.headers?.Referer).toBe('https://example.com/'); + }); + + it('filters sensitive query params in the referer attribute of segment spans', () => { + const client = new BrowserClient(getDefaultBrowserClientOptions()); + const span: Partial = { + is_segment: true, + attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload' }, + }; + + document.referrer = 'https://example.com/?jwt=baz&page=1'; + try { + httpContextIntegration().processSpan!(span as StreamedSpanJSON, client); + } finally { + document.referrer = 'https://example.com'; + } + + expect(span.attributes?.['http.request.header.referer']).toBe('https://example.com/?jwt=[Filtered]&page=1'); + }); + it('leaves a user-supplied event URL untouched', () => { const client = new BrowserClient(getDefaultBrowserClientOptions()); const event: Event = { request: { url: 'https://example.com/?jwt=user-set' } };