diff --git a/packages/telemetry/browser-telemetry/__tests__/filters/defaultUrlFilter.test.ts b/packages/telemetry/browser-telemetry/__tests__/filters/defaultUrlFilter.test.ts index 9b4876d62f..75a64e0a95 100644 --- a/packages/telemetry/browser-telemetry/__tests__/filters/defaultUrlFilter.test.ts +++ b/packages/telemetry/browser-telemetry/__tests__/filters/defaultUrlFilter.test.ts @@ -39,3 +39,27 @@ it.each([ ])('passes through other URLs unfiltered', (url) => { expect(defaultUrlFilter(url)).toBe(url); }); + +it('filters out username and password from URLs', () => { + const urls = [ + // Username only + { + input: 'https://user@sdk.launchdarkly.com/', + expected: 'https://redacted@sdk.launchdarkly.com/', + }, + // Password only + { + input: 'https://:password123@sdk.launchdarkly.com/', + expected: 'https://:redacted@sdk.launchdarkly.com/', + }, + // Both username and password + { + input: 'https://user:password123@sdk.launchdarkly.com/', + expected: 'https://redacted:redacted@sdk.launchdarkly.com/', + }, + ]; + + urls.forEach(({ input, expected }) => { + expect(defaultUrlFilter(input)).toBe(expected); + }); +}); diff --git a/packages/telemetry/browser-telemetry/src/filters/defaultUrlFilter.ts b/packages/telemetry/browser-telemetry/src/filters/defaultUrlFilter.ts index a81c45722c..0cd85f5a44 100644 --- a/packages/telemetry/browser-telemetry/src/filters/defaultUrlFilter.ts +++ b/packages/telemetry/browser-telemetry/src/filters/defaultUrlFilter.ts @@ -1,13 +1,43 @@ const pollingRegex = /sdk\/evalx\/[^/]+\/contexts\/(?[^/?]*)\??.*?/; const streamingREgex = /\/eval\/[^/]+\/(?[^/?]*)\??.*?/; +/** + * Filter which redacts user information (auth) from a URL. + * + * If a username/password is present, then they are replaced with 'redacted'. + * Authority reference: https://developer.mozilla.org/en-US/docs/Web/URI/Authority + * + * @param url URL to filter. + * @returns A filtered URL. + */ +function authorityUrlFilter(url: string): string { + // This will work in browser environments, but in the future we may want to consider an approach + // which doesn't rely on the browser's URL parsing. This is because other environments we may + // want to target, such as ReactNative, may not have as robust URL parsing. + const urlObj = new URL(url); + let hadAuth = false; + if (urlObj.username) { + urlObj.username = 'redacted'; + hadAuth = true; + } + if (urlObj.password) { + urlObj.password = 'redacted'; + hadAuth = true; + } + if (hadAuth) { + return urlObj.toString(); + } + // If there was no auth information, then we don't need to modify the URL. + return url; +} + /** * Filter which removes context information for browser JavaScript endpoints. * * @param url URL to filter. * @returns A filtered URL. */ -export default function defaultUrlFilter(url: string): string { +function ldUrlFilter(url: string): string { // TODO: Maybe we consider a way to identify LD requests so they can be filtered without // regular expressions. @@ -27,3 +57,13 @@ export default function defaultUrlFilter(url: string): string { } return url; } + +/** + * Filter which redacts user information and removes context information for browser JavaScript endpoints. + * + * @param url URL to filter. + * @returns A filtered URL. + */ +export default function defaultUrlFilter(url: string): string { + return ldUrlFilter(authorityUrlFilter(url)); +}