From e0651b79e6a6d4bff48389ce8156fb9fb6175471 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Thu, 12 Mar 2026 14:51:43 -0700 Subject: [PATCH 01/13] Make URL Redaction more dynamic --- .../Unit/src/ai/AppInsightsCommon.tests.ts | 18 +++- .../src/ai/ApplicationInsightsCore.Tests.ts | 89 +++++++++++++++---- .../src/interfaces/ai/IConfiguration.ts | 19 +++- shared/AppInsightsCore/src/utils/EnvUtils.ts | 16 ++-- 4 files changed, 115 insertions(+), 27 deletions(-) diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts index 19a432098..f7f927e19 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts @@ -395,7 +395,7 @@ export class ApplicationInsightsTests extends AITestClass { test: () => { // URLs with sensitive query parameters let config = { - redactQueryParams: ["authorize", "api_key", "password"] + appendRedactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; const urlWithSensitiveParams = "https://example.com/api?Signature=secret&authorize=value"; const expectedRedactedUrl = "https://example.com/api?Signature=REDACTED&authorize=REDACTED"; @@ -405,5 +405,21 @@ export class ApplicationInsightsTests extends AITestClass { Assert.equal(expectedRedactedUrl, result); } }); + + this.testCase({ + name: 'DataSanitizerTests: dataSanitizeUrl properly redacts sensitive query parameters ( only custom)', + test: () => { + // URLs with sensitive query parameters + let config = { + replaceRedactQueryParams: ["authorize", "api_key", "password"] + } as IConfiguration; + const urlWithSensitiveParams = "https://example.com/api?Signature=secret&authorize=value"; + const expectedRedactedUrl = "https://example.com/api?Signature=secret&authorize=REDACTED"; + + // Act & Assert + const result = dataSanitizeUrl(this.logger, urlWithSensitiveParams, config); + Assert.equal(expectedRedactedUrl, result); + } + }); } } diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts index 15826fc36..0a601903d 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts @@ -2067,6 +2067,21 @@ export class ApplicationInsightsCoreTests extends AITestClass { "Complex URL should have credentials and sensitive query parameters redacted while preserving other components"); } }); + + this.testCase({ + name: "FieldRedaction: should not redact URLs when redaction is disabled in config, even if they contain credentials and sensitive query parameters", + test: () => { + let config = { + redactUrls: false, + redactQueryParams: false, + } as IConfiguration; + const url = "https://username:password@example.com:8443/path/to/resource?sig=secret&color=blue#section2"; + const redactedLocation = fieldRedaction(url, config); + Assert.equal(redactedLocation, "https://username:password@example.com:8443/path/to/resource?sig=secret&color=blue#section2", + "URL should not redact credentials and sensitive query parameters when redaction is disabled in config"); + } + }); + this.testCase({ name: "FieldRedaction: should handle completely empty URL string", test: () => { @@ -2197,10 +2212,10 @@ export class ApplicationInsightsCoreTests extends AITestClass { }); this.testCase({ - name: "FieldRedaction: should redact custom query parameters defined in redactQueryParams", + name: "FieldRedaction: should redact custom query parameters defined in redactQueryParams and replace custom queryParams", test: () => { let config = { - redactQueryParams: ["authorize", "api_key", "password"] + replaceRedactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; const url = "https://example.com/path?auth_token=12345&name=test&authorize=secret"; @@ -2213,7 +2228,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should redact both default and custom query parameters", test: () => { let config = { - redactQueryParams: ["auth_token"] + appendRedactQueryParams: ["auth_token"] } as IConfiguration; const url = "https://example.com/path?sig=abc123&auth_token=12345&name=test"; @@ -2223,26 +2238,24 @@ export class ApplicationInsightsCoreTests extends AITestClass { } }); this.testCase({ - name: "FieldRedaction:should not redact custom parameters when redaction is disabled", + name: "FieldRedaction:should redact custom parameters when redactUrls is disabled but redactQueryParams is not false", test: () => { let config = { redactUrls: false, - redactQueryParams: ["authorize", "api_key"] + replaceRedactQueryParams: ["authorize", "api_key"] } as IConfiguration; - const url = "https://example.com/path?auth_token=12345&authorize=secret"; + const url = "https://username:password@example.com/path?auth_token=12345&authorize=secret"; const redactedLocation = fieldRedaction(url, config); - Assert.equal(redactedLocation, "https://example.com/path?auth_token=12345&authorize=secret", - "URL with custom sensitive parameters should not be redacted when redaction is disabled"); + Assert.equal(redactedLocation, "https://username:password@example.com/path?auth_token=12345&authorize=REDACTED", + "URL with custom sensitive parameters should be redacted when query redaction is not disabled"); } }); this.testCase({ name: "FieldRedaction: should handle empty redactQueryParams array", test: () => { - let config = { - redactQueryParams: [] - } as IConfiguration; + let config = {} as IConfiguration; // Should still redact default parameters const url = "https://example.com/path?Signature=secret&custom_param=value"; @@ -2256,7 +2269,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction:should handle complex URLs with both credentials and custom query parameters", test: () => { let config = { - redactQueryParams: ["authorize", "session_id"] + appendRedactQueryParams: ["authorize", "session_id"] } as IConfiguration; const url = "https://user:pass@example.com/path?sig=secret&authorize=abc123&visible=true&session_id=xyz789"; @@ -2584,6 +2597,33 @@ export class ApplicationInsightsCoreTests extends AITestClass { } }); + this.testCase({ + name: "FieldRedaction: should redact credentials while preserving query strings when redactQueryParams is false", + test: () => { + let config = { + redactQueryParams: false + } as IConfiguration; + const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; + const redactedLocation = fieldRedaction(url, config); + Assert.equal(redactedLocation, "https://REDACTED:REDACTED@example.com/path?sig=secret&color=blue&token=abc123", + "Credentials should be redacted while query string values remain unchanged when redactQueryParams is false"); + } + }); + + this.testCase({ + name: "FieldRedaction: should handle custom parameters with multiple occurrences and empty values", + test: () => { + let config = { + replaceRedactQueryParams: ["auth_token", "session_id"] + } as IConfiguration; + const url = "https://example.com/path?auth_token=first&name=test&auth_token=&session_id=abc&session_id="; + const redactedLocation = fieldRedaction(url, config); + // Only redact parameters that have actual values, not empty ones + Assert.equal(redactedLocation, "https://example.com/path?auth_token=REDACTED&name=test&auth_token=&session_id=REDACTED&session_id=", + "Only non-empty custom sensitive parameters should be redacted"); + } + }); + this.testCase({ name: "FieldRedaction: should handle parameters without values mixed with valued parameters", test: () => { @@ -2598,16 +2638,29 @@ export class ApplicationInsightsCoreTests extends AITestClass { }); this.testCase({ - name: "FieldRedaction: should handle custom parameters with multiple occurrences and empty values", + name: "FieldRedaction: should preserve credentials while redacting query strings when redactUrls is false", test: () => { let config = { - redactQueryParams: ["auth_token", "session_id"] + redactUrls: false } as IConfiguration; - const url = "https://example.com/path?auth_token=first&name=test&auth_token=&session_id=abc&session_id="; + const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; const redactedLocation = fieldRedaction(url, config); - // Only redact parameters that have actual values, not empty ones - Assert.equal(redactedLocation, "https://example.com/path?auth_token=REDACTED&name=test&auth_token=&session_id=REDACTED&session_id=", - "Only non-empty custom sensitive parameters should be redacted"); + Assert.equal(redactedLocation, "https://user:password@example.com/path?sig=REDACTED&color=blue&token=abc123", + "Query string values should be redacted while credentials remain unchanged when redactUrls is false"); + } + }); + + this.testCase({ + name: "FieldRedaction: should not redact credentials or query strings when redactUrls and redactQueryParams are false", + test: () => { + let config = { + redactUrls: false, + redactQueryParams: false + } as IConfiguration; + const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; + const redactedLocation = fieldRedaction(url, config); + Assert.equal(redactedLocation, url, + "Nothing should be redacted when both redactUrls and redactQueryParams are false"); } }); diff --git a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts index c3db75b74..c0f61f0db 100644 --- a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts +++ b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts @@ -232,11 +232,17 @@ export interface IConfiguration extends IOTelConfig { expCfg?: IExceptionConfig; /** - * [Optional] A flag to enable or disable the use of the field redaction for urls. + * [Optional] A flag to enable or disable redaction for username and password in URLs. * @defaultValue true */ redactUrls?: boolean; + /** + * [Optional] A flag to enable or disable redaction for query parameters. + * @defaultValue true + */ + redactQueryParams?: boolean; + /** * [Optional] Additional query parameters to redact beyond the default set. * Use this to specify custom parameters that contain sensitive information. @@ -244,7 +250,16 @@ export interface IConfiguration extends IOTelConfig { * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"] * @example ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature","auth_token", "api_key", "private_data"] */ - redactQueryParams?: string[]; + appendRedactQueryParams?: string[]; + + /** + * [Optional] Replaces the default set with a custom set of query parameters to redact. + * Use this to specify custom parameters that contain sensitive information. + * These will replace the default parameters that are redacted. + * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"] + * @example ["auth_token", "api_key", "private_data"] + */ + replaceRedactQueryParams?: string[]; ///** // * [Optional] Internal SDK configuration for developers diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index ed2382901..7f1692a34 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -455,8 +455,10 @@ function redactQueryParameters(url: string, config?: IConfiguration): string { return url; } - if (config && config.redactQueryParams) { - sensitiveParams = DEFAULT_SENSITIVE_PARAMS.concat(config.redactQueryParams); + if (config && config.appendRedactQueryParams) { + sensitiveParams = DEFAULT_SENSITIVE_PARAMS.concat(config.appendRedactQueryParams); + } else if (config && config.replaceRedactQueryParams) { + sensitiveParams = config.replaceRedactQueryParams; } else { sensitiveParams = DEFAULT_SENSITIVE_PARAMS; } @@ -543,10 +545,12 @@ export function fieldRedaction(input: string, config: IConfiguration): string { if (!input || !isString(input) || strIndexOf(input, " ") !== -1) { return input; } - const isRedactionDisabled = config && config.redactUrls === false; - if (isRedactionDisabled) { + const isRedactionDisabled = config && config.redactUrls === false + const isQueryParamRedactionDisabled = config && config.redactQueryParams === false; + if (isRedactionDisabled && isQueryParamRedactionDisabled) { return input; } + const hasCredentials = strIndexOf(input, "@") !== -1; const hasQueryParams = strIndexOf(input, "?") !== -1; @@ -556,10 +560,10 @@ export function fieldRedaction(input: string, config: IConfiguration): string { } try { let result = input; - if (hasCredentials) { + if (hasCredentials && !isRedactionDisabled) { result = redactUserInfo(input); } - if (hasQueryParams) { + if (hasQueryParams && !isQueryParamRedactionDisabled) { result = redactQueryParameters(result, config); } return result; From 8d1de519699e173495465a796155adb4fce20198 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Thu, 12 Mar 2026 15:02:50 -0700 Subject: [PATCH 02/13] Add missing colon --- shared/AppInsightsCore/src/utils/EnvUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 7f1692a34..8708fc478 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -545,7 +545,7 @@ export function fieldRedaction(input: string, config: IConfiguration): string { if (!input || !isString(input) || strIndexOf(input, " ") !== -1) { return input; } - const isRedactionDisabled = config && config.redactUrls === false + const isRedactionDisabled = config && config.redactUrls === false; const isQueryParamRedactionDisabled = config && config.redactQueryParams === false; if (isRedactionDisabled && isQueryParamRedactionDisabled) { return input; From b4d41fde0396a45d09c655cc237af092ba1fe2b6 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Thu, 12 Mar 2026 16:35:36 -0700 Subject: [PATCH 03/13] Retrigger CI/CD pipeline From 781fa521d1a5dcb9dabc4c72d607464e307fde37 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 16 Mar 2026 10:18:43 -0700 Subject: [PATCH 04/13] Addressed feedback, modified config --- .../Unit/src/ai/AppInsightsCommon.tests.ts | 10 +++-- .../src/ai/ApplicationInsightsCore.Tests.ts | 35 ++++++++-------- .../src/enums/ai/UrlRedactionOptions.ts | 41 +++++++++++++++++++ .../src/interfaces/ai/IConfiguration.ts | 20 ++------- shared/AppInsightsCore/src/utils/EnvUtils.ts | 31 +++++++++----- 5 files changed, 89 insertions(+), 48 deletions(-) create mode 100644 shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts index f7f927e19..88e5173f8 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts @@ -3,7 +3,7 @@ import { Assert, AITestClass } from "@microsoft/ai-test-framework"; import { DiagnosticLogger } from "../../../../src/diagnostics/DiagnosticLogger"; import { IConfiguration } from "../../../../src/interfaces/ai/IConfiguration"; import { dataSanitizeInput, dataSanitizeKey, dataSanitizeMessage, DataSanitizerValues, dataSanitizeString, dataSanitizeUrl } from "../../../../src/telemetry/ai/Common/DataSanitizer"; - +import { UrlRedactionOptions } from "../../../../src/enums/ai/UrlRedactionOptions" export class ApplicationInsightsTests extends AITestClass { logger = new DiagnosticLogger(); @@ -395,7 +395,8 @@ export class ApplicationInsightsTests extends AITestClass { test: () => { // URLs with sensitive query parameters let config = { - appendRedactQueryParams: ["authorize", "api_key", "password"] + redactUrls: UrlRedactionOptions.append, + redactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; const urlWithSensitiveParams = "https://example.com/api?Signature=secret&authorize=value"; const expectedRedactedUrl = "https://example.com/api?Signature=REDACTED&authorize=REDACTED"; @@ -407,11 +408,12 @@ export class ApplicationInsightsTests extends AITestClass { }); this.testCase({ - name: 'DataSanitizerTests: dataSanitizeUrl properly redacts sensitive query parameters ( only custom)', + name: 'DataSanitizerTests: dataSanitizeUrl properly redacts sensitive query parameters (only custom)', test: () => { // URLs with sensitive query parameters let config = { - replaceRedactQueryParams: ["authorize", "api_key", "password"] + redactUrls: UrlRedactionOptions.replace, + redactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; const urlWithSensitiveParams = "https://example.com/api?Signature=secret&authorize=value"; const expectedRedactedUrl = "https://example.com/api?Signature=secret&authorize=REDACTED"; diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts index 0a601903d..4271151c3 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts @@ -11,6 +11,7 @@ import { _InternalLogMessage, DiagnosticLogger } from "../../../../src/diagnosti import { ActiveStatus } from "../../../../src/enums/ai/InitActiveStatusEnum"; import { createAsyncPromise, createAsyncRejectedPromise, createAsyncResolvedPromise, createTimeoutPromise, doAwaitResponse } from "@nevware21/ts-async"; import { setBypassLazyCache } from "@nevware21/ts-utils"; +import { UrlRedactionOptions } from "../../../../src/enums/ai/UrlRedactionOptions" const AIInternalMessagePrefix = "AITR_"; const MaxInt32 = 0xFFFFFFFF; @@ -2073,7 +2074,6 @@ export class ApplicationInsightsCoreTests extends AITestClass { test: () => { let config = { redactUrls: false, - redactQueryParams: false, } as IConfiguration; const url = "https://username:password@example.com:8443/path/to/resource?sig=secret&color=blue#section2"; const redactedLocation = fieldRedaction(url, config); @@ -2215,7 +2215,8 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should redact custom query parameters defined in redactQueryParams and replace custom queryParams", test: () => { let config = { - replaceRedactQueryParams: ["authorize", "api_key", "password"] + redactUrls: UrlRedactionOptions.replace, + redactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; const url = "https://example.com/path?auth_token=12345&name=test&authorize=secret"; @@ -2228,7 +2229,8 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should redact both default and custom query parameters", test: () => { let config = { - appendRedactQueryParams: ["auth_token"] + redactUrls: UrlRedactionOptions.append, + redactQueryParams: ["auth_token"] } as IConfiguration; const url = "https://example.com/path?sig=abc123&auth_token=12345&name=test"; @@ -2238,16 +2240,16 @@ export class ApplicationInsightsCoreTests extends AITestClass { } }); this.testCase({ - name: "FieldRedaction:should redact custom parameters when redactUrls is disabled but redactQueryParams is not false", + name: "FieldRedaction:should replace custom parameters redactQueryParams when user specifies the replace config", test: () => { let config = { - redactUrls: false, - replaceRedactQueryParams: ["authorize", "api_key"] + redactUrls: UrlRedactionOptions.replace, + redactQueryParams: ["authorize", "api_key"] } as IConfiguration; const url = "https://username:password@example.com/path?auth_token=12345&authorize=secret"; const redactedLocation = fieldRedaction(url, config); - Assert.equal(redactedLocation, "https://username:password@example.com/path?auth_token=12345&authorize=REDACTED", + Assert.equal(redactedLocation, "https://REDACTED:REDACTED@example.com/path?auth_token=12345&authorize=REDACTED", "URL with custom sensitive parameters should be redacted when query redaction is not disabled"); } }); @@ -2269,7 +2271,8 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction:should handle complex URLs with both credentials and custom query parameters", test: () => { let config = { - appendRedactQueryParams: ["authorize", "session_id"] + redactUrls: UrlRedactionOptions.append, + redactQueryParams: ["authorize", "session_id"] } as IConfiguration; const url = "https://user:pass@example.com/path?sig=secret&authorize=abc123&visible=true&session_id=xyz789"; @@ -2601,7 +2604,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should redact credentials while preserving query strings when redactQueryParams is false", test: () => { let config = { - redactQueryParams: false + redactUrls: 5 } as IConfiguration; const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; const redactedLocation = fieldRedaction(url, config); @@ -2614,7 +2617,8 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should handle custom parameters with multiple occurrences and empty values", test: () => { let config = { - replaceRedactQueryParams: ["auth_token", "session_id"] + redactUrls: UrlRedactionOptions.replace, + redactQueryParams: ["auth_token", "session_id"] } as IConfiguration; const url = "https://example.com/path?auth_token=first&name=test&auth_token=&session_id=abc&session_id="; const redactedLocation = fieldRedaction(url, config); @@ -2638,15 +2642,15 @@ export class ApplicationInsightsCoreTests extends AITestClass { }); this.testCase({ - name: "FieldRedaction: should preserve credentials while redacting query strings when redactUrls is false", + name: "FieldRedaction: should redact all parts of the URL (username, password, default query params) when redactUrls is set to True", test: () => { let config = { - redactUrls: false + redactUrls: true } as IConfiguration; const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; const redactedLocation = fieldRedaction(url, config); - Assert.equal(redactedLocation, "https://user:password@example.com/path?sig=REDACTED&color=blue&token=abc123", - "Query string values should be redacted while credentials remain unchanged when redactUrls is false"); + Assert.equal(redactedLocation, "https://REDACTED:REDACTED@example.com/path?sig=REDACTED&color=blue&token=abc123", + "All parts of the URL should be redacted when redactUrls is true"); } }); @@ -2654,8 +2658,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should not redact credentials or query strings when redactUrls and redactQueryParams are false", test: () => { let config = { - redactUrls: false, - redactQueryParams: false + redactUrls: UrlRedactionOptions.false } as IConfiguration; const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; const redactedLocation = fieldRedaction(url, config); diff --git a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts new file mode 100644 index 000000000..194f17ced --- /dev/null +++ b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * Controls how the user can configure which parts of the URL should be redacted. Example, certain query parameters, username and password, etc. + * @since + */ +export const enum UrlRedactionOptions { + /** + * The default value, will redact the username and password as well as the default set of query parameters + */ + true = 1, + + /** + * Does not redact username and password or any query parameters, the URL will be left as is. Note: this is not recommended as it may lead + * to sensitive data being sent in clear text. + */ + false = 2, + + /** + * This will append any additional queryParams that the user has provided through redactQueryParams config to the default set i.e to + * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"]. + */ + append = 3, + + /** + * This will replace the default set of query parameters to redact with the query parameters defined in redactQueryParams config, if provided by the user. + */ + replace = 4, + + /** + * This will redact username and password in the URL but will not redact any query parameters, even those in the default set. + */ + usernamePasswordOnly = 5, + + /** + * This will only redact the query parameter in the default set of query parameters to redact. It will not redact username and password. + */ + queryParamsOnly = 6, + +} \ No newline at end of file diff --git a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts index c0f61f0db..d4d6c7487 100644 --- a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts +++ b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { IPromise } from "@nevware21/ts-async"; import { eTraceHeadersMode } from "../../enums/ai/TraceHeadersMode"; +import { UrlRedactionOptions } from "../../enums/ai/UrlRedactionOptions"; import { IOTelConfig } from "../otel/config/IOTelConfig"; import { IAppInsightsCore } from "./IAppInsightsCore"; import { IChannelControls } from "./IChannelControls"; @@ -231,17 +232,11 @@ export interface IConfiguration extends IOTelConfig { */ expCfg?: IExceptionConfig; - /** - * [Optional] A flag to enable or disable redaction for username and password in URLs. - * @defaultValue true - */ - redactUrls?: boolean; - /** * [Optional] A flag to enable or disable redaction for query parameters. * @defaultValue true */ - redactQueryParams?: boolean; + redactUrls?: boolean | UrlRedactionOptions; /** * [Optional] Additional query parameters to redact beyond the default set. @@ -250,16 +245,7 @@ export interface IConfiguration extends IOTelConfig { * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"] * @example ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature","auth_token", "api_key", "private_data"] */ - appendRedactQueryParams?: string[]; - - /** - * [Optional] Replaces the default set with a custom set of query parameters to redact. - * Use this to specify custom parameters that contain sensitive information. - * These will replace the default parameters that are redacted. - * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"] - * @example ["auth_token", "api_key", "private_data"] - */ - replaceRedactQueryParams?: string[]; + redactQueryParams?: string[]; ///** // * [Optional] Internal SDK configuration for developers diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 8708fc478..8347b5cfc 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -8,6 +8,7 @@ import { isFunction, isNullOrUndefined, isString, isUndefined, mathMax, strIndexOf, strSubstring } from "@nevware21/ts-utils"; import { DEFAULT_SENSITIVE_PARAMS, STR_EMPTY, STR_REDACTED } from "../constants/InternalConstants"; +import { UrlRedactionOptions } from "../enums/ai/UrlRedactionOptions"; import { IConfiguration } from "../interfaces/ai/IConfiguration"; import { strContains } from "./HelperFuncs"; @@ -455,10 +456,10 @@ function redactQueryParameters(url: string, config?: IConfiguration): string { return url; } - if (config && config.appendRedactQueryParams) { - sensitiveParams = DEFAULT_SENSITIVE_PARAMS.concat(config.appendRedactQueryParams); - } else if (config && config.replaceRedactQueryParams) { - sensitiveParams = config.replaceRedactQueryParams; + if (config && config.redactUrls === UrlRedactionOptions.append) { + sensitiveParams = DEFAULT_SENSITIVE_PARAMS.concat(config.redactQueryParams); + } else if (config && config.redactUrls === UrlRedactionOptions.replace) { + sensitiveParams = config.redactQueryParams; } else { sensitiveParams = DEFAULT_SENSITIVE_PARAMS; } @@ -545,25 +546,33 @@ export function fieldRedaction(input: string, config: IConfiguration): string { if (!input || !isString(input) || strIndexOf(input, " ") !== -1) { return input; } - const isRedactionDisabled = config && config.redactUrls === false; - const isQueryParamRedactionDisabled = config && config.redactQueryParams === false; - if (isRedactionDisabled && isQueryParamRedactionDisabled) { + const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions.false); + if (isRedactionDisabled) { return input; } - const hasCredentials = strIndexOf(input, "@") !== -1; - const hasQueryParams = strIndexOf(input, "?") !== -1; + let hasCredentials = strIndexOf(input, "@") !== -1; + let hasQueryParams = strIndexOf(input, "?") !== -1; // If no credentials and no query params, return original if (!hasCredentials && !hasQueryParams) { return input; } + + if (config.redactUrls === UrlRedactionOptions.usernamePasswordOnly) { + hasQueryParams = false; + } + + if (config.redactUrls === UrlRedactionOptions.queryParamsOnly) { + hasCredentials = false; + } + try { let result = input; - if (hasCredentials && !isRedactionDisabled) { + if (hasCredentials) { result = redactUserInfo(input); } - if (hasQueryParams && !isQueryParamRedactionDisabled) { + if (hasQueryParams) { result = redactQueryParameters(result, config); } return result; From 9a25f45d71fd061637a5675927af2f467f2aab61 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 16 Mar 2026 10:23:29 -0700 Subject: [PATCH 05/13] Retrigger CI/CD pipeline From 53aa2c8dfa894b6dc76190b931d51e84823410b1 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 09:24:00 -0700 Subject: [PATCH 06/13] Fix comparison issue --- shared/AppInsightsCore/src/utils/EnvUtils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 8347b5cfc..403bd971d 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -456,9 +456,11 @@ function redactQueryParameters(url: string, config?: IConfiguration): string { return url; } - if (config && config.redactUrls === UrlRedactionOptions.append) { + const option = config ? config.redactUrls : undefined; + + if (option === UrlRedactionOptions.append) { sensitiveParams = DEFAULT_SENSITIVE_PARAMS.concat(config.redactQueryParams); - } else if (config && config.redactUrls === UrlRedactionOptions.replace) { + } else if (option === UrlRedactionOptions.replace) { sensitiveParams = config.redactQueryParams; } else { sensitiveParams = DEFAULT_SENSITIVE_PARAMS; From 8368977e6aa1edc6e2a60343cb18e3398e878164 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 10:14:11 -0700 Subject: [PATCH 07/13] Fix comparison issue for false --- shared/AppInsightsCore/src/utils/EnvUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 403bd971d..0dcbbd4ed 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -548,7 +548,7 @@ export function fieldRedaction(input: string, config: IConfiguration): string { if (!input || !isString(input) || strIndexOf(input, " ") !== -1) { return input; } - const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions.false); + const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions["false"]); if (isRedactionDisabled) { return input; } From ddc25311e81911795b6233e13e75d80cba7e787f Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 11:36:29 -0700 Subject: [PATCH 08/13] Fix enum issue --- .../Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts | 2 +- shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts | 4 ++-- shared/AppInsightsCore/src/utils/EnvUtils.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts index 4271151c3..68c88f8f2 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts @@ -2658,7 +2658,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should not redact credentials or query strings when redactUrls and redactQueryParams are false", test: () => { let config = { - redactUrls: UrlRedactionOptions.false + redactUrls: UrlRedactionOptions.redactNone } as IConfiguration; const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; const redactedLocation = fieldRedaction(url, config); diff --git a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts index 194f17ced..a42217554 100644 --- a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts +++ b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts @@ -9,13 +9,13 @@ export const enum UrlRedactionOptions { /** * The default value, will redact the username and password as well as the default set of query parameters */ - true = 1, + redactAll = 1, /** * Does not redact username and password or any query parameters, the URL will be left as is. Note: this is not recommended as it may lead * to sensitive data being sent in clear text. */ - false = 2, + redactNone = 2, /** * This will append any additional queryParams that the user has provided through redactQueryParams config to the default set i.e to diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 0dcbbd4ed..9efe5a940 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -548,7 +548,7 @@ export function fieldRedaction(input: string, config: IConfiguration): string { if (!input || !isString(input) || strIndexOf(input, " ") !== -1) { return input; } - const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions["false"]); + const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions.redactNone); if (isRedactionDisabled) { return input; } From 08598488c38b3a0fa54f8f2505da59df183655b8 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 14:12:24 -0700 Subject: [PATCH 09/13] Push changes after rebuild --- .../Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts | 2 +- .../AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts | 8 ++++---- shared/AppInsightsCore/src/utils/EnvUtils.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts index 68c88f8f2..4271151c3 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts @@ -2658,7 +2658,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should not redact credentials or query strings when redactUrls and redactQueryParams are false", test: () => { let config = { - redactUrls: UrlRedactionOptions.redactNone + redactUrls: UrlRedactionOptions.false } as IConfiguration; const url = "https://user:password@example.com/path?sig=secret&color=blue&token=abc123"; const redactedLocation = fieldRedaction(url, config); diff --git a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts index a42217554..154164fd5 100644 --- a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts +++ b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts @@ -9,17 +9,17 @@ export const enum UrlRedactionOptions { /** * The default value, will redact the username and password as well as the default set of query parameters */ - redactAll = 1, + true = 1, /** - * Does not redact username and password or any query parameters, the URL will be left as is. Note: this is not recommended as it may lead + * Does not redact username and password or any query parameters, the URL will be left as is. Note: this is not recommended as it may lead * to sensitive data being sent in clear text. */ - redactNone = 2, + false = 2, /** * This will append any additional queryParams that the user has provided through redactQueryParams config to the default set i.e to - * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"]. + * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"]. */ append = 3, diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 9efe5a940..403bd971d 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -548,7 +548,7 @@ export function fieldRedaction(input: string, config: IConfiguration): string { if (!input || !isString(input) || strIndexOf(input, " ") !== -1) { return input; } - const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions.redactNone); + const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions.false); if (isRedactionDisabled) { return input; } From 1822734a8b5e4bd3303336eb9b745a21e05d1763 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 15:13:28 -0700 Subject: [PATCH 10/13] Fix string literal issue --- .../Tests/Unit/src/ai/AppInsightsCommon.tests.ts | 4 ++-- .../Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts | 10 +++++----- .../src/enums/ai/UrlRedactionOptions.ts | 8 ++++---- shared/AppInsightsCore/src/utils/EnvUtils.ts | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts index 88e5173f8..a62b775f5 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCommon.tests.ts @@ -395,7 +395,7 @@ export class ApplicationInsightsTests extends AITestClass { test: () => { // URLs with sensitive query parameters let config = { - redactUrls: UrlRedactionOptions.append, + redactUrls: UrlRedactionOptions.appendToDefault, redactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; const urlWithSensitiveParams = "https://example.com/api?Signature=secret&authorize=value"; @@ -412,7 +412,7 @@ export class ApplicationInsightsTests extends AITestClass { test: () => { // URLs with sensitive query parameters let config = { - redactUrls: UrlRedactionOptions.replace, + redactUrls: UrlRedactionOptions.replaceDefault, redactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; const urlWithSensitiveParams = "https://example.com/api?Signature=secret&authorize=value"; diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts index 4271151c3..ea9fb9710 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts @@ -2215,7 +2215,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should redact custom query parameters defined in redactQueryParams and replace custom queryParams", test: () => { let config = { - redactUrls: UrlRedactionOptions.replace, + redactUrls: UrlRedactionOptions.replaceDefault, redactQueryParams: ["authorize", "api_key", "password"] } as IConfiguration; @@ -2229,7 +2229,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should redact both default and custom query parameters", test: () => { let config = { - redactUrls: UrlRedactionOptions.append, + redactUrls: UrlRedactionOptions.appendToDefault, redactQueryParams: ["auth_token"] } as IConfiguration; @@ -2243,7 +2243,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction:should replace custom parameters redactQueryParams when user specifies the replace config", test: () => { let config = { - redactUrls: UrlRedactionOptions.replace, + redactUrls: UrlRedactionOptions.replaceDefault, redactQueryParams: ["authorize", "api_key"] } as IConfiguration; @@ -2271,7 +2271,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction:should handle complex URLs with both credentials and custom query parameters", test: () => { let config = { - redactUrls: UrlRedactionOptions.append, + redactUrls: UrlRedactionOptions.appendToDefault, redactQueryParams: ["authorize", "session_id"] } as IConfiguration; @@ -2617,7 +2617,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { name: "FieldRedaction: should handle custom parameters with multiple occurrences and empty values", test: () => { let config = { - redactUrls: UrlRedactionOptions.replace, + redactUrls: UrlRedactionOptions.replaceDefault, redactQueryParams: ["auth_token", "session_id"] } as IConfiguration; const url = "https://example.com/path?auth_token=first&name=test&auth_token=&session_id=abc&session_id="; diff --git a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts index 154164fd5..0e8d90858 100644 --- a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts +++ b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts @@ -3,8 +3,8 @@ /** * Controls how the user can configure which parts of the URL should be redacted. Example, certain query parameters, username and password, etc. - * @since - */ +*/ + export const enum UrlRedactionOptions { /** * The default value, will redact the username and password as well as the default set of query parameters @@ -21,12 +21,12 @@ export const enum UrlRedactionOptions { * This will append any additional queryParams that the user has provided through redactQueryParams config to the default set i.e to * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"]. */ - append = 3, + appendToDefault = 3, /** * This will replace the default set of query parameters to redact with the query parameters defined in redactQueryParams config, if provided by the user. */ - replace = 4, + replaceDefault = 4, /** * This will redact username and password in the URL but will not redact any query parameters, even those in the default set. diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 403bd971d..5a9cb3e53 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -458,9 +458,9 @@ function redactQueryParameters(url: string, config?: IConfiguration): string { const option = config ? config.redactUrls : undefined; - if (option === UrlRedactionOptions.append) { + if (option === UrlRedactionOptions.appendToDefault) { sensitiveParams = DEFAULT_SENSITIVE_PARAMS.concat(config.redactQueryParams); - } else if (option === UrlRedactionOptions.replace) { + } else if (option === UrlRedactionOptions.replaceDefault) { sensitiveParams = config.redactQueryParams; } else { sensitiveParams = DEFAULT_SENSITIVE_PARAMS; From 7af28dc83fa9e66ff2923376aaa872c546f87c9d Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Wed, 18 Mar 2026 09:12:13 -0700 Subject: [PATCH 11/13] Fix failing test --- .../AppInsightsCore/src/interfaces/ai/IConfiguration.ts | 2 +- shared/AppInsightsCore/src/utils/EnvUtils.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts index d4d6c7487..21559344f 100644 --- a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts +++ b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts @@ -233,7 +233,7 @@ export interface IConfiguration extends IOTelConfig { expCfg?: IExceptionConfig; /** - * [Optional] A flag to enable or disable redaction for query parameters. + * [Optional] A flag to enable or disable redaction for query parameters and username/password. * @defaultValue true */ redactUrls?: boolean | UrlRedactionOptions; diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 5a9cb3e53..6b3dbfa7d 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -548,7 +548,10 @@ export function fieldRedaction(input: string, config: IConfiguration): string { if (!input || !isString(input) || strIndexOf(input, " ") !== -1) { return input; } - const isRedactionDisabled = config && (config.redactUrls === false || config.redactUrls === UrlRedactionOptions.false); + + const option = config ? config.redactUrls : undefined; + + const isRedactionDisabled = option === false || option === UrlRedactionOptions.false; if (isRedactionDisabled) { return input; } @@ -561,11 +564,11 @@ export function fieldRedaction(input: string, config: IConfiguration): string { return input; } - if (config.redactUrls === UrlRedactionOptions.usernamePasswordOnly) { + if (option === UrlRedactionOptions.usernamePasswordOnly) { hasQueryParams = false; } - if (config.redactUrls === UrlRedactionOptions.queryParamsOnly) { + if (option === UrlRedactionOptions.queryParamsOnly) { hasCredentials = false; } From 72c224bd13e6e1fa8a335a87e007cdac40c19424 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Thu, 19 Mar 2026 11:47:43 -0700 Subject: [PATCH 12/13] Modify enum logic --- .../src/enums/ai/UrlRedactionOptions.ts | 43 ++++++++++++++++++- shared/AppInsightsCore/src/index.ts | 1 + .../src/interfaces/ai/IConfiguration.ts | 2 +- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts index 0e8d90858..b9764deaa 100644 --- a/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts +++ b/shared/AppInsightsCore/src/enums/ai/UrlRedactionOptions.ts @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { createEnumStyle } from "../EnumHelperFuncs"; + /** * Controls how the user can configure which parts of the URL should be redacted. Example, certain query parameters, username and password, etc. */ -export const enum UrlRedactionOptions { +export const enum eUrlRedactionOptions { /** * The default value, will redact the username and password as well as the default set of query parameters */ @@ -38,4 +40,41 @@ export const enum UrlRedactionOptions { */ queryParamsOnly = 6, -} \ No newline at end of file +} + +export const UrlRedactionOptions = (/* @__PURE__ */ createEnumStyle({ + /** + * The default value, will redact the username and password as well as the default set of query parameters + */ + true: eUrlRedactionOptions.true, + + /** + * Does not redact username and password or any query parameters, the URL will be left as is. Note: this is not recommended as it may lead + * to sensitive data being sent in clear text. + */ + false: eUrlRedactionOptions.false, + + /** + * This will append any additional queryParams that the user has provided through redactQueryParams config to the default set i.e to + * @defaultValue ["sig", "Signature", "AWSAccessKeyId", "X-Goog-Signature"]. + */ + appendToDefault: eUrlRedactionOptions.appendToDefault, + + /** + * This will replace the default set of query parameters to redact with the query parameters defined in redactQueryParams config, if provided by the user. + */ + replaceDefault: eUrlRedactionOptions.replaceDefault, + + /** + * This will redact username and password in the URL but will not redact any query parameters, even those in the default set. + */ + usernamePasswordOnly: eUrlRedactionOptions.usernamePasswordOnly, + + /** + * This will only redact the query parameter in the default set of query parameters to redact. It will not redact username and password. + */ + queryParamsOnly: eUrlRedactionOptions.queryParamsOnly, + +})); + +export type UrlRedactionOptions = boolean | eUrlRedactionOptions; \ No newline at end of file diff --git a/shared/AppInsightsCore/src/index.ts b/shared/AppInsightsCore/src/index.ts index 8c6cd803f..3b22d0abc 100644 --- a/shared/AppInsightsCore/src/index.ts +++ b/shared/AppInsightsCore/src/index.ts @@ -22,6 +22,7 @@ export { SendRequestReason } from "./enums/ai/SendRequestReason"; //export { StatsType, eStatsType } from "./enums/ai/StatsType"; export { TelemetryUpdateReason } from "./enums/ai/TelemetryUpdateReason"; export { TelemetryUnloadReason } from "./enums/ai/TelemetryUnloadReason"; +export { eUrlRedactionOptions, UrlRedactionOptions } from "./enums/ai/UrlRedactionOptions" export { eActiveStatus, ActiveStatus } from "./enums/ai/InitActiveStatusEnum"; export { throwAggregationError } from "./core/AggregationError"; export { AppInsightsCore } from "./core/AppInsightsCore"; diff --git a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts index 21559344f..be2ed3793 100644 --- a/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts +++ b/shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts @@ -236,7 +236,7 @@ export interface IConfiguration extends IOTelConfig { * [Optional] A flag to enable or disable redaction for query parameters and username/password. * @defaultValue true */ - redactUrls?: boolean | UrlRedactionOptions; + redactUrls?: UrlRedactionOptions; /** * [Optional] Additional query parameters to redact beyond the default set. From 8fd3800bb67959adfe2a06c2565af50576613e39 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 27 Mar 2026 09:37:40 -0700 Subject: [PATCH 13/13] Addressed feedback --- .../src/ai/ApplicationInsightsCore.Tests.ts | 18 +++++++++++++++++- shared/AppInsightsCore/src/utils/EnvUtils.ts | 12 ++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts index ea9fb9710..a98439fd6 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/ai/ApplicationInsightsCore.Tests.ts @@ -2225,6 +2225,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { "URL with custom sensitive parameters should have them redacted while preserving other parameters"); } }); + this.testCase({ name: "FieldRedaction: should redact both default and custom query parameters", test: () => { @@ -2239,6 +2240,7 @@ export class ApplicationInsightsCoreTests extends AITestClass { "URL with both default and custom sensitive parameters should have all redacted"); } }); + this.testCase({ name: "FieldRedaction:should replace custom parameters redactQueryParams when user specifies the replace config", test: () => { @@ -2254,6 +2256,21 @@ export class ApplicationInsightsCoreTests extends AITestClass { } }); + this.testCase({ + name: "FieldRedaction: should not redact any query string values when custom query parameters are empty", + test: () => { + let config = { + redactUrls: UrlRedactionOptions.replaceDefault, + redactQueryParams: [] + } as IConfiguration; + + const url = "https://example.com/path?auth_token=12345&name=test&authorize=secret"; + const redactedLocation = fieldRedaction(url, config); + Assert.equal(redactedLocation, "https://example.com/path?auth_token=12345&name=test&authorize=secret", + "URL with custom sensitive parameters should not be redacted when custom query parameters are empty"); + } + }); + this.testCase({ name: "FieldRedaction: should handle empty redactQueryParams array", test: () => { @@ -2420,7 +2437,6 @@ export class ApplicationInsightsCoreTests extends AITestClass { } }); - this.testCase({ name: "FieldRedaction: should handle extremely long usernames without infinite looping", test: () => { diff --git a/shared/AppInsightsCore/src/utils/EnvUtils.ts b/shared/AppInsightsCore/src/utils/EnvUtils.ts index 6b3dbfa7d..b8209b2b1 100644 --- a/shared/AppInsightsCore/src/utils/EnvUtils.ts +++ b/shared/AppInsightsCore/src/utils/EnvUtils.ts @@ -8,7 +8,7 @@ import { isFunction, isNullOrUndefined, isString, isUndefined, mathMax, strIndexOf, strSubstring } from "@nevware21/ts-utils"; import { DEFAULT_SENSITIVE_PARAMS, STR_EMPTY, STR_REDACTED } from "../constants/InternalConstants"; -import { UrlRedactionOptions } from "../enums/ai/UrlRedactionOptions"; +import { eUrlRedactionOptions } from "../enums/ai/UrlRedactionOptions"; import { IConfiguration } from "../interfaces/ai/IConfiguration"; import { strContains } from "./HelperFuncs"; @@ -458,9 +458,9 @@ function redactQueryParameters(url: string, config?: IConfiguration): string { const option = config ? config.redactUrls : undefined; - if (option === UrlRedactionOptions.appendToDefault) { + if (option === eUrlRedactionOptions.appendToDefault) { sensitiveParams = DEFAULT_SENSITIVE_PARAMS.concat(config.redactQueryParams); - } else if (option === UrlRedactionOptions.replaceDefault) { + } else if (option === eUrlRedactionOptions.replaceDefault) { sensitiveParams = config.redactQueryParams; } else { sensitiveParams = DEFAULT_SENSITIVE_PARAMS; @@ -551,7 +551,7 @@ export function fieldRedaction(input: string, config: IConfiguration): string { const option = config ? config.redactUrls : undefined; - const isRedactionDisabled = option === false || option === UrlRedactionOptions.false; + const isRedactionDisabled = option === false || option === eUrlRedactionOptions.false; if (isRedactionDisabled) { return input; } @@ -564,11 +564,11 @@ export function fieldRedaction(input: string, config: IConfiguration): string { return input; } - if (option === UrlRedactionOptions.usernamePasswordOnly) { + if (option === eUrlRedactionOptions.usernamePasswordOnly) { hasQueryParams = false; } - if (option === UrlRedactionOptions.queryParamsOnly) { + if (option === eUrlRedactionOptions.queryParamsOnly) { hasCredentials = false; }