Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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: () => {
Expand Down Expand Up @@ -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";
Expand All @@ -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";
Expand All @@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -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: () => {
Expand All @@ -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");
}
});

Expand Down
19 changes: 17 additions & 2 deletions shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,19 +232,34 @@ 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;
Copy link
Member Author

@rads-1996 rads-1996 Mar 12, 2026

Choose a reason for hiding this comment

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

I have added an opt in/out option indiviually for username:password and query params. Previously we just had a redactUrls which would completely not redact anything in the url, if set to False

Do we need this level of flexibility?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems like too many configs (with long names) which are ( probably ) going to be rarely used.

Suggestion: Introduce a new constant enum and lest change the redactUrls to use that

redactUrls?: boolean | RedactUrlOption

With values like (names are suggestions only so feel free to change them)

  • true / false (does what it does today - nothing or use defaults with merged urls)
  • AllMerge (same as true)
  • AllReplace
  • UrlOnly
  • UsernamePasswordOnly
  • etc

Or use a bitwise value logic for the values
0x00-0x0f (Url)
0x10 add (to Skip Username/Passwword etc)

Copy link
Member Author

Choose a reason for hiding this comment

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

Quick question, when the user choose replace/append for query params, the username and password should be redacted as per logic right?


/**
* [Optional] Additional query parameters to redact beyond the default set.
* Use this to specify custom parameters that contain sensitive information.
* These will be combined with the default parameters that are redacted.
* @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
Expand Down
14 changes: 9 additions & 5 deletions shared/AppInsightsCore/src/utils/EnvUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -544,9 +546,11 @@ export function fieldRedaction(input: string, config: IConfiguration): string {
return input;
}
const isRedactionDisabled = config && config.redactUrls === false;
if (isRedactionDisabled) {
const isQueryParamRedactionDisabled = config && config.redactQueryParams === false;
if (isRedactionDisabled && isQueryParamRedactionDisabled) {
return input;
}

const hasCredentials = strIndexOf(input, "@") !== -1;
const hasQueryParams = strIndexOf(input, "?") !== -1;

Expand All @@ -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;
Expand Down
Loading