@microsoft/omnichannel-chat-widget 1.8.4-main.51ef6e3 → 1.8.4-main.5b3f077
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/common/telemetry/TelemetryManager.js +6 -1
- package/lib/cjs/common/telemetry/sanitizeSasInPayload.js +82 -0
- package/lib/cjs/components/prechatsurveypanestateful/common/defaultStyles/defaultGeneralPreChatSurveyPaneStyleProps.js +1 -2
- package/lib/esm/common/telemetry/TelemetryManager.js +6 -1
- package/lib/esm/common/telemetry/sanitizeSasInPayload.js +75 -0
- package/lib/esm/components/prechatsurveypanestateful/common/defaultStyles/defaultGeneralPreChatSurveyPaneStyleProps.js +1 -2
- package/lib/types/common/telemetry/sanitizeSasInPayload.d.ts +24 -0
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ var _appInsightsLogger = require("./loggers/appInsightsLogger");
|
|
|
11
11
|
var _ariaTelemetryLogger = require("./loggers/ariaTelemetryLogger");
|
|
12
12
|
var _consoleLogger = require("./loggers/consoleLogger");
|
|
13
13
|
var _defaultAriaConfig = require("./defaultConfigs/defaultAriaConfig");
|
|
14
|
+
var _sanitizeSasInPayload = require("./sanitizeSasInPayload");
|
|
14
15
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
15
16
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
16
17
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
@@ -81,11 +82,15 @@ const RegisterLoggers = () => {
|
|
|
81
82
|
};
|
|
82
83
|
};
|
|
83
84
|
const logTelemetry = telemetryEvent => {
|
|
85
|
+
// Redact Azure SAS credentials in any URL embedded in the payload (e.g.
|
|
86
|
+
// customer-supplied blob asset URLs in widget customization props) before
|
|
87
|
+
// it fans out to console, Aria, AppInsights, or any custom logger.
|
|
88
|
+
const sanitizedPayload = (0, _sanitizeSasInPayload.sanitizeSasInPayload)(telemetryEvent === null || telemetryEvent === void 0 ? void 0 : telemetryEvent.payload);
|
|
84
89
|
loggers.map(logger => {
|
|
85
90
|
var _payload, _telemetryInput$paylo;
|
|
86
91
|
const logLevel = telemetryEvent.logLevel ?? _TelemetryConstants.LogLevel.INFO;
|
|
87
92
|
const scenarioType = ((_payload = telemetryEvent.payload) === null || _payload === void 0 ? void 0 : _payload.scenarioType) ?? _TelemetryConstants.ScenarioType.UNDEFINED;
|
|
88
|
-
const telemetryInput = parseInput(
|
|
93
|
+
const telemetryInput = parseInput(sanitizedPayload, scenarioType);
|
|
89
94
|
telemetryInput.telemetryInfo = {
|
|
90
95
|
telemetryInfo: _TelemetryHelper.TelemetryHelper.buildTelemetryEvent(logLevel, telemetryInput)
|
|
91
96
|
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.sanitizeSasInPayload = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Redacts the values of Azure Shared Access Signature (SAS) query parameters in any
|
|
9
|
+
* URL embedded in a telemetry payload, so customer-supplied SAS URLs (e.g. blob asset
|
|
10
|
+
* URLs in widget customization props) cannot leak credentials through Aria/AppInsights.
|
|
11
|
+
*
|
|
12
|
+
* Trigger: any URL whose query string contains `sig=`. When detected, every well-known
|
|
13
|
+
* SAS parameter value in that query is replaced with `[REDACTED]`. Other URLs and other
|
|
14
|
+
* substrings of the payload pass through unchanged.
|
|
15
|
+
*
|
|
16
|
+
* Coverage of SAS parameter names:
|
|
17
|
+
* - Service/Blob SAS: sig, sv, se, st, sp, spr, sr, sip
|
|
18
|
+
* - Account SAS: ss, srt
|
|
19
|
+
* - User-delegation key SAS: skoid, sktid, skt, ske, sks, skv, saoid, suoid, sce, sdd
|
|
20
|
+
* - Response header overrides: rscc, rscd, rsce, rscl, rsct
|
|
21
|
+
*
|
|
22
|
+
* Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const SAS_PARAM_NAMES = new Set(["sig", "sv", "se", "st", "sp", "spr", "sr", "sip", "ss", "srt", "skoid", "sktid", "skt", "ske", "sks", "skv", "saoid", "suoid", "sce", "sdd", "rscc", "rscd", "rsce", "rscl", "rsct"]);
|
|
26
|
+
const REDACTED_VALUE = "[REDACTED]";
|
|
27
|
+
|
|
28
|
+
// Matches any http(s) URL substring up to whitespace / closing quote / angle-bracket / paren.
|
|
29
|
+
const URL_REGEX = /\bhttps?:\/\/[^\s"'<>)]+/gi;
|
|
30
|
+
const redactQueryIfSas = url => {
|
|
31
|
+
const queryIdx = url.indexOf("?");
|
|
32
|
+
if (queryIdx < 0) {
|
|
33
|
+
return url;
|
|
34
|
+
}
|
|
35
|
+
// Separate the fragment (#...) before processing the query — fragments come
|
|
36
|
+
// after the query string in URL syntax and must not be folded into the last
|
|
37
|
+
// query parameter's value when we split on `&`.
|
|
38
|
+
const afterQuestionMark = url.slice(queryIdx + 1);
|
|
39
|
+
const fragIdx = afterQuestionMark.indexOf("#");
|
|
40
|
+
const query = fragIdx < 0 ? afterQuestionMark : afterQuestionMark.slice(0, fragIdx);
|
|
41
|
+
const fragment = fragIdx < 0 ? "" : afterQuestionMark.slice(fragIdx); // includes the leading '#'
|
|
42
|
+
if (!/(^|&)sig=/i.test(query)) {
|
|
43
|
+
// No SAS signature present — leave the URL alone so legitimate non-SAS query
|
|
44
|
+
// strings (e.g. ?utm_source=...) are not mangled.
|
|
45
|
+
return url;
|
|
46
|
+
}
|
|
47
|
+
const base = url.slice(0, queryIdx);
|
|
48
|
+
const sanitizedQuery = query.split("&").map(pair => {
|
|
49
|
+
const eqIdx = pair.indexOf("=");
|
|
50
|
+
if (eqIdx < 0) {
|
|
51
|
+
return pair;
|
|
52
|
+
}
|
|
53
|
+
const name = pair.slice(0, eqIdx).toLowerCase();
|
|
54
|
+
return SAS_PARAM_NAMES.has(name) ? `${pair.slice(0, eqIdx)}=${REDACTED_VALUE}` : pair;
|
|
55
|
+
}).join("&");
|
|
56
|
+
return `${base}?${sanitizedQuery}${fragment}`;
|
|
57
|
+
};
|
|
58
|
+
const sanitizeString = s => s.replace(URL_REGEX, redactQueryIfSas);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Deep-walk a value and redact SAS credentials in any string it contains.
|
|
62
|
+
* Returns a new object/array (originals are not mutated); non-string leaves are
|
|
63
|
+
* passed through. Tree-shaped, JSON-like values only — not designed for class
|
|
64
|
+
* instances, prototype-bound objects, or cyclic graphs.
|
|
65
|
+
*/
|
|
66
|
+
const sanitizeSasInPayload = value => {
|
|
67
|
+
if (typeof value === "string") {
|
|
68
|
+
return sanitizeString(value);
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
return value.map(sanitizeSasInPayload);
|
|
72
|
+
}
|
|
73
|
+
if (value !== null && typeof value === "object") {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const [k, v] of Object.entries(value)) {
|
|
76
|
+
out[k] = sanitizeSasInPayload(v);
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
};
|
|
82
|
+
exports.sanitizeSasInPayload = sanitizeSasInPayload;
|
|
@@ -12,7 +12,6 @@ const defaultGeneralPreChatSurveyPaneStyleProps = {
|
|
|
12
12
|
borderColor: "#F1F1F1",
|
|
13
13
|
overflowY: "auto",
|
|
14
14
|
height: "inherit",
|
|
15
|
-
width: "inherit"
|
|
16
|
-
overscrollBehavior: "none"
|
|
15
|
+
width: "inherit"
|
|
17
16
|
};
|
|
18
17
|
exports.defaultGeneralPreChatSurveyPaneStyleProps = defaultGeneralPreChatSurveyPaneStyleProps;
|
|
@@ -11,6 +11,7 @@ import { appInsightsLogger } from "./loggers/appInsightsLogger";
|
|
|
11
11
|
import { ariaTelemetryLogger } from "./loggers/ariaTelemetryLogger";
|
|
12
12
|
import { consoleLogger } from "./loggers/consoleLogger";
|
|
13
13
|
import { defaultAriaConfig } from "./defaultConfigs/defaultAriaConfig";
|
|
14
|
+
import { sanitizeSasInPayload } from "./sanitizeSasInPayload";
|
|
14
15
|
export let TelemetryTimers = /*#__PURE__*/_createClass(function TelemetryTimers() {
|
|
15
16
|
_classCallCheck(this, TelemetryTimers);
|
|
16
17
|
});
|
|
@@ -72,11 +73,15 @@ export const RegisterLoggers = () => {
|
|
|
72
73
|
};
|
|
73
74
|
};
|
|
74
75
|
const logTelemetry = telemetryEvent => {
|
|
76
|
+
// Redact Azure SAS credentials in any URL embedded in the payload (e.g.
|
|
77
|
+
// customer-supplied blob asset URLs in widget customization props) before
|
|
78
|
+
// it fans out to console, Aria, AppInsights, or any custom logger.
|
|
79
|
+
const sanitizedPayload = sanitizeSasInPayload(telemetryEvent === null || telemetryEvent === void 0 ? void 0 : telemetryEvent.payload);
|
|
75
80
|
loggers.map(logger => {
|
|
76
81
|
var _payload, _telemetryInput$paylo;
|
|
77
82
|
const logLevel = telemetryEvent.logLevel ?? LogLevel.INFO;
|
|
78
83
|
const scenarioType = ((_payload = telemetryEvent.payload) === null || _payload === void 0 ? void 0 : _payload.scenarioType) ?? ScenarioType.UNDEFINED;
|
|
79
|
-
const telemetryInput = parseInput(
|
|
84
|
+
const telemetryInput = parseInput(sanitizedPayload, scenarioType);
|
|
80
85
|
telemetryInput.telemetryInfo = {
|
|
81
86
|
telemetryInfo: TelemetryHelper.buildTelemetryEvent(logLevel, telemetryInput)
|
|
82
87
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redacts the values of Azure Shared Access Signature (SAS) query parameters in any
|
|
3
|
+
* URL embedded in a telemetry payload, so customer-supplied SAS URLs (e.g. blob asset
|
|
4
|
+
* URLs in widget customization props) cannot leak credentials through Aria/AppInsights.
|
|
5
|
+
*
|
|
6
|
+
* Trigger: any URL whose query string contains `sig=`. When detected, every well-known
|
|
7
|
+
* SAS parameter value in that query is replaced with `[REDACTED]`. Other URLs and other
|
|
8
|
+
* substrings of the payload pass through unchanged.
|
|
9
|
+
*
|
|
10
|
+
* Coverage of SAS parameter names:
|
|
11
|
+
* - Service/Blob SAS: sig, sv, se, st, sp, spr, sr, sip
|
|
12
|
+
* - Account SAS: ss, srt
|
|
13
|
+
* - User-delegation key SAS: skoid, sktid, skt, ske, sks, skv, saoid, suoid, sce, sdd
|
|
14
|
+
* - Response header overrides: rscc, rscd, rsce, rscl, rsct
|
|
15
|
+
*
|
|
16
|
+
* Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const SAS_PARAM_NAMES = new Set(["sig", "sv", "se", "st", "sp", "spr", "sr", "sip", "ss", "srt", "skoid", "sktid", "skt", "ske", "sks", "skv", "saoid", "suoid", "sce", "sdd", "rscc", "rscd", "rsce", "rscl", "rsct"]);
|
|
20
|
+
const REDACTED_VALUE = "[REDACTED]";
|
|
21
|
+
|
|
22
|
+
// Matches any http(s) URL substring up to whitespace / closing quote / angle-bracket / paren.
|
|
23
|
+
const URL_REGEX = /\bhttps?:\/\/[^\s"'<>)]+/gi;
|
|
24
|
+
const redactQueryIfSas = url => {
|
|
25
|
+
const queryIdx = url.indexOf("?");
|
|
26
|
+
if (queryIdx < 0) {
|
|
27
|
+
return url;
|
|
28
|
+
}
|
|
29
|
+
// Separate the fragment (#...) before processing the query — fragments come
|
|
30
|
+
// after the query string in URL syntax and must not be folded into the last
|
|
31
|
+
// query parameter's value when we split on `&`.
|
|
32
|
+
const afterQuestionMark = url.slice(queryIdx + 1);
|
|
33
|
+
const fragIdx = afterQuestionMark.indexOf("#");
|
|
34
|
+
const query = fragIdx < 0 ? afterQuestionMark : afterQuestionMark.slice(0, fragIdx);
|
|
35
|
+
const fragment = fragIdx < 0 ? "" : afterQuestionMark.slice(fragIdx); // includes the leading '#'
|
|
36
|
+
if (!/(^|&)sig=/i.test(query)) {
|
|
37
|
+
// No SAS signature present — leave the URL alone so legitimate non-SAS query
|
|
38
|
+
// strings (e.g. ?utm_source=...) are not mangled.
|
|
39
|
+
return url;
|
|
40
|
+
}
|
|
41
|
+
const base = url.slice(0, queryIdx);
|
|
42
|
+
const sanitizedQuery = query.split("&").map(pair => {
|
|
43
|
+
const eqIdx = pair.indexOf("=");
|
|
44
|
+
if (eqIdx < 0) {
|
|
45
|
+
return pair;
|
|
46
|
+
}
|
|
47
|
+
const name = pair.slice(0, eqIdx).toLowerCase();
|
|
48
|
+
return SAS_PARAM_NAMES.has(name) ? `${pair.slice(0, eqIdx)}=${REDACTED_VALUE}` : pair;
|
|
49
|
+
}).join("&");
|
|
50
|
+
return `${base}?${sanitizedQuery}${fragment}`;
|
|
51
|
+
};
|
|
52
|
+
const sanitizeString = s => s.replace(URL_REGEX, redactQueryIfSas);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Deep-walk a value and redact SAS credentials in any string it contains.
|
|
56
|
+
* Returns a new object/array (originals are not mutated); non-string leaves are
|
|
57
|
+
* passed through. Tree-shaped, JSON-like values only — not designed for class
|
|
58
|
+
* instances, prototype-bound objects, or cyclic graphs.
|
|
59
|
+
*/
|
|
60
|
+
export const sanitizeSasInPayload = value => {
|
|
61
|
+
if (typeof value === "string") {
|
|
62
|
+
return sanitizeString(value);
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(value)) {
|
|
65
|
+
return value.map(sanitizeSasInPayload);
|
|
66
|
+
}
|
|
67
|
+
if (value !== null && typeof value === "object") {
|
|
68
|
+
const out = {};
|
|
69
|
+
for (const [k, v] of Object.entries(value)) {
|
|
70
|
+
out[k] = sanitizeSasInPayload(v);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redacts the values of Azure Shared Access Signature (SAS) query parameters in any
|
|
3
|
+
* URL embedded in a telemetry payload, so customer-supplied SAS URLs (e.g. blob asset
|
|
4
|
+
* URLs in widget customization props) cannot leak credentials through Aria/AppInsights.
|
|
5
|
+
*
|
|
6
|
+
* Trigger: any URL whose query string contains `sig=`. When detected, every well-known
|
|
7
|
+
* SAS parameter value in that query is replaced with `[REDACTED]`. Other URLs and other
|
|
8
|
+
* substrings of the payload pass through unchanged.
|
|
9
|
+
*
|
|
10
|
+
* Coverage of SAS parameter names:
|
|
11
|
+
* - Service/Blob SAS: sig, sv, se, st, sp, spr, sr, sip
|
|
12
|
+
* - Account SAS: ss, srt
|
|
13
|
+
* - User-delegation key SAS: skoid, sktid, skt, ske, sks, skv, saoid, suoid, sce, sdd
|
|
14
|
+
* - Response header overrides: rscc, rscd, rsce, rscl, rsct
|
|
15
|
+
*
|
|
16
|
+
* Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Deep-walk a value and redact SAS credentials in any string it contains.
|
|
20
|
+
* Returns a new object/array (originals are not mutated); non-string leaves are
|
|
21
|
+
* passed through. Tree-shaped, JSON-like values only — not designed for class
|
|
22
|
+
* instances, prototype-bound objects, or cyclic graphs.
|
|
23
|
+
*/
|
|
24
|
+
export declare const sanitizeSasInPayload: <T>(value: T) => T;
|