@microsoft/omnichannel-chat-widget 1.8.4-main.f45c44b → 1.8.4-main.f8ae4f8
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/livechatwidget/common/createMarkdown.js +4 -2
- package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +16 -12
- package/lib/cjs/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +5 -1
- 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/livechatwidget/common/createMarkdown.js +4 -2
- package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +16 -12
- package/lib/esm/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +5 -1
- package/lib/esm/components/prechatsurveypanestateful/common/defaultStyles/defaultGeneralPreChatSurveyPaneStyleProps.js +1 -2
- package/lib/types/common/telemetry/sanitizeSasInPayload.d.ts +24 -0
- package/package.json +3 -3
|
@@ -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;
|
|
@@ -173,14 +173,16 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
173
173
|
|
|
174
174
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
175
|
md.render = function (text, env) {
|
|
176
|
+
const safeEnv = env ?? {};
|
|
176
177
|
const processedText = preprocessText(text);
|
|
177
|
-
return md.renderer.render(md.parse(processedText,
|
|
178
|
+
return md.renderer.render(md.parse(processedText, safeEnv), md.options, safeEnv);
|
|
178
179
|
};
|
|
179
180
|
|
|
180
181
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
181
182
|
md.renderInline = function (text, env) {
|
|
183
|
+
const safeEnv = env ?? {};
|
|
182
184
|
const processedText = preprocessText(text);
|
|
183
|
-
return md.renderer.render(md.parseInline(processedText,
|
|
185
|
+
return md.renderer.render(md.parseInline(processedText, safeEnv), md.options, safeEnv);
|
|
184
186
|
};
|
|
185
187
|
});
|
|
186
188
|
|
|
@@ -304,24 +304,28 @@ const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endChat) =>
|
|
|
304
304
|
const conversationId = (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.chatToken) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.chatId) || "unknown";
|
|
305
305
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
306
306
|
Event: _TelemetryConstants.TelemetryEvent.HTMLSanitized,
|
|
307
|
-
Description:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
307
|
+
Description: JSON.stringify({
|
|
308
|
+
message: "HTML content would be sanitized by stricter allowlist (monitor-only)",
|
|
309
|
+
removedTags: uniqueTags,
|
|
310
|
+
removedAttributes: uniqueAttrs,
|
|
311
|
+
phase: "Monitor",
|
|
312
|
+
organizationId: orgId,
|
|
313
|
+
conversationId: conversationId
|
|
314
|
+
}),
|
|
315
|
+
ElapsedTimeInMilliseconds: executionTimeMs
|
|
316
316
|
});
|
|
317
317
|
|
|
318
318
|
// Log to console in development for debugging
|
|
319
319
|
if (process.env.NODE_ENV === "development") {
|
|
320
|
-
console.warn("[Monitor] Stricter HTML sanitization
|
|
320
|
+
console.warn("[Monitor] Stricter HTML sanitization telemetry:", {
|
|
321
|
+
description: JSON.parse(JSON.stringify({
|
|
322
|
+
message: "HTML content would be sanitized by stricter allowlist (monitor-only)",
|
|
323
|
+
removedTags: uniqueTags,
|
|
324
|
+
removedAttributes: uniqueAttrs,
|
|
325
|
+
phase: "Monitor"
|
|
326
|
+
})),
|
|
321
327
|
orgId,
|
|
322
328
|
conversationId,
|
|
323
|
-
removedTags: uniqueTags,
|
|
324
|
-
removedAttributes: uniqueAttrs,
|
|
325
329
|
executionTimeMs
|
|
326
330
|
});
|
|
327
331
|
}
|
|
@@ -221,7 +221,11 @@ const PreChatSurveyPaneStateful = props => {
|
|
|
221
221
|
}
|
|
222
222
|
}, []);
|
|
223
223
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
224
|
-
onFocusCapture: announceFocusedElement
|
|
224
|
+
onFocusCapture: announceFocusedElement,
|
|
225
|
+
style: {
|
|
226
|
+
width: "100%",
|
|
227
|
+
height: "100%"
|
|
228
|
+
}
|
|
225
229
|
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
226
230
|
role: "status",
|
|
227
231
|
"aria-live": "polite",
|
|
@@ -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
|
+
};
|
|
@@ -167,14 +167,16 @@ export const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineM
|
|
|
167
167
|
|
|
168
168
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
169
169
|
md.render = function (text, env) {
|
|
170
|
+
const safeEnv = env ?? {};
|
|
170
171
|
const processedText = preprocessText(text);
|
|
171
|
-
return md.renderer.render(md.parse(processedText,
|
|
172
|
+
return md.renderer.render(md.parse(processedText, safeEnv), md.options, safeEnv);
|
|
172
173
|
};
|
|
173
174
|
|
|
174
175
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
176
|
md.renderInline = function (text, env) {
|
|
177
|
+
const safeEnv = env ?? {};
|
|
176
178
|
const processedText = preprocessText(text);
|
|
177
|
-
return md.renderer.render(md.parseInline(processedText,
|
|
179
|
+
return md.renderer.render(md.parseInline(processedText, safeEnv), md.options, safeEnv);
|
|
178
180
|
};
|
|
179
181
|
});
|
|
180
182
|
|
|
@@ -299,24 +299,28 @@ export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endCh
|
|
|
299
299
|
const conversationId = (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.chatToken) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.chatId) || "unknown";
|
|
300
300
|
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
301
301
|
Event: TelemetryEvent.HTMLSanitized,
|
|
302
|
-
Description:
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
302
|
+
Description: JSON.stringify({
|
|
303
|
+
message: "HTML content would be sanitized by stricter allowlist (monitor-only)",
|
|
304
|
+
removedTags: uniqueTags,
|
|
305
|
+
removedAttributes: uniqueAttrs,
|
|
306
|
+
phase: "Monitor",
|
|
307
|
+
organizationId: orgId,
|
|
308
|
+
conversationId: conversationId
|
|
309
|
+
}),
|
|
310
|
+
ElapsedTimeInMilliseconds: executionTimeMs
|
|
311
311
|
});
|
|
312
312
|
|
|
313
313
|
// Log to console in development for debugging
|
|
314
314
|
if (process.env.NODE_ENV === "development") {
|
|
315
|
-
console.warn("[Monitor] Stricter HTML sanitization
|
|
315
|
+
console.warn("[Monitor] Stricter HTML sanitization telemetry:", {
|
|
316
|
+
description: JSON.parse(JSON.stringify({
|
|
317
|
+
message: "HTML content would be sanitized by stricter allowlist (monitor-only)",
|
|
318
|
+
removedTags: uniqueTags,
|
|
319
|
+
removedAttributes: uniqueAttrs,
|
|
320
|
+
phase: "Monitor"
|
|
321
|
+
})),
|
|
316
322
|
orgId,
|
|
317
323
|
conversationId,
|
|
318
|
-
removedTags: uniqueTags,
|
|
319
|
-
removedAttributes: uniqueAttrs,
|
|
320
324
|
executionTimeMs
|
|
321
325
|
});
|
|
322
326
|
}
|
|
@@ -212,7 +212,11 @@ export const PreChatSurveyPaneStateful = props => {
|
|
|
212
212
|
}
|
|
213
213
|
}, []);
|
|
214
214
|
return /*#__PURE__*/React.createElement("div", {
|
|
215
|
-
onFocusCapture: announceFocusedElement
|
|
215
|
+
onFocusCapture: announceFocusedElement,
|
|
216
|
+
style: {
|
|
217
|
+
width: "100%",
|
|
218
|
+
height: "100%"
|
|
219
|
+
}
|
|
216
220
|
}, /*#__PURE__*/React.createElement("div", {
|
|
217
221
|
role: "status",
|
|
218
222
|
"aria-live": "polite",
|
|
@@ -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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/omnichannel-chat-widget",
|
|
3
|
-
"version": "1.8.4-main.
|
|
3
|
+
"version": "1.8.4-main.f8ae4f8",
|
|
4
4
|
"description": "Microsoft Omnichannel Chat Widget",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"types": "lib/types/index.d.ts",
|
|
@@ -95,8 +95,8 @@
|
|
|
95
95
|
"dependencies": {
|
|
96
96
|
"@azure/core-tracing": "^1.2.0",
|
|
97
97
|
"@microsoft/applicationinsights-web": "^3.3.6",
|
|
98
|
-
"@microsoft/omnichannel-chat-components": "1.1.17-main.
|
|
99
|
-
"@microsoft/omnichannel-chat-sdk": "1.11.9-main.
|
|
98
|
+
"@microsoft/omnichannel-chat-components": "1.1.17-main.05edfd3",
|
|
99
|
+
"@microsoft/omnichannel-chat-sdk": "1.11.9-main.47a6498",
|
|
100
100
|
"@opentelemetry/api": "^1.9.0",
|
|
101
101
|
"abort-controller": "^3",
|
|
102
102
|
"abort-controller-es5": "^2.0.1",
|