@microsoft/omnichannel-chat-widget 1.8.1-main.65a1ab5 → 1.8.1-main.6ec59e7
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/Constants.js +1 -0
- package/lib/cjs/common/telemetry/TelemetryConstants.js +1 -0
- package/lib/cjs/common/telemetry/TelemetryManager.js +2 -2
- package/lib/cjs/common/telemetry/loggers/appInsightsLogger.js +4 -5
- package/lib/cjs/common/utils/xssUtils.js +79 -0
- package/lib/cjs/components/livechatwidget/common/startChat.js +1 -7
- package/lib/cjs/components/ooohpanestateful/OOOHPaneStateful.js +23 -2
- package/lib/cjs/firstresponselatency/util.js +5 -3
- package/lib/esm/common/Constants.js +1 -0
- package/lib/esm/common/telemetry/TelemetryConstants.js +1 -0
- package/lib/esm/common/telemetry/TelemetryManager.js +2 -2
- package/lib/esm/common/telemetry/loggers/appInsightsLogger.js +4 -5
- package/lib/esm/common/utils/xssUtils.js +72 -0
- package/lib/esm/components/livechatwidget/common/startChat.js +1 -7
- package/lib/esm/components/ooohpanestateful/OOOHPaneStateful.js +23 -2
- package/lib/esm/firstresponselatency/util.js +5 -3
- package/lib/types/common/Constants.d.ts +1 -0
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +1 -0
- package/lib/types/common/telemetry/loggers/appInsightsLogger.d.ts +1 -1
- package/lib/types/common/utils/xssUtils.d.ts +29 -0
- package/package.json +1 -1
|
@@ -19,6 +19,7 @@ _defineProperty(Constants, "magicCodeBroadcastChannel", "MagicCodeChannel");
|
|
|
19
19
|
_defineProperty(Constants, "magicCodeResponseBroadcastChannel", "MagicCodeResponseChannel");
|
|
20
20
|
_defineProperty(Constants, "systemMessageTag", "system");
|
|
21
21
|
_defineProperty(Constants, "userMessageTag", "user");
|
|
22
|
+
_defineProperty(Constants, "channelMessageTag", "channel");
|
|
22
23
|
_defineProperty(Constants, "historyMessageTag", "history");
|
|
23
24
|
_defineProperty(Constants, "agentEndConversationMessageTag", "agentendconversation");
|
|
24
25
|
_defineProperty(Constants, "supervisorForceCloseMessageTag", "supervisorforceclosedconversation");
|
|
@@ -256,6 +256,7 @@ exports.TelemetryEvent = TelemetryEvent;
|
|
|
256
256
|
TelemetryEvent["UXNotificationPaneCompleted"] = "UXNotificationPaneCompleted";
|
|
257
257
|
TelemetryEvent["UXOutOfOfficeHoursPaneStart"] = "UXOutOfOfficeHoursPaneStart";
|
|
258
258
|
TelemetryEvent["UXOutOfOfficeHoursPaneCompleted"] = "UXOutOfOfficeHoursPaneCompleted";
|
|
259
|
+
TelemetryEvent["XSSTextDetected"] = "XSSTextDetected";
|
|
259
260
|
TelemetryEvent["UXPostChatLoadingPaneStart"] = "UXPostChatLoadingPaneStart";
|
|
260
261
|
TelemetryEvent["UXPostChatLoadingPaneCompleted"] = "UXPostChatLoadingPaneCompleted";
|
|
261
262
|
TelemetryEvent["UXPrechatPaneStart"] = "UXPrechatPaneStart";
|
|
@@ -64,8 +64,8 @@ const RegisterLoggers = () => {
|
|
|
64
64
|
if (((_TelemetryManager$Int22 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int22 === void 0 ? void 0 : (_TelemetryManager$Int23 = _TelemetryManager$Int22.appInsightsConfig) === null || _TelemetryManager$Int23 === void 0 ? void 0 : _TelemetryManager$Int23.appInsightsDisabled) === false) {
|
|
65
65
|
var _TelemetryManager$Int24;
|
|
66
66
|
if ((_TelemetryManager$Int24 = TelemetryManager.InternalTelemetryData) !== null && _TelemetryManager$Int24 !== void 0 && _TelemetryManager$Int24.appInsightsConfig.appInsightsKey) {
|
|
67
|
-
var _TelemetryManager$Int25
|
|
68
|
-
loggers.push((0, _appInsightsLogger.appInsightsLogger)((_TelemetryManager$Int25 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int25 === void 0 ? void 0 : _TelemetryManager$Int25.appInsightsConfig.appInsightsKey
|
|
67
|
+
var _TelemetryManager$Int25;
|
|
68
|
+
loggers.push((0, _appInsightsLogger.appInsightsLogger)((_TelemetryManager$Int25 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int25 === void 0 ? void 0 : _TelemetryManager$Int25.appInsightsConfig.appInsightsKey));
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -25,14 +25,14 @@ var AllowedKeys;
|
|
|
25
25
|
AllowedKeys["ElapsedTimeInMilliseconds"] = "DurationInMilliseconds";
|
|
26
26
|
})(AllowedKeys || (AllowedKeys = {}));
|
|
27
27
|
let initializationPromise = null;
|
|
28
|
-
const appInsightsLogger =
|
|
28
|
+
const appInsightsLogger = appInsightsKey => {
|
|
29
29
|
const isValidKey = key => {
|
|
30
30
|
const INSTRUMENTATION_KEY_PATTERN = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
|
|
31
31
|
const INSTRUMENTATION_KEY_REGEX = new RegExp(`^${INSTRUMENTATION_KEY_PATTERN}$`);
|
|
32
32
|
const CONNECTION_STRING_REGEX = new RegExp(`^InstrumentationKey=${INSTRUMENTATION_KEY_PATTERN};IngestionEndpoint=https://[a-zA-Z0-9\\-\\.]+\\.applicationinsights\\.azure\\.com/.*`);
|
|
33
33
|
return INSTRUMENTATION_KEY_REGEX.test(key) || CONNECTION_STRING_REGEX.test(key);
|
|
34
34
|
};
|
|
35
|
-
const initializeAppInsights = async
|
|
35
|
+
const initializeAppInsights = async appInsightsKey => {
|
|
36
36
|
if (!window.appInsights && appInsightsKey) {
|
|
37
37
|
if (!isValidKey(appInsightsKey)) {
|
|
38
38
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.ERROR, {
|
|
@@ -54,8 +54,7 @@ const appInsightsLogger = (appInsightsKey, disableCookiesUsage) => {
|
|
|
54
54
|
connectionString: appInsightsKey
|
|
55
55
|
} : {
|
|
56
56
|
instrumentationKey: appInsightsKey
|
|
57
|
-
})
|
|
58
|
-
disableCookiesUsage: disableCookiesUsage
|
|
57
|
+
})
|
|
59
58
|
};
|
|
60
59
|
|
|
61
60
|
// Initialize Application Insights instance
|
|
@@ -82,7 +81,7 @@ const appInsightsLogger = (appInsightsKey, disableCookiesUsage) => {
|
|
|
82
81
|
};
|
|
83
82
|
const logger = async () => {
|
|
84
83
|
if (!initializationPromise) {
|
|
85
|
-
initializationPromise = initializeAppInsights(appInsightsKey
|
|
84
|
+
initializationPromise = initializeAppInsights(appInsightsKey);
|
|
86
85
|
}
|
|
87
86
|
await initializationPromise;
|
|
88
87
|
return window.appInsights;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.detectAndCleanXSS = void 0;
|
|
7
|
+
var _dompurify = _interopRequireDefault(require("dompurify"));
|
|
8
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
|
+
/**
|
|
10
|
+
* Detects potential Cross-Site Scripting (XSS) attacks in text input and sanitizes the content.
|
|
11
|
+
*
|
|
12
|
+
* This function performs comprehensive XSS detection using pattern matching for common attack vectors
|
|
13
|
+
* and then sanitizes the input using DOMPurify with strict configuration. It's designed to protect
|
|
14
|
+
* against various XSS techniques including script injection, event handler injection, style-based
|
|
15
|
+
* attacks, and encoded payloads.
|
|
16
|
+
*
|
|
17
|
+
* Security patterns detected:
|
|
18
|
+
* - JavaScript protocol URLs (javascript:)
|
|
19
|
+
* - HTML event handlers (onmouseover, onclick, etc.)
|
|
20
|
+
* - Script tags (<script>)
|
|
21
|
+
* - CSS expression() functions
|
|
22
|
+
* - CSS url() functions
|
|
23
|
+
* - Position-based CSS attacks (position: fixed/absolute)
|
|
24
|
+
* - VBScript protocol URLs
|
|
25
|
+
* - Data URLs with HTML content
|
|
26
|
+
* - Fragment identifiers with escaped quotes
|
|
27
|
+
* - HTML entity-encoded angle brackets
|
|
28
|
+
*
|
|
29
|
+
* @param text - The input text to be analyzed and sanitized
|
|
30
|
+
* @returns An object containing:
|
|
31
|
+
* - cleanText: The sanitized version of the input text with all HTML tags and attributes removed
|
|
32
|
+
* - isXSSDetected: Boolean flag indicating whether potential XSS patterns were found in the original text
|
|
33
|
+
*/
|
|
34
|
+
const detectAndCleanXSS = text => {
|
|
35
|
+
// Comprehensive array of regular expressions to detect common XSS attack patterns
|
|
36
|
+
const xssPatterns = [/javascript\s*:/gi,
|
|
37
|
+
// JavaScript protocol URLs (with optional spaces)
|
|
38
|
+
/vbscript\s*:/gi,
|
|
39
|
+
// VBScript protocol URLs (with optional spaces)
|
|
40
|
+
/on\w+\s*=/gi,
|
|
41
|
+
// HTML event handlers (onmouseover, onclick, onload, etc.)
|
|
42
|
+
/<\s*script/gi,
|
|
43
|
+
// Script tag opening (with optional spaces)
|
|
44
|
+
/expression\s*\(/gi,
|
|
45
|
+
// CSS expression() function (IE-specific)
|
|
46
|
+
/url\s*\(/gi,
|
|
47
|
+
// CSS url() function
|
|
48
|
+
/style\s*=.*position\s*:\s*fixed/gi,
|
|
49
|
+
// CSS position fixed attacks
|
|
50
|
+
/style\s*=.*position\s*:\s*absolute/gi,
|
|
51
|
+
// CSS position absolute attacks
|
|
52
|
+
/data\s*:\s*text\s*\/\s*html/gi,
|
|
53
|
+
// Data URLs containing HTML
|
|
54
|
+
/#.*\\"/gi,
|
|
55
|
+
// Fragment identifiers with escaped quotes
|
|
56
|
+
/>.*</gi // HTML entity-encoded angle brackets indicating tag structure
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Check if any XSS patterns are detected in the input text
|
|
60
|
+
const isXSSDetected = xssPatterns.some(pattern => pattern.test(text));
|
|
61
|
+
|
|
62
|
+
// Clean the text using DOMPurify with strict config
|
|
63
|
+
const cleanText = _dompurify.default.sanitize(text, {
|
|
64
|
+
ALLOWED_TAGS: [],
|
|
65
|
+
// No HTML tags allowed in title
|
|
66
|
+
ALLOWED_ATTR: [],
|
|
67
|
+
KEEP_CONTENT: true,
|
|
68
|
+
// Keep text content
|
|
69
|
+
ALLOW_DATA_ATTR: false,
|
|
70
|
+
ALLOW_UNKNOWN_PROTOCOLS: false,
|
|
71
|
+
SANITIZE_DOM: true,
|
|
72
|
+
FORCE_BODY: false
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
cleanText,
|
|
76
|
+
isXSSDetected
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
exports.detectAndCleanXSS = detectAndCleanXSS;
|
|
@@ -315,13 +315,7 @@ const canConnectToExistingChat = async (props, facadeChatSDK, state, dispatch, s
|
|
|
315
315
|
|
|
316
316
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
317
317
|
const setCustomContextParams = async (state, props) => {
|
|
318
|
-
var
|
|
319
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
320
|
-
const isAuthenticatedChat = props !== null && props !== void 0 && (_props$chatConfig = props.chatConfig) !== null && _props$chatConfig !== void 0 && (_props$chatConfig$Liv = _props$chatConfig.LiveChatConfigAuthSettings) !== null && _props$chatConfig$Liv !== void 0 && _props$chatConfig$Liv.msdyn_javascriptclientfunction ? true : false;
|
|
321
|
-
//Should not set custom context for auth chat
|
|
322
|
-
if (isAuthenticatedChat) {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
318
|
+
var _state$domainStates8, _persistedState$domai8;
|
|
325
319
|
if (state !== null && state !== void 0 && (_state$domainStates8 = state.domainStates) !== null && _state$domainStates8 !== void 0 && _state$domainStates8.customContext) {
|
|
326
320
|
var _state$domainStates9;
|
|
327
321
|
optionalParams = Object.assign({}, optionalParams, {
|
|
@@ -7,15 +7,16 @@ exports.default = exports.OutOfOfficeHoursPaneStateful = void 0;
|
|
|
7
7
|
var _TelemetryConstants = require("../../common/telemetry/TelemetryConstants");
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
var _utils = require("../../common/utils");
|
|
10
|
-
var _dompurify = _interopRequireDefault(require("dompurify"));
|
|
11
10
|
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
12
11
|
var _TelemetryHelper = require("../../common/telemetry/TelemetryHelper");
|
|
13
12
|
var _defaultgeneralOOOHPaneStyleProps = require("./common/defaultStyleProps/defaultgeneralOOOHPaneStyleProps");
|
|
13
|
+
var _xssUtils = require("../../common/utils/xssUtils");
|
|
14
14
|
var _useChatContextStore = _interopRequireDefault(require("../../hooks/useChatContextStore"));
|
|
15
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
16
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
17
17
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
18
18
|
let uiTimer;
|
|
19
|
+
const OOOHPaneTitleText = "Thanks for contacting us. You have reached us outside of our operating hours. An agent will respond when we open.";
|
|
19
20
|
const OutOfOfficeHoursPaneStateful = props => {
|
|
20
21
|
var _props$styleProps;
|
|
21
22
|
(0, _react.useEffect)(() => {
|
|
@@ -54,8 +55,28 @@ const OutOfOfficeHoursPaneStateful = props => {
|
|
|
54
55
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
55
56
|
});
|
|
56
57
|
}, []);
|
|
58
|
+
|
|
59
|
+
// Enhanced titleText sanitization
|
|
57
60
|
if (controlProps !== null && controlProps !== void 0 && controlProps.titleText) {
|
|
58
|
-
|
|
61
|
+
const {
|
|
62
|
+
cleanText,
|
|
63
|
+
isXSSDetected
|
|
64
|
+
} = (0, _xssUtils.detectAndCleanXSS)(controlProps.titleText);
|
|
65
|
+
if (!isXSSDetected) {
|
|
66
|
+
// replace with the sanitized text
|
|
67
|
+
controlProps.titleText = cleanText;
|
|
68
|
+
} else {
|
|
69
|
+
_TelemetryHelper.TelemetryHelper.logLoadingEventToAllTelemetry(_TelemetryConstants.LogLevel.WARN, {
|
|
70
|
+
Event: _TelemetryConstants.TelemetryEvent.XSSTextDetected,
|
|
71
|
+
Description: "Potential XSS attempt detected in titleText",
|
|
72
|
+
CustomProperties: {
|
|
73
|
+
originalText: controlProps.titleText.substring(0, 100),
|
|
74
|
+
// Log first 100 chars for analysis
|
|
75
|
+
cleanedText: cleanText.substring(0, 100)
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
controlProps.titleText = OOOHPaneTitleText;
|
|
79
|
+
}
|
|
59
80
|
}
|
|
60
81
|
return /*#__PURE__*/_react.default.createElement(_omnichannelChatComponents.OutOfOfficeHoursPane, {
|
|
61
82
|
componentOverrides: props.componentOverrides,
|
|
@@ -73,11 +73,13 @@ const polyfillMessagePayloadForEvent = (activity, payload, conversationId) => {
|
|
|
73
73
|
};
|
|
74
74
|
exports.polyfillMessagePayloadForEvent = polyfillMessagePayloadForEvent;
|
|
75
75
|
const getScenarioType = activity => {
|
|
76
|
-
var _activity$from3, _activity$channelData4
|
|
77
|
-
|
|
76
|
+
var _activity$from3, _activity$channelData4;
|
|
77
|
+
const role = activity === null || activity === void 0 ? void 0 : (_activity$from3 = activity.from) === null || _activity$from3 === void 0 ? void 0 : _activity$from3.role;
|
|
78
|
+
const tags = activity === null || activity === void 0 ? void 0 : (_activity$channelData4 = activity.channelData) === null || _activity$channelData4 === void 0 ? void 0 : _activity$channelData4.tags;
|
|
79
|
+
if (role === _Constants2.Constants.userMessageTag) {
|
|
78
80
|
return _Constants.ScenarioType.UserSendMessageStrategy;
|
|
79
81
|
}
|
|
80
|
-
if (
|
|
82
|
+
if (tags && tags.includes(_Constants2.Constants.systemMessageTag) || role === _Constants2.Constants.channelMessageTag) {
|
|
81
83
|
return _Constants.ScenarioType.SystemMessageStrategy;
|
|
82
84
|
}
|
|
83
85
|
return _Constants.ScenarioType.ReceivedMessageStrategy;
|
|
@@ -12,6 +12,7 @@ _defineProperty(Constants, "magicCodeBroadcastChannel", "MagicCodeChannel");
|
|
|
12
12
|
_defineProperty(Constants, "magicCodeResponseBroadcastChannel", "MagicCodeResponseChannel");
|
|
13
13
|
_defineProperty(Constants, "systemMessageTag", "system");
|
|
14
14
|
_defineProperty(Constants, "userMessageTag", "user");
|
|
15
|
+
_defineProperty(Constants, "channelMessageTag", "channel");
|
|
15
16
|
_defineProperty(Constants, "historyMessageTag", "history");
|
|
16
17
|
_defineProperty(Constants, "agentEndConversationMessageTag", "agentendconversation");
|
|
17
18
|
_defineProperty(Constants, "supervisorForceCloseMessageTag", "supervisorforceclosedconversation");
|
|
@@ -250,6 +250,7 @@ export let TelemetryEvent;
|
|
|
250
250
|
TelemetryEvent["UXNotificationPaneCompleted"] = "UXNotificationPaneCompleted";
|
|
251
251
|
TelemetryEvent["UXOutOfOfficeHoursPaneStart"] = "UXOutOfOfficeHoursPaneStart";
|
|
252
252
|
TelemetryEvent["UXOutOfOfficeHoursPaneCompleted"] = "UXOutOfOfficeHoursPaneCompleted";
|
|
253
|
+
TelemetryEvent["XSSTextDetected"] = "XSSTextDetected";
|
|
253
254
|
TelemetryEvent["UXPostChatLoadingPaneStart"] = "UXPostChatLoadingPaneStart";
|
|
254
255
|
TelemetryEvent["UXPostChatLoadingPaneCompleted"] = "UXPostChatLoadingPaneCompleted";
|
|
255
256
|
TelemetryEvent["UXPrechatPaneStart"] = "UXPrechatPaneStart";
|
|
@@ -55,8 +55,8 @@ export const RegisterLoggers = () => {
|
|
|
55
55
|
if (((_TelemetryManager$Int22 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int22 === void 0 ? void 0 : (_TelemetryManager$Int23 = _TelemetryManager$Int22.appInsightsConfig) === null || _TelemetryManager$Int23 === void 0 ? void 0 : _TelemetryManager$Int23.appInsightsDisabled) === false) {
|
|
56
56
|
var _TelemetryManager$Int24;
|
|
57
57
|
if ((_TelemetryManager$Int24 = TelemetryManager.InternalTelemetryData) !== null && _TelemetryManager$Int24 !== void 0 && _TelemetryManager$Int24.appInsightsConfig.appInsightsKey) {
|
|
58
|
-
var _TelemetryManager$Int25
|
|
59
|
-
loggers.push(appInsightsLogger((_TelemetryManager$Int25 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int25 === void 0 ? void 0 : _TelemetryManager$Int25.appInsightsConfig.appInsightsKey
|
|
58
|
+
var _TelemetryManager$Int25;
|
|
59
|
+
loggers.push(appInsightsLogger((_TelemetryManager$Int25 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int25 === void 0 ? void 0 : _TelemetryManager$Int25.appInsightsConfig.appInsightsKey));
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -16,14 +16,14 @@ var AllowedKeys;
|
|
|
16
16
|
AllowedKeys["ElapsedTimeInMilliseconds"] = "DurationInMilliseconds";
|
|
17
17
|
})(AllowedKeys || (AllowedKeys = {}));
|
|
18
18
|
let initializationPromise = null;
|
|
19
|
-
export const appInsightsLogger =
|
|
19
|
+
export const appInsightsLogger = appInsightsKey => {
|
|
20
20
|
const isValidKey = key => {
|
|
21
21
|
const INSTRUMENTATION_KEY_PATTERN = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
|
|
22
22
|
const INSTRUMENTATION_KEY_REGEX = new RegExp(`^${INSTRUMENTATION_KEY_PATTERN}$`);
|
|
23
23
|
const CONNECTION_STRING_REGEX = new RegExp(`^InstrumentationKey=${INSTRUMENTATION_KEY_PATTERN};IngestionEndpoint=https://[a-zA-Z0-9\\-\\.]+\\.applicationinsights\\.azure\\.com/.*`);
|
|
24
24
|
return INSTRUMENTATION_KEY_REGEX.test(key) || CONNECTION_STRING_REGEX.test(key);
|
|
25
25
|
};
|
|
26
|
-
const initializeAppInsights = async
|
|
26
|
+
const initializeAppInsights = async appInsightsKey => {
|
|
27
27
|
if (!window.appInsights && appInsightsKey) {
|
|
28
28
|
if (!isValidKey(appInsightsKey)) {
|
|
29
29
|
TelemetryHelper.logActionEvent(LogLevel.ERROR, {
|
|
@@ -45,8 +45,7 @@ export const appInsightsLogger = (appInsightsKey, disableCookiesUsage) => {
|
|
|
45
45
|
connectionString: appInsightsKey
|
|
46
46
|
} : {
|
|
47
47
|
instrumentationKey: appInsightsKey
|
|
48
|
-
})
|
|
49
|
-
disableCookiesUsage: disableCookiesUsage
|
|
48
|
+
})
|
|
50
49
|
};
|
|
51
50
|
|
|
52
51
|
// Initialize Application Insights instance
|
|
@@ -73,7 +72,7 @@ export const appInsightsLogger = (appInsightsKey, disableCookiesUsage) => {
|
|
|
73
72
|
};
|
|
74
73
|
const logger = async () => {
|
|
75
74
|
if (!initializationPromise) {
|
|
76
|
-
initializationPromise = initializeAppInsights(appInsightsKey
|
|
75
|
+
initializationPromise = initializeAppInsights(appInsightsKey);
|
|
77
76
|
}
|
|
78
77
|
await initializationPromise;
|
|
79
78
|
return window.appInsights;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import DOMPurify from "dompurify";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Detects potential Cross-Site Scripting (XSS) attacks in text input and sanitizes the content.
|
|
5
|
+
*
|
|
6
|
+
* This function performs comprehensive XSS detection using pattern matching for common attack vectors
|
|
7
|
+
* and then sanitizes the input using DOMPurify with strict configuration. It's designed to protect
|
|
8
|
+
* against various XSS techniques including script injection, event handler injection, style-based
|
|
9
|
+
* attacks, and encoded payloads.
|
|
10
|
+
*
|
|
11
|
+
* Security patterns detected:
|
|
12
|
+
* - JavaScript protocol URLs (javascript:)
|
|
13
|
+
* - HTML event handlers (onmouseover, onclick, etc.)
|
|
14
|
+
* - Script tags (<script>)
|
|
15
|
+
* - CSS expression() functions
|
|
16
|
+
* - CSS url() functions
|
|
17
|
+
* - Position-based CSS attacks (position: fixed/absolute)
|
|
18
|
+
* - VBScript protocol URLs
|
|
19
|
+
* - Data URLs with HTML content
|
|
20
|
+
* - Fragment identifiers with escaped quotes
|
|
21
|
+
* - HTML entity-encoded angle brackets
|
|
22
|
+
*
|
|
23
|
+
* @param text - The input text to be analyzed and sanitized
|
|
24
|
+
* @returns An object containing:
|
|
25
|
+
* - cleanText: The sanitized version of the input text with all HTML tags and attributes removed
|
|
26
|
+
* - isXSSDetected: Boolean flag indicating whether potential XSS patterns were found in the original text
|
|
27
|
+
*/
|
|
28
|
+
export const detectAndCleanXSS = text => {
|
|
29
|
+
// Comprehensive array of regular expressions to detect common XSS attack patterns
|
|
30
|
+
const xssPatterns = [/javascript\s*:/gi,
|
|
31
|
+
// JavaScript protocol URLs (with optional spaces)
|
|
32
|
+
/vbscript\s*:/gi,
|
|
33
|
+
// VBScript protocol URLs (with optional spaces)
|
|
34
|
+
/on\w+\s*=/gi,
|
|
35
|
+
// HTML event handlers (onmouseover, onclick, onload, etc.)
|
|
36
|
+
/<\s*script/gi,
|
|
37
|
+
// Script tag opening (with optional spaces)
|
|
38
|
+
/expression\s*\(/gi,
|
|
39
|
+
// CSS expression() function (IE-specific)
|
|
40
|
+
/url\s*\(/gi,
|
|
41
|
+
// CSS url() function
|
|
42
|
+
/style\s*=.*position\s*:\s*fixed/gi,
|
|
43
|
+
// CSS position fixed attacks
|
|
44
|
+
/style\s*=.*position\s*:\s*absolute/gi,
|
|
45
|
+
// CSS position absolute attacks
|
|
46
|
+
/data\s*:\s*text\s*\/\s*html/gi,
|
|
47
|
+
// Data URLs containing HTML
|
|
48
|
+
/#.*\\"/gi,
|
|
49
|
+
// Fragment identifiers with escaped quotes
|
|
50
|
+
/>.*</gi // HTML entity-encoded angle brackets indicating tag structure
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Check if any XSS patterns are detected in the input text
|
|
54
|
+
const isXSSDetected = xssPatterns.some(pattern => pattern.test(text));
|
|
55
|
+
|
|
56
|
+
// Clean the text using DOMPurify with strict config
|
|
57
|
+
const cleanText = DOMPurify.sanitize(text, {
|
|
58
|
+
ALLOWED_TAGS: [],
|
|
59
|
+
// No HTML tags allowed in title
|
|
60
|
+
ALLOWED_ATTR: [],
|
|
61
|
+
KEEP_CONTENT: true,
|
|
62
|
+
// Keep text content
|
|
63
|
+
ALLOW_DATA_ATTR: false,
|
|
64
|
+
ALLOW_UNKNOWN_PROTOCOLS: false,
|
|
65
|
+
SANITIZE_DOM: true,
|
|
66
|
+
FORCE_BODY: false
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
cleanText,
|
|
70
|
+
isXSSDetected
|
|
71
|
+
};
|
|
72
|
+
};
|
|
@@ -307,13 +307,7 @@ const canConnectToExistingChat = async (props, facadeChatSDK, state, dispatch, s
|
|
|
307
307
|
|
|
308
308
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
309
309
|
const setCustomContextParams = async (state, props) => {
|
|
310
|
-
var
|
|
311
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
312
|
-
const isAuthenticatedChat = props !== null && props !== void 0 && (_props$chatConfig = props.chatConfig) !== null && _props$chatConfig !== void 0 && (_props$chatConfig$Liv = _props$chatConfig.LiveChatConfigAuthSettings) !== null && _props$chatConfig$Liv !== void 0 && _props$chatConfig$Liv.msdyn_javascriptclientfunction ? true : false;
|
|
313
|
-
//Should not set custom context for auth chat
|
|
314
|
-
if (isAuthenticatedChat) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
310
|
+
var _state$domainStates8, _persistedState$domai8;
|
|
317
311
|
if (state !== null && state !== void 0 && (_state$domainStates8 = state.domainStates) !== null && _state$domainStates8 !== void 0 && _state$domainStates8.customContext) {
|
|
318
312
|
var _state$domainStates9;
|
|
319
313
|
optionalParams = Object.assign({}, optionalParams, {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { LogLevel, TelemetryEvent } from "../../common/telemetry/TelemetryConstants";
|
|
2
2
|
import React, { useEffect } from "react";
|
|
3
3
|
import { createTimer, findAllFocusableElement } from "../../common/utils";
|
|
4
|
-
import DOMPurify from "dompurify";
|
|
5
4
|
import { OutOfOfficeHoursPane } from "@microsoft/omnichannel-chat-components";
|
|
6
5
|
import { TelemetryHelper } from "../../common/telemetry/TelemetryHelper";
|
|
7
6
|
import { defaultGeneralStyleProps } from "./common/defaultStyleProps/defaultgeneralOOOHPaneStyleProps";
|
|
7
|
+
import { detectAndCleanXSS } from "../../common/utils/xssUtils";
|
|
8
8
|
import useChatContextStore from "../../hooks/useChatContextStore";
|
|
9
9
|
let uiTimer;
|
|
10
|
+
const OOOHPaneTitleText = "Thanks for contacting us. You have reached us outside of our operating hours. An agent will respond when we open.";
|
|
10
11
|
export const OutOfOfficeHoursPaneStateful = props => {
|
|
11
12
|
var _props$styleProps;
|
|
12
13
|
useEffect(() => {
|
|
@@ -45,8 +46,28 @@ export const OutOfOfficeHoursPaneStateful = props => {
|
|
|
45
46
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
46
47
|
});
|
|
47
48
|
}, []);
|
|
49
|
+
|
|
50
|
+
// Enhanced titleText sanitization
|
|
48
51
|
if (controlProps !== null && controlProps !== void 0 && controlProps.titleText) {
|
|
49
|
-
|
|
52
|
+
const {
|
|
53
|
+
cleanText,
|
|
54
|
+
isXSSDetected
|
|
55
|
+
} = detectAndCleanXSS(controlProps.titleText);
|
|
56
|
+
if (!isXSSDetected) {
|
|
57
|
+
// replace with the sanitized text
|
|
58
|
+
controlProps.titleText = cleanText;
|
|
59
|
+
} else {
|
|
60
|
+
TelemetryHelper.logLoadingEventToAllTelemetry(LogLevel.WARN, {
|
|
61
|
+
Event: TelemetryEvent.XSSTextDetected,
|
|
62
|
+
Description: "Potential XSS attempt detected in titleText",
|
|
63
|
+
CustomProperties: {
|
|
64
|
+
originalText: controlProps.titleText.substring(0, 100),
|
|
65
|
+
// Log first 100 chars for analysis
|
|
66
|
+
cleanedText: cleanText.substring(0, 100)
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
controlProps.titleText = OOOHPaneTitleText;
|
|
70
|
+
}
|
|
50
71
|
}
|
|
51
72
|
return /*#__PURE__*/React.createElement(OutOfOfficeHoursPane, {
|
|
52
73
|
componentOverrides: props.componentOverrides,
|
|
@@ -64,11 +64,13 @@ export const polyfillMessagePayloadForEvent = (activity, payload, conversationId
|
|
|
64
64
|
};
|
|
65
65
|
};
|
|
66
66
|
export const getScenarioType = activity => {
|
|
67
|
-
var _activity$from3, _activity$channelData4
|
|
68
|
-
|
|
67
|
+
var _activity$from3, _activity$channelData4;
|
|
68
|
+
const role = activity === null || activity === void 0 ? void 0 : (_activity$from3 = activity.from) === null || _activity$from3 === void 0 ? void 0 : _activity$from3.role;
|
|
69
|
+
const tags = activity === null || activity === void 0 ? void 0 : (_activity$channelData4 = activity.channelData) === null || _activity$channelData4 === void 0 ? void 0 : _activity$channelData4.tags;
|
|
70
|
+
if (role === Constants.userMessageTag) {
|
|
69
71
|
return ScenarioType.UserSendMessageStrategy;
|
|
70
72
|
}
|
|
71
|
-
if (
|
|
73
|
+
if (tags && tags.includes(Constants.systemMessageTag) || role === Constants.channelMessageTag) {
|
|
72
74
|
return ScenarioType.SystemMessageStrategy;
|
|
73
75
|
}
|
|
74
76
|
return ScenarioType.ReceivedMessageStrategy;
|
|
@@ -3,6 +3,7 @@ export declare class Constants {
|
|
|
3
3
|
static readonly magicCodeResponseBroadcastChannel = "MagicCodeResponseChannel";
|
|
4
4
|
static readonly systemMessageTag = "system";
|
|
5
5
|
static readonly userMessageTag = "user";
|
|
6
|
+
static readonly channelMessageTag = "channel";
|
|
6
7
|
static readonly historyMessageTag = "history";
|
|
7
8
|
static readonly agentEndConversationMessageTag = "agentendconversation";
|
|
8
9
|
static readonly supervisorForceCloseMessageTag = "supervisorforceclosedconversation";
|
|
@@ -238,6 +238,7 @@ export declare enum TelemetryEvent {
|
|
|
238
238
|
UXNotificationPaneCompleted = "UXNotificationPaneCompleted",
|
|
239
239
|
UXOutOfOfficeHoursPaneStart = "UXOutOfOfficeHoursPaneStart",
|
|
240
240
|
UXOutOfOfficeHoursPaneCompleted = "UXOutOfOfficeHoursPaneCompleted",
|
|
241
|
+
XSSTextDetected = "XSSTextDetected",
|
|
241
242
|
UXPostChatLoadingPaneStart = "UXPostChatLoadingPaneStart",
|
|
242
243
|
UXPostChatLoadingPaneCompleted = "UXPostChatLoadingPaneCompleted",
|
|
243
244
|
UXPrechatPaneStart = "UXPrechatPaneStart",
|
|
@@ -4,7 +4,7 @@ declare global {
|
|
|
4
4
|
appInsights?: any;
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
-
export declare const appInsightsLogger: (appInsightsKey: string
|
|
7
|
+
export declare const appInsightsLogger: (appInsightsKey: string) => IChatSDKLogger;
|
|
8
8
|
export interface ICustomProperties {
|
|
9
9
|
[key: string]: any;
|
|
10
10
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects potential Cross-Site Scripting (XSS) attacks in text input and sanitizes the content.
|
|
3
|
+
*
|
|
4
|
+
* This function performs comprehensive XSS detection using pattern matching for common attack vectors
|
|
5
|
+
* and then sanitizes the input using DOMPurify with strict configuration. It's designed to protect
|
|
6
|
+
* against various XSS techniques including script injection, event handler injection, style-based
|
|
7
|
+
* attacks, and encoded payloads.
|
|
8
|
+
*
|
|
9
|
+
* Security patterns detected:
|
|
10
|
+
* - JavaScript protocol URLs (javascript:)
|
|
11
|
+
* - HTML event handlers (onmouseover, onclick, etc.)
|
|
12
|
+
* - Script tags (<script>)
|
|
13
|
+
* - CSS expression() functions
|
|
14
|
+
* - CSS url() functions
|
|
15
|
+
* - Position-based CSS attacks (position: fixed/absolute)
|
|
16
|
+
* - VBScript protocol URLs
|
|
17
|
+
* - Data URLs with HTML content
|
|
18
|
+
* - Fragment identifiers with escaped quotes
|
|
19
|
+
* - HTML entity-encoded angle brackets
|
|
20
|
+
*
|
|
21
|
+
* @param text - The input text to be analyzed and sanitized
|
|
22
|
+
* @returns An object containing:
|
|
23
|
+
* - cleanText: The sanitized version of the input text with all HTML tags and attributes removed
|
|
24
|
+
* - isXSSDetected: Boolean flag indicating whether potential XSS patterns were found in the original text
|
|
25
|
+
*/
|
|
26
|
+
export declare const detectAndCleanXSS: (text: string) => {
|
|
27
|
+
cleanText: string;
|
|
28
|
+
isXSSDetected: boolean;
|
|
29
|
+
};
|