diff --git a/packages/node/src/integrations/utils/http.ts b/packages/node/src/integrations/utils/http.ts index 86902425914d..1ef1be2549b5 100644 --- a/packages/node/src/integrations/utils/http.ts +++ b/packages/node/src/integrations/utils/http.ts @@ -48,11 +48,17 @@ export function extractUrl(requestOptions: RequestOptions): string { !requestOptions.port || requestOptions.port === 80 || requestOptions.port === 443 ? '' : `:${requestOptions.port}`; // do not include search or hash in span descriptions, per https://develop.sentry.dev/sdk/data-handling/#structuring-data const path = requestOptions.pathname || '/'; - const authority = requestOptions.auth ? `${requestOptions.auth}@` : ''; + // always filter authority, see https://develop.sentry.dev/sdk/data-handling/#structuring-data + const authority = requestOptions.auth ? redactAuthority(requestOptions.auth) : ''; return `${protocol}//${authority}${hostname}${port}${path}`; } +function redactAuthority(auth: string): string { + const [user, password] = auth.split(':'); + return `${user ? '[Filtered]' : ''}:${password ? '[Filtered]' : ''}@`; +} + /** * Handle various edge cases in the span description (for spans representing http(s) requests). * @@ -123,8 +129,7 @@ export function urlToOptions(url: URL): RequestOptions { options.port = Number(url.port); } if (url.username || url.password) { - // always filter authority, see https://develop.sentry.dev/sdk/data-handling/#structuring-data - options.auth = '[Filtered]:[Filtered]'; + options.auth = `${url.username}:${url.password}`; } return options; } diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 555aad7f44b3..cf4f979d2753 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -215,18 +215,24 @@ describe('tracing', () => { expect(spans[1].data['http.fragment']).toEqual('learn-more'); }); - it('filters the authority (username and password) in span description', () => { - nock('http://username:password@dogs.are.great').get('/').reply(200); + it.each([ + ['user:pwd', '[Filtered]:[Filtered]@'], + ['user:', '[Filtered]:@'], + ['user', '[Filtered]:@'], + [':pwd', ':[Filtered]@'], + ['', ''], + ])('filters the authority %s in span description', (auth, redactedAuth) => { + nock(`http://${auth}@dogs.are.great`).get('/').reply(200); const transaction = createTransactionOnScope(); const spans = (transaction as unknown as Span).spanRecorder?.spans as Span[]; - http.get('http://username:password@dogs.are.great/'); + http.get(`http://${auth}@dogs.are.great/`); expect(spans.length).toEqual(2); // our span is at index 1 because the transaction itself is at index 0 - expect(spans[1].description).toEqual('GET http://[Filtered]:[Filtered]@dogs.are.great/'); + expect(spans[1].description).toEqual(`GET http://${redactedAuth}dogs.are.great/`); }); describe('Tracing options', () => {