From c6956cd550018029436fbb0392b00966793de149 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 12 Aug 2025 16:23:15 +0200 Subject: [PATCH 1/3] fix(core): Only consider ingest endpoint requests when checking `isSentryRequestUrl` --- packages/core/src/utils/isSentryRequestUrl.ts | 5 ++++- .../core/test/lib/utils/isSentryRequestUrl.test.ts | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/core/src/utils/isSentryRequestUrl.ts b/packages/core/src/utils/isSentryRequestUrl.ts index e93f61a5919a..68df860bef65 100644 --- a/packages/core/src/utils/isSentryRequestUrl.ts +++ b/packages/core/src/utils/isSentryRequestUrl.ts @@ -21,7 +21,10 @@ function checkTunnel(url: string, tunnel: string | undefined): boolean { } function checkDsn(url: string, dsn: DsnComponents | undefined): boolean { - return dsn ? url.includes(dsn.host) : false; + // Requests to Sentry's ingest endpoint must have a `sentry_key` in the query string + // This is equivalent to the public_key which is required in the DSN + // see https://develop.sentry.dev/sdk/overview/#parsing-the-dsn + return dsn ? url.includes(dsn.host) && !!url.match(/sentry_key/) : false; } function removeTrailingSlash(str: string): string { diff --git a/packages/core/test/lib/utils/isSentryRequestUrl.test.ts b/packages/core/test/lib/utils/isSentryRequestUrl.test.ts index 195e93493e98..75e8826b2470 100644 --- a/packages/core/test/lib/utils/isSentryRequestUrl.test.ts +++ b/packages/core/test/lib/utils/isSentryRequestUrl.test.ts @@ -4,12 +4,17 @@ import type { Client } from '../../../src/client'; describe('isSentryRequestUrl', () => { it.each([ - ['', 'sentry-dsn.com', '', false], - ['http://sentry-dsn.com/my-url', 'sentry-dsn.com', '', true], - ['http://sentry-dsn.com', 'sentry-dsn.com', '', true], + ['http://sentry-dsn.com/my-url?sentry_key=123', 'sentry-dsn.com', '', true], ['http://tunnel:4200', 'sentry-dsn.com', 'http://tunnel:4200', true], ['http://tunnel:4200', 'sentry-dsn.com', 'http://tunnel:4200/', true], ['http://tunnel:4200/', 'sentry-dsn.com', 'http://tunnel:4200', true], + ['http://tunnel:4200/', 'another-dsn.com', 'http://tunnel:4200', true], + + ['http://tunnel:4200/?sentry_key=123', 'another-dsn.com', '', false], + ['http://sentry-dsn.com/my-url', 'sentry-dsn.com', '', false], + ['http://sentry-dsn.com', 'sentry-dsn.com', '', false], + ['http://tunnel:4200/', 'another-dsn.com', 'http://tunnel:4200/sentry-tunnel', false], + ['', 'sentry-dsn.com', '', false], ['http://tunnel:4200/a', 'sentry-dsn.com', 'http://tunnel:4200', false], ])('works with url=%s, dsn=%s, tunnel=%s', (url: string, dsn: string, tunnel: string, expected: boolean) => { const client = { From e63873155805dfc624ad14e40047e9ebfa27ad48 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 12 Aug 2025 17:07:38 +0200 Subject: [PATCH 2/3] fix test --- packages/cloudflare/test/integrations/fetch.test.ts | 4 ++-- packages/core/src/utils/isSentryRequestUrl.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cloudflare/test/integrations/fetch.test.ts b/packages/cloudflare/test/integrations/fetch.test.ts index 724ff39c7dde..2a8f9cf6e718 100644 --- a/packages/cloudflare/test/integrations/fetch.test.ts +++ b/packages/cloudflare/test/integrations/fetch.test.ts @@ -101,8 +101,8 @@ describe('WinterCGFetch instrumentation', () => { expect(fetchInstrumentationHandlerCallback).toBeDefined(); const startHandlerData: HandlerDataFetch = { - fetchData: { url: 'https://dsn.ingest.sentry.io/1337', method: 'POST' }, - args: ['https://dsn.ingest.sentry.io/1337'], + fetchData: { url: 'https://dsn.ingest.sentry.io/1337?sentry_key=123', method: 'POST' }, + args: ['https://dsn.ingest.sentry.io/1337?sentry_key=123'], startTimestamp: Date.now(), }; fetchInstrumentationHandlerCallback(startHandlerData); diff --git a/packages/core/src/utils/isSentryRequestUrl.ts b/packages/core/src/utils/isSentryRequestUrl.ts index 68df860bef65..c2127cdfeb38 100644 --- a/packages/core/src/utils/isSentryRequestUrl.ts +++ b/packages/core/src/utils/isSentryRequestUrl.ts @@ -24,6 +24,8 @@ function checkDsn(url: string, dsn: DsnComponents | undefined): boolean { // Requests to Sentry's ingest endpoint must have a `sentry_key` in the query string // This is equivalent to the public_key which is required in the DSN // see https://develop.sentry.dev/sdk/overview/#parsing-the-dsn + // Therefore a request to the same host and with a `sentry_key` in the query string + // can be considered a request to the ingest endpoint return dsn ? url.includes(dsn.host) && !!url.match(/sentry_key/) : false; } From 21aac6b34e1ce3d390726377180d2200ed631c0b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 25 Aug 2025 11:30:39 +0200 Subject: [PATCH 3/3] improve check for DSN by extracging URL parts --- packages/core/src/utils/isSentryRequestUrl.ts | 12 +++-- .../test/lib/utils/isSentryRequestUrl.test.ts | 49 +++++++++++++------ .../integration/shouldFilterRequest.test.ts | 2 +- .../vercel-edge/test/wintercg-fetch.test.ts | 4 +- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/packages/core/src/utils/isSentryRequestUrl.ts b/packages/core/src/utils/isSentryRequestUrl.ts index c2127cdfeb38..8cda9404164a 100644 --- a/packages/core/src/utils/isSentryRequestUrl.ts +++ b/packages/core/src/utils/isSentryRequestUrl.ts @@ -1,5 +1,6 @@ import type { Client } from '../client'; import type { DsnComponents } from '../types-hoist/dsn'; +import { isURLObjectRelative, parseStringToURLObject } from './url'; /** * Checks whether given url points to Sentry server @@ -24,9 +25,14 @@ function checkDsn(url: string, dsn: DsnComponents | undefined): boolean { // Requests to Sentry's ingest endpoint must have a `sentry_key` in the query string // This is equivalent to the public_key which is required in the DSN // see https://develop.sentry.dev/sdk/overview/#parsing-the-dsn - // Therefore a request to the same host and with a `sentry_key` in the query string - // can be considered a request to the ingest endpoint - return dsn ? url.includes(dsn.host) && !!url.match(/sentry_key/) : false; + // Therefore, a request to the same host and with a `sentry_key` in the query string + // can be considered a request to the ingest endpoint. + const urlParts = parseStringToURLObject(url); + if (!urlParts || isURLObjectRelative(urlParts)) { + return false; + } + + return dsn ? urlParts.host.includes(dsn.host) && /(^|&|\?)sentry_key=/.test(urlParts.search) : false; } function removeTrailingSlash(str: string): string { diff --git a/packages/core/test/lib/utils/isSentryRequestUrl.test.ts b/packages/core/test/lib/utils/isSentryRequestUrl.test.ts index 75e8826b2470..806165fb52be 100644 --- a/packages/core/test/lib/utils/isSentryRequestUrl.test.ts +++ b/packages/core/test/lib/utils/isSentryRequestUrl.test.ts @@ -4,25 +4,46 @@ import type { Client } from '../../../src/client'; describe('isSentryRequestUrl', () => { it.each([ - ['http://sentry-dsn.com/my-url?sentry_key=123', 'sentry-dsn.com', '', true], - ['http://tunnel:4200', 'sentry-dsn.com', 'http://tunnel:4200', true], - ['http://tunnel:4200', 'sentry-dsn.com', 'http://tunnel:4200/', true], - ['http://tunnel:4200/', 'sentry-dsn.com', 'http://tunnel:4200', true], - ['http://tunnel:4200/', 'another-dsn.com', 'http://tunnel:4200', true], + ['http://sentry-dsn.com/my-url?sentry_key=123', 'sentry-dsn.com', ''], - ['http://tunnel:4200/?sentry_key=123', 'another-dsn.com', '', false], - ['http://sentry-dsn.com/my-url', 'sentry-dsn.com', '', false], - ['http://sentry-dsn.com', 'sentry-dsn.com', '', false], - ['http://tunnel:4200/', 'another-dsn.com', 'http://tunnel:4200/sentry-tunnel', false], - ['', 'sentry-dsn.com', '', false], - ['http://tunnel:4200/a', 'sentry-dsn.com', 'http://tunnel:4200', false], - ])('works with url=%s, dsn=%s, tunnel=%s', (url: string, dsn: string, tunnel: string, expected: boolean) => { + ['http://tunnel:4200', 'sentry-dsn.com', 'http://tunnel:4200'], + ['http://tunnel:4200', 'sentry-dsn.com', 'http://tunnel:4200/'], + ['http://tunnel:4200/', 'sentry-dsn.com', 'http://tunnel:4200'], + ['http://tunnel:4200/', 'another-dsn.com', 'http://tunnel:4200'], + ])('returns `true` for url=%s, dsn=%s, tunnel=%s', (url: string, dsn: string, tunnel: string) => { const client = { getOptions: () => ({ tunnel }), getDsn: () => ({ host: dsn }), } as unknown as Client; - // Works with client passed - expect(isSentryRequestUrl(url, client)).toBe(expected); + expect(isSentryRequestUrl(url, client)).toBe(true); + }); + + it.each([ + ['http://tunnel:4200/?sentry_key=123', 'another-dsn.com', ''], + ['http://sentry-dsn.com/my-url', 'sentry-dsn.com', ''], + ['http://sentry-dsn.com', 'sentry-dsn.com', ''], + ['http://sAntry-dsn.com/?sentry_key=123', 'sentry-dsn.com', ''], + ['http://sAntry-dsn.com/?sAntry_key=123', 'sAntry-dsn.com', ''], + ['/ingest', 'sentry-dsn.com', ''], + ['/ingest?sentry_key=123', 'sentry-dsn.com', ''], + ['/ingest', '', ''], + ['', '', ''], + ['', 'sentry-dsn.com', ''], + + ['http://tunnel:4200/', 'another-dsn.com', 'http://tunnel:4200/sentry-tunnel'], + ['http://tunnel:4200/a', 'sentry-dsn.com', 'http://tunnel:4200'], + ['http://tunnel:4200/a', '', 'http://tunnel:4200/'], + ])('returns `false` for url=%s, dsn=%s, tunnel=%s', (url: string, dsn: string, tunnel: string) => { + const client = { + getOptions: () => ({ tunnel }), + getDsn: () => ({ host: dsn }), + } as unknown as Client; + + expect(isSentryRequestUrl(url, client)).toBe(false); + }); + + it('handles undefined client', () => { + expect(isSentryRequestUrl('http://sentry-dsn.com/my-url?sentry_key=123', undefined)).toBe(false); }); }); diff --git a/packages/replay-internal/test/integration/shouldFilterRequest.test.ts b/packages/replay-internal/test/integration/shouldFilterRequest.test.ts index 355afe80060b..8e50fb75d608 100644 --- a/packages/replay-internal/test/integration/shouldFilterRequest.test.ts +++ b/packages/replay-internal/test/integration/shouldFilterRequest.test.ts @@ -20,6 +20,6 @@ describe('Integration | shouldFilterRequest', () => { it('should filter requests for Sentry ingest URLs', async () => { const { replay } = await mockSdk(); - expect(shouldFilterRequest(replay, 'https://03031aa.ingest.f00.f00/api/129312/')).toBe(true); + expect(shouldFilterRequest(replay, 'https://03031aa.ingest.f00.f00/api/129312/?sentry_key=123')).toBe(true); }); }); diff --git a/packages/vercel-edge/test/wintercg-fetch.test.ts b/packages/vercel-edge/test/wintercg-fetch.test.ts index c1605568de2e..7dc67d0131ea 100644 --- a/packages/vercel-edge/test/wintercg-fetch.test.ts +++ b/packages/vercel-edge/test/wintercg-fetch.test.ts @@ -102,8 +102,8 @@ describe('WinterCGFetch instrumentation', () => { expect(fetchInstrumentationHandlerCallback).toBeDefined(); const startHandlerData: HandlerDataFetch = { - fetchData: { url: 'https://dsn.ingest.sentry.io/1337', method: 'POST' }, - args: ['https://dsn.ingest.sentry.io/1337'], + fetchData: { url: 'https://dsn.ingest.sentry.io/1337?sentry_key=123', method: 'POST' }, + args: ['https://dsn.ingest.sentry.io/1337?sentry_key=123'], startTimestamp: Date.now(), }; fetchInstrumentationHandlerCallback(startHandlerData);