Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/cloudflare/test/integrations/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Comment on lines +104 to +105
Copy link
Member Author

@Lms24 Lms24 Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this perfectly illustrates the consequences of this change. Previously, this URL was considered a "Sentry request", now we have to add the query parameter for it to still be considered one. This is likely just because the test was "incomplete" before but it does open up potential for a few false negatives. Thoughts anyone?

We can further refine the URL matching logic as we want

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds reasonable to me!

startTimestamp: Date.now(),
};
fetchInstrumentationHandlerCallback(startHandlerData);
Expand Down
13 changes: 12 additions & 1 deletion packages/core/src/utils/isSentryRequestUrl.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -21,7 +22,17 @@ 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
// 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 {
Expand Down
46 changes: 36 additions & 10 deletions packages/core/test/lib/utils/isSentryRequestUrl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,46 @@ 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://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/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://sentry-dsn.com/my-url?sentry_key=123', 'sentry-dsn.com', ''],

['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;

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;

// Works with client passed
expect(isSentryRequestUrl(url, client)).toBe(expected);
expect(isSentryRequestUrl(url, client)).toBe(false);
});

it('handles undefined client', () => {
expect(isSentryRequestUrl('http://sentry-dsn.com/my-url?sentry_key=123', undefined)).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
4 changes: 2 additions & 2 deletions packages/vercel-edge/test/wintercg-fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading