@capillarytech/creatives-library 8.0.263 → 8.0.265
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/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/constants/unified.js +1 -3
- package/initialReducer.js +0 -2
- package/package.json +1 -1
- package/services/api.js +0 -15
- package/services/tests/api.test.js +0 -34
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +35 -17
- package/tests/integration/TemplateCreation/api-response.js +1 -31
- package/tests/integration/TemplateCreation/msw-handler.js +0 -2
- package/utils/common.js +0 -11
- package/utils/commonUtils.js +5 -28
- package/utils/tests/commonUtil.test.js +0 -224
- package/utils/tests/transformerUtils.test.js +0 -297
- package/utils/transformTemplateConfig.js +10 -0
- package/utils/transformerUtils.js +0 -40
- package/v2Components/CapDeviceContent/index.js +56 -61
- package/v2Components/CapImageUpload/constants.js +0 -2
- package/v2Components/CapImageUpload/index.js +16 -65
- package/v2Components/CapImageUpload/index.scss +1 -4
- package/v2Components/CapImageUpload/messages.js +1 -5
- package/v2Components/CapTagList/index.js +1 -6
- package/v2Components/CapTagListWithInput/index.js +1 -5
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
- package/v2Components/ErrorInfoNote/index.js +72 -402
- package/v2Components/ErrorInfoNote/messages.js +6 -32
- package/v2Components/ErrorInfoNote/style.scss +6 -278
- package/v2Components/FormBuilder/tests/index.test.js +4 -13
- package/v2Components/HtmlEditor/HTMLEditor.js +99 -418
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1882
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
- package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -9
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +6 -31
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +10 -7
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -18
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -36
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -46
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +46 -52
- package/v2Components/HtmlEditor/constants.js +20 -45
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -351
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
- package/v2Components/HtmlEditor/hooks/useValidation.js +56 -213
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +94 -102
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -214
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
- package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +124 -158
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
- package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
- package/v2Components/MobilePushPreviewV2/index.js +7 -33
- package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
- package/v2Components/TemplatePreview/index.js +32 -47
- package/v2Components/TemplatePreview/messages.js +0 -4
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
- package/v2Containers/App/constants.js +0 -5
- package/v2Containers/BeeEditor/index.js +90 -172
- package/v2Containers/CreativesContainer/SlideBoxContent.js +53 -184
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -3
- package/v2Containers/CreativesContainer/constants.js +0 -4
- package/v2Containers/CreativesContainer/index.js +46 -408
- package/v2Containers/CreativesContainer/messages.js +0 -12
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -342
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -103
- package/v2Containers/Email/actions.js +0 -7
- package/v2Containers/Email/constants.js +1 -5
- package/v2Containers/Email/index.js +36 -237
- package/v2Containers/Email/messages.js +0 -32
- package/v2Containers/Email/reducer.js +1 -12
- package/v2Containers/Email/sagas.js +7 -61
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
- package/v2Containers/Email/tests/reducer.test.js +0 -46
- package/v2Containers/Email/tests/sagas.test.js +29 -320
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
- package/v2Containers/EmailWrapper/constants.js +0 -2
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
- package/v2Containers/EmailWrapper/index.js +23 -103
- package/v2Containers/EmailWrapper/messages.js +1 -65
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
- package/v2Containers/InApp/actions.js +0 -7
- package/v2Containers/InApp/constants.js +4 -20
- package/v2Containers/InApp/index.js +359 -802
- package/v2Containers/InApp/index.scss +3 -4
- package/v2Containers/InApp/messages.js +3 -7
- package/v2Containers/InApp/reducer.js +3 -21
- package/v2Containers/InApp/sagas.js +9 -29
- package/v2Containers/InApp/selectors.js +5 -25
- package/v2Containers/InApp/tests/index.test.js +50 -154
- package/v2Containers/InApp/tests/reducer.test.js +0 -34
- package/v2Containers/InApp/tests/sagas.test.js +9 -61
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
- package/v2Containers/TagList/index.js +19 -62
- package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
- package/v2Containers/Templates/_templates.scss +1 -265
- package/v2Containers/Templates/actions.js +1 -2
- package/v2Containers/Templates/constants.js +0 -1
- package/v2Containers/Templates/index.js +38 -363
- package/v2Containers/Templates/messages.js +0 -28
- package/v2Containers/Templates/reducer.js +0 -2
- package/v2Containers/Templates/tests/index.test.js +0 -10
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +2 -4
- package/v2Containers/TemplatesV2/index.js +7 -15
- package/v2Containers/TemplatesV2/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -34
- package/utils/imageUrlUpload.js +0 -141
- package/v2Components/CapImageUrlUpload/constants.js +0 -26
- package/v2Components/CapImageUrlUpload/index.js +0 -365
- package/v2Components/CapImageUrlUpload/index.scss +0 -35
- package/v2Components/CapImageUrlUpload/messages.js +0 -47
- package/v2Components/ErrorInfoNote/constants.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -870
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -281
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -295
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
- package/v2Components/HtmlEditor/utils/validationConstants.js +0 -38
- package/v2Components/MobilePushPreviewV2/constants.js +0 -6
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
- package/v2Containers/BeePopupEditor/constants.js +0 -10
- package/v2Containers/BeePopupEditor/index.js +0 -194
- package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1246
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -2472
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -956
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
- package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
- package/v2Containers/InApp/tests/selectors.test.js +0 -612
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
- package/v2Containers/InAppWrapper/constants.js +0 -16
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
- package/v2Containers/InAppWrapper/index.js +0 -148
- package/v2Containers/InAppWrapper/messages.js +0 -49
- package/v2Containers/InappAdvance/index.js +0 -1099
- package/v2Containers/InappAdvance/index.scss +0 -10
- package/v2Containers/InappAdvance/tests/index.test.js +0 -448
- package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
- package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
- package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
- package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
- package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
- package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
- package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
- package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
- package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
- package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
- package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
- package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
- package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
- package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
- package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
- package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
- package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
- package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
- package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
- package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
- package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
- package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
- package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
- package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
- package/v2Containers/WebPush/Create/index.js +0 -1148
- package/v2Containers/WebPush/Create/index.scss +0 -134
- package/v2Containers/WebPush/Create/messages.js +0 -211
- package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
- package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
- package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
- package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
- package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -25
- package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
- package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
- package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
- package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
- package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
- package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
- package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -51
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -145
- package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
- package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
- package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
- package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
- package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
- package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -85
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
- package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
- package/v2Containers/WebPush/Create/preview/constants.js +0 -637
- package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
- package/v2Containers/WebPush/Create/preview/preview.scss +0 -358
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
- package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
- package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
- package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
- package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
- package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
- package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
- package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
- package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
- package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
- package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
- package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
- package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
- package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
- package/v2Containers/WebPush/Create/utils/validation.js +0 -75
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
- package/v2Containers/WebPush/actions.js +0 -60
- package/v2Containers/WebPush/constants.js +0 -132
- package/v2Containers/WebPush/index.js +0 -2
- package/v2Containers/WebPush/reducer.js +0 -104
- package/v2Containers/WebPush/sagas.js +0 -119
- package/v2Containers/WebPush/selectors.js +0 -65
- package/v2Containers/WebPush/tests/reducer.test.js +0 -863
- package/v2Containers/WebPush/tests/sagas.test.js +0 -566
- package/v2Containers/WebPush/tests/selectors.test.js +0 -960
|
@@ -24,7 +24,7 @@ const defaultMessageFormatter = (messageKey, values = {}) => {
|
|
|
24
24
|
'sanitizer.productionValidHtml': 'Provide valid HTML content before deploying to production',
|
|
25
25
|
'sanitizer.productionSanitized': 'Content has been sanitized for security - review changes before deploying',
|
|
26
26
|
'sanitizer.productionInlineCss': 'Consider inlining CSS for better email client compatibility',
|
|
27
|
-
'sanitizer.productionLargeContent': `Content is large (${values.size || 'unknown'} characters) - consider optimizing for mobile performance
|
|
27
|
+
'sanitizer.productionLargeContent': `Content is large (${values.size || 'unknown'} characters) - consider optimizing for mobile performance`
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
return fallbackMessages[messageKey] || messageKey;
|
|
@@ -33,17 +33,17 @@ const defaultMessageFormatter = (messageKey, values = {}) => {
|
|
|
33
33
|
// Constants for better maintainability
|
|
34
34
|
const SANITIZER_VARIANTS = {
|
|
35
35
|
EMAIL: 'email',
|
|
36
|
-
INAPP: 'inapp'
|
|
36
|
+
INAPP: 'inapp'
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
const SECURITY_LEVELS = {
|
|
40
40
|
STANDARD: 'standard',
|
|
41
|
-
STRICT: 'strict'
|
|
41
|
+
STRICT: 'strict'
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
const CONTENT_LIMITS = {
|
|
45
45
|
LARGE_CONTENT_SIZE: 50000,
|
|
46
|
-
MIN_CONTENT_LENGTH: 0
|
|
46
|
+
MIN_CONTENT_LENGTH: 0
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const DANGEROUS_PROTOCOLS = ['javascript:', 'data:', 'vbscript:'];
|
|
@@ -51,7 +51,7 @@ const DANGEROUS_PROTOCOLS = ['javascript:', 'data:', 'vbscript:'];
|
|
|
51
51
|
const EVENT_HANDLERS = [
|
|
52
52
|
'onclick', 'onload', 'onerror', 'onmouseover', 'onmouseout',
|
|
53
53
|
'onmousedown', 'onmouseup', 'onkeydown', 'onkeyup', 'onkeypress',
|
|
54
|
-
'onfocus', 'onblur', 'onchange', 'onsubmit', 'onreset'
|
|
54
|
+
'onfocus', 'onblur', 'onchange', 'onsubmit', 'onreset'
|
|
55
55
|
];
|
|
56
56
|
|
|
57
57
|
// Email-specific sanitization config
|
|
@@ -62,18 +62,18 @@ const EMAIL_CONFIG = {
|
|
|
62
62
|
'a', 'img', 'table', 'tr', 'td', 'th', 'thead', 'tbody', 'tfoot',
|
|
63
63
|
'ul', 'ol', 'li', 'strong', 'b', 'em', 'i', 'u', 'center',
|
|
64
64
|
'font', 'small', 'big', 'sup', 'sub', 'pre', 'code',
|
|
65
|
-
'blockquote', 'cite', 'abbr', 'acronym', 'address'
|
|
65
|
+
'blockquote', 'cite', 'abbr', 'acronym', 'address'
|
|
66
66
|
],
|
|
67
67
|
ALLOWED_ATTR: [
|
|
68
68
|
'src', 'alt', 'title', 'href', 'target', 'rel',
|
|
69
69
|
'width', 'height', 'style', 'class', 'id',
|
|
70
70
|
'align', 'valign', 'bgcolor', 'color', 'border',
|
|
71
71
|
'cellpadding', 'cellspacing', 'colspan', 'rowspan',
|
|
72
|
-
'type', 'charset', 'content', 'name', 'http-equiv'
|
|
72
|
+
'type', 'charset', 'content', 'name', 'http-equiv'
|
|
73
73
|
],
|
|
74
74
|
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'applet', 'form', 'input'],
|
|
75
75
|
FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],
|
|
76
|
-
ALLOW_DATA_ATTR: false
|
|
76
|
+
ALLOW_DATA_ATTR: false
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
// InApp-specific sanitization config
|
|
@@ -83,29 +83,29 @@ const INAPP_CONFIG = {
|
|
|
83
83
|
'div', 'span', 'p', 'br', 'hr', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
84
84
|
'a', 'img', 'button', 'ul', 'ol', 'li', 'strong', 'b', 'em', 'i', 'u',
|
|
85
85
|
'small', 'big', 'sup', 'sub', 'pre', 'code', 'blockquote', 'cite',
|
|
86
|
-
'video', 'audio', 'source', 'canvas'
|
|
86
|
+
'video', 'audio', 'source', 'canvas' // Mobile-friendly multimedia
|
|
87
87
|
],
|
|
88
88
|
ALLOWED_ATTR: [
|
|
89
89
|
'src', 'alt', 'title', 'href', 'target', 'rel',
|
|
90
90
|
'width', 'height', 'style', 'class', 'id',
|
|
91
91
|
'type', 'controls', 'autoplay', 'loop', 'muted',
|
|
92
|
-
'poster', 'preload'
|
|
92
|
+
'poster', 'preload'
|
|
93
93
|
],
|
|
94
94
|
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'applet', 'form', 'input'],
|
|
95
95
|
FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],
|
|
96
|
-
ALLOW_DATA_ATTR: true
|
|
96
|
+
ALLOW_DATA_ATTR: true
|
|
97
97
|
};
|
|
98
98
|
|
|
99
99
|
// Strict sanitization config for production
|
|
100
100
|
const STRICT_CONFIG = {
|
|
101
101
|
ALLOWED_TAGS: [
|
|
102
102
|
'div', 'span', 'p', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
103
|
-
'a', 'img', 'strong', 'b', 'em', 'i', 'u', 'ul', 'ol', 'li'
|
|
103
|
+
'a', 'img', 'strong', 'b', 'em', 'i', 'u', 'ul', 'ol', 'li'
|
|
104
104
|
],
|
|
105
105
|
ALLOWED_ATTR: ['src', 'alt', 'title', 'href', 'style', 'class'],
|
|
106
106
|
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'applet', 'form', 'input', 'style'],
|
|
107
107
|
FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],
|
|
108
|
-
ALLOW_DATA_ATTR: false
|
|
108
|
+
ALLOW_DATA_ATTR: false
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
/**
|
|
@@ -126,9 +126,8 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
126
126
|
warnings: html === null || html === undefined ? [] : [{
|
|
127
127
|
type: 'warning',
|
|
128
128
|
message: formatMessage('sanitizer.invalidInput'),
|
|
129
|
-
source: 'sanitizer'
|
|
130
|
-
|
|
131
|
-
}],
|
|
129
|
+
source: 'sanitizer'
|
|
130
|
+
}]
|
|
132
131
|
};
|
|
133
132
|
}
|
|
134
133
|
|
|
@@ -138,7 +137,7 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
138
137
|
sanitized: '',
|
|
139
138
|
isClean: true,
|
|
140
139
|
removedElements: [],
|
|
141
|
-
warnings: []
|
|
140
|
+
warnings: []
|
|
142
141
|
};
|
|
143
142
|
}
|
|
144
143
|
|
|
@@ -160,7 +159,7 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
160
159
|
sanitized: '',
|
|
161
160
|
isClean: true,
|
|
162
161
|
removedElements: [],
|
|
163
|
-
warnings: []
|
|
162
|
+
warnings: []
|
|
164
163
|
};
|
|
165
164
|
|
|
166
165
|
try {
|
|
@@ -189,8 +188,8 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
189
188
|
CUSTOM_ELEMENT_HANDLING: {
|
|
190
189
|
tagNameCheck: null,
|
|
191
190
|
attributeNameCheck: null,
|
|
192
|
-
allowCustomizedBuiltInElements: false
|
|
193
|
-
}
|
|
191
|
+
allowCustomizedBuiltInElements: false
|
|
192
|
+
}
|
|
194
193
|
};
|
|
195
194
|
|
|
196
195
|
// Sanitize the content directly
|
|
@@ -203,12 +202,12 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
203
202
|
|
|
204
203
|
// Add variant-specific warnings (check original HTML before sanitization)
|
|
205
204
|
addVariantWarnings(html, variant, result, formatMessage);
|
|
205
|
+
|
|
206
206
|
} catch (error) {
|
|
207
207
|
result.warnings.push({
|
|
208
208
|
type: 'error',
|
|
209
209
|
message: formatMessage('sanitizer.sanitizationFailed', { error: error.message }),
|
|
210
|
-
source: 'sanitizer'
|
|
211
|
-
rule: 'sanitizer.sanitizationFailed', // Rule Group #1 – blocking error for UI gating
|
|
210
|
+
source: 'sanitizer'
|
|
212
211
|
});
|
|
213
212
|
result.sanitized = ''; // Return empty content if sanitization fails
|
|
214
213
|
result.isClean = false;
|
|
@@ -230,30 +229,30 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
230
229
|
if (variant === SANITIZER_VARIANTS.EMAIL) {
|
|
231
230
|
// Check for potentially problematic email elements
|
|
232
231
|
const emailProblematicElements = ['<video', '<audio', '<canvas'];
|
|
233
|
-
if (emailProblematicElements.some(
|
|
232
|
+
if (emailProblematicElements.some(element => html.includes(element))) {
|
|
234
233
|
result.warnings.push({
|
|
235
234
|
type: 'warning',
|
|
236
235
|
message: formatMessage('sanitizer.emailMultimediaNotSupported'),
|
|
237
|
-
source: 'email-compatibility'
|
|
236
|
+
source: 'email-compatibility'
|
|
238
237
|
});
|
|
239
238
|
}
|
|
240
239
|
|
|
241
240
|
const problematicStyles = ['position: fixed', 'position: sticky', 'position:fixed', 'position:sticky'];
|
|
242
|
-
if (problematicStyles.some(
|
|
241
|
+
if (problematicStyles.some(style => html.includes(style))) {
|
|
243
242
|
result.warnings.push({
|
|
244
243
|
type: 'warning',
|
|
245
244
|
message: formatMessage('sanitizer.emailPositioningNotSupported'),
|
|
246
|
-
source: 'email-compatibility'
|
|
245
|
+
source: 'email-compatibility'
|
|
247
246
|
});
|
|
248
247
|
}
|
|
249
248
|
|
|
250
249
|
// Check for CSS Grid/Flexbox which may have limited email support
|
|
251
250
|
const modernCssFeatures = ['display: grid', 'display: flex', 'display:grid', 'display:flex'];
|
|
252
|
-
if (modernCssFeatures.some(
|
|
251
|
+
if (modernCssFeatures.some(feature => html.includes(feature))) {
|
|
253
252
|
result.warnings.push({
|
|
254
253
|
type: 'info',
|
|
255
254
|
message: formatMessage('sanitizer.emailModernCssLimited'),
|
|
256
|
-
source: 'email-compatibility'
|
|
255
|
+
source: 'email-compatibility'
|
|
257
256
|
});
|
|
258
257
|
}
|
|
259
258
|
} else if (variant === SANITIZER_VARIANTS.INAPP) {
|
|
@@ -262,7 +261,7 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
262
261
|
result.warnings.push({
|
|
263
262
|
type: 'info',
|
|
264
263
|
message: formatMessage('sanitizer.mobileTablesNotFriendly'),
|
|
265
|
-
source: 'mobile-optimization'
|
|
264
|
+
source: 'mobile-optimization'
|
|
266
265
|
});
|
|
267
266
|
}
|
|
268
267
|
|
|
@@ -271,7 +270,7 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
271
270
|
result.warnings.push({
|
|
272
271
|
type: 'info',
|
|
273
272
|
message: formatMessage('sanitizer.mobileRelativeFontSizes'),
|
|
274
|
-
source: 'mobile-optimization'
|
|
273
|
+
source: 'mobile-optimization'
|
|
275
274
|
});
|
|
276
275
|
}
|
|
277
276
|
|
|
@@ -280,7 +279,7 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
280
279
|
result.warnings.push({
|
|
281
280
|
type: 'info',
|
|
282
281
|
message: formatMessage('sanitizer.mobileFixedWidthsProblematic'),
|
|
283
|
-
source: 'mobile-optimization'
|
|
282
|
+
source: 'mobile-optimization'
|
|
284
283
|
});
|
|
285
284
|
}
|
|
286
285
|
}
|
|
@@ -303,9 +302,9 @@ export const prepareForProduction = (html, variant = SANITIZER_VARIANTS.EMAIL, f
|
|
|
303
302
|
warnings: [{
|
|
304
303
|
type: 'error',
|
|
305
304
|
message: formatMessage('sanitizer.invalidInputNonEmpty'),
|
|
306
|
-
source: 'production-validator'
|
|
305
|
+
source: 'production-validator'
|
|
307
306
|
}],
|
|
308
|
-
recommendations: [formatMessage('sanitizer.productionValidHtml')]
|
|
307
|
+
recommendations: [formatMessage('sanitizer.productionValidHtml')]
|
|
309
308
|
};
|
|
310
309
|
}
|
|
311
310
|
|
|
@@ -317,7 +316,7 @@ export const prepareForProduction = (html, variant = SANITIZER_VARIANTS.EMAIL, f
|
|
|
317
316
|
isProductionReady: sanitizeResult.isClean,
|
|
318
317
|
securityIssues: sanitizeResult.removedElements,
|
|
319
318
|
warnings: sanitizeResult.warnings,
|
|
320
|
-
recommendations: []
|
|
319
|
+
recommendations: []
|
|
321
320
|
};
|
|
322
321
|
|
|
323
322
|
// Add production readiness recommendations
|
|
@@ -356,7 +355,9 @@ export const isContentSafe = (html) => {
|
|
|
356
355
|
if (!html || typeof html !== 'string') return true;
|
|
357
356
|
|
|
358
357
|
// Create dynamic patterns from constants
|
|
359
|
-
const protocolPatterns = DANGEROUS_PROTOCOLS.map(
|
|
358
|
+
const protocolPatterns = DANGEROUS_PROTOCOLS.map(protocol =>
|
|
359
|
+
new RegExp(protocol.replace(':', '\\:'), 'gi')
|
|
360
|
+
);
|
|
360
361
|
|
|
361
362
|
const eventHandlerPattern = new RegExp(EVENT_HANDLERS.join('|'), 'gi');
|
|
362
363
|
|
|
@@ -368,10 +369,10 @@ export const isContentSafe = (html) => {
|
|
|
368
369
|
/<object/gi,
|
|
369
370
|
/<embed/gi,
|
|
370
371
|
/<applet/gi,
|
|
371
|
-
/<form/gi
|
|
372
|
+
/<form/gi
|
|
372
373
|
];
|
|
373
374
|
|
|
374
|
-
return !dangerousPatterns.some(
|
|
375
|
+
return !dangerousPatterns.some(pattern => pattern.test(html));
|
|
375
376
|
};
|
|
376
377
|
|
|
377
378
|
/**
|
|
@@ -394,7 +395,7 @@ export const findUnsafeContent = (html) => {
|
|
|
394
395
|
'Iframe': /<iframe[^>]*>/gi,
|
|
395
396
|
'Object/Embed': /<(object|embed)[^>]*>/gi,
|
|
396
397
|
'Applet': /<applet[^>]*>/gi,
|
|
397
|
-
'Form': /<form[^>]*>/gi
|
|
398
|
+
'Form': /<form[^>]*>/gi
|
|
398
399
|
};
|
|
399
400
|
|
|
400
401
|
Object.entries(patterns).forEach(([name, pattern]) => {
|
|
@@ -405,7 +406,7 @@ export const findUnsafeContent = (html) => {
|
|
|
405
406
|
type: name,
|
|
406
407
|
content: match[0],
|
|
407
408
|
position: match.index,
|
|
408
|
-
length: match[0].length
|
|
409
|
+
length: match[0].length
|
|
409
410
|
});
|
|
410
411
|
|
|
411
412
|
// Prevent infinite loop for global patterns
|
|
@@ -428,5 +429,5 @@ export default {
|
|
|
428
429
|
// Include constants in default export for convenience
|
|
429
430
|
VARIANTS: SANITIZER_VARIANTS,
|
|
430
431
|
SECURITY_LEVELS,
|
|
431
|
-
CONTENT_LIMITS
|
|
432
|
+
CONTENT_LIMITS
|
|
432
433
|
};
|
|
@@ -20,7 +20,7 @@ const defaultMessageFormatter = (messageKey, values = {}) => {
|
|
|
20
20
|
'validator.largeImageDetected': 'Large image dimensions detected - consider mobile optimization',
|
|
21
21
|
'validator.unclosedCssRule': 'Unclosed CSS rule detected',
|
|
22
22
|
'validator.emptyCssRule': 'Empty CSS rule detected',
|
|
23
|
-
'validator.cssValidationFailed': `CSS validation failed: ${values.error || 'Unknown error'}
|
|
23
|
+
'validator.cssValidationFailed': `CSS validation failed: ${values.error || 'Unknown error'}`
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
return fallbackMessages[messageKey] || messageKey;
|
|
@@ -49,7 +49,7 @@ const HTML_RULES = {
|
|
|
49
49
|
'space-tab-mixed-disabled': 'space',
|
|
50
50
|
'id-class-ad-disabled': false,
|
|
51
51
|
'href-abs-or-rel': false,
|
|
52
|
-
'attr-unsafe-chars': true
|
|
52
|
+
'attr-unsafe-chars': true
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
// Additional custom validation rules
|
|
@@ -66,7 +66,7 @@ const CUSTOM_VALIDATIONS = {
|
|
|
66
66
|
|
|
67
67
|
// InApp-specific validations
|
|
68
68
|
MOBILE_INCOMPATIBLE: /<(object|embed|applet)\b/gi,
|
|
69
|
-
LARGE_IMAGES: /width\s*:\s*[5-9]\d{2,}px|height\s*:\s*[5-9]\d{2,}px/gi
|
|
69
|
+
LARGE_IMAGES: /width\s*:\s*[5-9]\d{2,}px|height\s*:\s*[5-9]\d{2,}px/gi
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -82,7 +82,7 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
|
|
|
82
82
|
isValid: true,
|
|
83
83
|
errors: [],
|
|
84
84
|
warnings: [],
|
|
85
|
-
info: []
|
|
85
|
+
info: []
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -90,7 +90,7 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
|
|
|
90
90
|
isValid: true,
|
|
91
91
|
errors: [],
|
|
92
92
|
warnings: [],
|
|
93
|
-
info: []
|
|
93
|
+
info: []
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
try {
|
|
@@ -98,7 +98,7 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
|
|
|
98
98
|
const htmlHintResults = HTMLHint.verify(html, HTML_RULES);
|
|
99
99
|
|
|
100
100
|
// Process HTMLHint results
|
|
101
|
-
htmlHintResults.forEach(
|
|
101
|
+
htmlHintResults.forEach(issue => {
|
|
102
102
|
const error = {
|
|
103
103
|
type: issue.type,
|
|
104
104
|
message: issue.message,
|
|
@@ -106,64 +106,66 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
|
|
|
106
106
|
column: issue.col,
|
|
107
107
|
rule: issue.rule.id,
|
|
108
108
|
severity: getSeverityLevel(issue.type, issue.rule.id),
|
|
109
|
-
source: 'htmlhint'
|
|
109
|
+
source: 'htmlhint'
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
-
if (error.severity === '
|
|
112
|
+
if (error.severity === 'error') {
|
|
113
|
+
results.errors.push(error);
|
|
114
|
+
results.isValid = false;
|
|
115
|
+
} else if (error.severity === 'warning') {
|
|
113
116
|
results.warnings.push(error);
|
|
114
|
-
} else if (error.severity === 'info') {
|
|
115
|
-
results.info.push(error);
|
|
116
117
|
} else {
|
|
117
|
-
results.
|
|
118
|
+
results.info.push(error);
|
|
118
119
|
}
|
|
119
120
|
});
|
|
121
|
+
|
|
122
|
+
// Run custom validations
|
|
123
|
+
runCustomValidations(html, variant, results, formatMessage);
|
|
124
|
+
|
|
125
|
+
// Run Liquid template validation
|
|
126
|
+
runLiquidValidation(html, variant, results, formatMessage);
|
|
127
|
+
|
|
120
128
|
} catch (error) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
type: 'warning',
|
|
129
|
+
results.errors.push({
|
|
130
|
+
type: 'error',
|
|
124
131
|
message: formatMessage('validator.validationFailed', { error: error.message }),
|
|
125
132
|
line: 1,
|
|
126
133
|
column: 1,
|
|
127
134
|
rule: 'validation-error',
|
|
128
|
-
severity: '
|
|
129
|
-
source: 'validator'
|
|
135
|
+
severity: 'error',
|
|
136
|
+
source: 'validator'
|
|
130
137
|
});
|
|
138
|
+
results.isValid = false;
|
|
131
139
|
}
|
|
132
140
|
|
|
133
|
-
// Always run custom validations and Liquid validation, even if HTMLHint failed
|
|
134
|
-
// This ensures unsafe protocol detection and other critical validations still run
|
|
135
|
-
runCustomValidations(html, variant, results, formatMessage);
|
|
136
|
-
runLiquidValidation(html, variant, results, formatMessage);
|
|
137
|
-
|
|
138
141
|
return results;
|
|
139
142
|
};
|
|
140
143
|
|
|
141
144
|
/**
|
|
142
|
-
* Determines severity level based on error type and rule
|
|
143
|
-
* ONLY Rule Group #1 (Input & Sanitization) is blocking; that is handled in
|
|
144
|
-
* contentSanitizer/useValidation. All HTML/CSS/Liquid/security rules here are
|
|
145
|
-
* WARNING only for backward compatibility with CKEditor legacy templates.
|
|
145
|
+
* Determines severity level based on error type and rule
|
|
146
146
|
*/
|
|
147
147
|
const getSeverityLevel = (type, ruleId) => {
|
|
148
|
-
const
|
|
148
|
+
const errorRules = [
|
|
149
149
|
'tag-pair',
|
|
150
150
|
'attr-no-duplication',
|
|
151
151
|
'id-unique',
|
|
152
|
-
'spec-char-escape'
|
|
152
|
+
'spec-char-escape'
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
const warningRules = [
|
|
153
156
|
'tagname-lowercase',
|
|
154
157
|
'attr-lowercase',
|
|
155
158
|
'attr-value-double-quotes',
|
|
156
|
-
'alt-require'
|
|
159
|
+
'alt-require'
|
|
157
160
|
];
|
|
158
161
|
|
|
159
|
-
if (
|
|
160
|
-
return '
|
|
161
|
-
}
|
|
162
|
-
// Downgrade HTMLHint "error" type to warning (Rule Group #1 is sanitizer-only)
|
|
163
|
-
if (type === 'error') {
|
|
162
|
+
if (type === 'error' || errorRules.includes(ruleId)) {
|
|
163
|
+
return 'error';
|
|
164
|
+
} else if (warningRules.includes(ruleId)) {
|
|
164
165
|
return 'warning';
|
|
166
|
+
} else {
|
|
167
|
+
return 'info';
|
|
165
168
|
}
|
|
166
|
-
return 'info';
|
|
167
169
|
};
|
|
168
170
|
|
|
169
171
|
/**
|
|
@@ -175,7 +177,6 @@ const getSeverityLevel = (type, ruleId) => {
|
|
|
175
177
|
*/
|
|
176
178
|
const runCustomValidations = (html, variant, results, formatMessage = defaultMessageFormatter) => {
|
|
177
179
|
// Check for unsafe protocols using RegExp.exec loop
|
|
178
|
-
// These are BLOCKING ERRORS (Rule Group #1: sanitizer.dangerousProtocolDetected)
|
|
179
180
|
const unsafeProtocolsRegex = new RegExp(CUSTOM_VALIDATIONS.UNSAFE_PROTOCOLS.source, CUSTOM_VALIDATIONS.UNSAFE_PROTOCOLS.flags);
|
|
180
181
|
unsafeProtocolsRegex.lastIndex = 0; // Reset lastIndex before running
|
|
181
182
|
let match;
|
|
@@ -185,9 +186,9 @@ const runCustomValidations = (html, variant, results, formatMessage = defaultMes
|
|
|
185
186
|
message: formatMessage('validator.unsafeProtocolDetected', { protocol: match[0] }),
|
|
186
187
|
line: getLineNumber(html, match.index),
|
|
187
188
|
column: 1,
|
|
188
|
-
rule: '
|
|
189
|
+
rule: 'unsafe-protocol',
|
|
189
190
|
severity: 'error',
|
|
190
|
-
source: 'custom'
|
|
191
|
+
source: 'custom'
|
|
191
192
|
});
|
|
192
193
|
results.isValid = false;
|
|
193
194
|
|
|
@@ -208,7 +209,7 @@ const runCustomValidations = (html, variant, results, formatMessage = defaultMes
|
|
|
208
209
|
column: 1,
|
|
209
210
|
rule: 'script-tag-warning',
|
|
210
211
|
severity: 'warning',
|
|
211
|
-
source: 'custom'
|
|
212
|
+
source: 'custom'
|
|
212
213
|
});
|
|
213
214
|
|
|
214
215
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -244,7 +245,7 @@ const validateEmailSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
244
245
|
column: 1,
|
|
245
246
|
rule: 'outlook-compatibility',
|
|
246
247
|
severity: 'warning',
|
|
247
|
-
source: 'email-specific'
|
|
248
|
+
source: 'email-specific'
|
|
248
249
|
});
|
|
249
250
|
|
|
250
251
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -264,7 +265,7 @@ const validateEmailSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
264
265
|
column: 1,
|
|
265
266
|
rule: 'email-css-compatibility',
|
|
266
267
|
severity: 'warning',
|
|
267
|
-
source: 'email-specific'
|
|
268
|
+
source: 'email-specific'
|
|
268
269
|
});
|
|
269
270
|
|
|
270
271
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -293,7 +294,7 @@ const validateInAppSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
293
294
|
column: 1,
|
|
294
295
|
rule: 'mobile-compatibility',
|
|
295
296
|
severity: 'warning',
|
|
296
|
-
source: 'inapp-specific'
|
|
297
|
+
source: 'inapp-specific'
|
|
297
298
|
});
|
|
298
299
|
|
|
299
300
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -313,7 +314,7 @@ const validateInAppSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
313
314
|
column: 1,
|
|
314
315
|
rule: 'mobile-image-size',
|
|
315
316
|
severity: 'info',
|
|
316
|
-
source: 'inapp-specific'
|
|
317
|
+
source: 'inapp-specific'
|
|
317
318
|
});
|
|
318
319
|
|
|
319
320
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -335,9 +336,8 @@ const runLiquidValidation = (html, variant, results, formatMessage = defaultMess
|
|
|
335
336
|
const liquidResults = validateLiquidHTML(html, variant);
|
|
336
337
|
|
|
337
338
|
// Merge Liquid validation results
|
|
338
|
-
// Client-side Liquid validation errors are blocking (genuine syntax errors)
|
|
339
339
|
if (liquidResults.errors) {
|
|
340
|
-
results.errors.push(...liquidResults.errors
|
|
340
|
+
results.errors.push(...liquidResults.errors);
|
|
341
341
|
if (liquidResults.errors.length > 0) {
|
|
342
342
|
results.isValid = false;
|
|
343
343
|
}
|
|
@@ -375,7 +375,7 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
|
|
|
375
375
|
isValid: true,
|
|
376
376
|
errors: [],
|
|
377
377
|
warnings: [],
|
|
378
|
-
info: []
|
|
378
|
+
info: []
|
|
379
379
|
};
|
|
380
380
|
|
|
381
381
|
if (!css || typeof css !== 'string') {
|
|
@@ -388,7 +388,7 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
|
|
|
388
388
|
unclosedBraces: /\{[^{}]*$/gm,
|
|
389
389
|
invalidProperty: /[^;{}]+:\s*[^;{}]*[^;}]/g,
|
|
390
390
|
missingColon: /[^;{}]+\s+[^;{}:]+;/g,
|
|
391
|
-
emptyRule: /[^{}]+\{\s*\}/g
|
|
391
|
+
emptyRule: /[^{}]+\{\s*\}/g
|
|
392
392
|
};
|
|
393
393
|
|
|
394
394
|
// Check for unclosed braces using RegExp.exec loop
|
|
@@ -396,15 +396,16 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
|
|
|
396
396
|
unclosedBracesRegex.lastIndex = 0; // Reset lastIndex before running
|
|
397
397
|
let match;
|
|
398
398
|
while ((match = unclosedBracesRegex.exec(css)) !== null) {
|
|
399
|
-
results.
|
|
400
|
-
type: '
|
|
399
|
+
results.errors.push({
|
|
400
|
+
type: 'error',
|
|
401
401
|
message: formatMessage('validator.unclosedCssRule'),
|
|
402
402
|
line: getLineNumber(css, match.index),
|
|
403
403
|
column: 1,
|
|
404
404
|
rule: 'unclosed-brace',
|
|
405
|
-
severity: '
|
|
406
|
-
source: 'css-validator'
|
|
405
|
+
severity: 'error',
|
|
406
|
+
source: 'css-validator'
|
|
407
407
|
});
|
|
408
|
+
results.isValid = false;
|
|
408
409
|
|
|
409
410
|
// Guard against zero-length matches to avoid infinite loops
|
|
410
411
|
if (match[0].length === 0) {
|
|
@@ -416,31 +417,34 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
|
|
|
416
417
|
const emptyRulesRegex = new RegExp(validationPatterns.emptyRule.source, validationPatterns.emptyRule.flags);
|
|
417
418
|
emptyRulesRegex.lastIndex = 0; // Reset lastIndex before running
|
|
418
419
|
while ((match = emptyRulesRegex.exec(css)) !== null) {
|
|
419
|
-
results.
|
|
420
|
-
type: '
|
|
420
|
+
results.errors.push({
|
|
421
|
+
type: 'error',
|
|
421
422
|
message: formatMessage('validator.emptyCssRule'),
|
|
422
423
|
line: getLineNumber(css, match.index),
|
|
423
424
|
column: 1,
|
|
424
425
|
rule: 'empty-rule',
|
|
425
|
-
severity: '
|
|
426
|
-
source: 'css-validator'
|
|
426
|
+
severity: 'error',
|
|
427
|
+
source: 'css-validator'
|
|
427
428
|
});
|
|
429
|
+
results.isValid = false;
|
|
428
430
|
|
|
429
431
|
// Guard against zero-length matches to avoid infinite loops
|
|
430
432
|
if (match[0].length === 0) {
|
|
431
433
|
emptyRulesRegex.lastIndex++;
|
|
432
434
|
}
|
|
433
435
|
}
|
|
436
|
+
|
|
434
437
|
} catch (error) {
|
|
435
|
-
results.
|
|
436
|
-
type: '
|
|
438
|
+
results.errors.push({
|
|
439
|
+
type: 'error',
|
|
437
440
|
message: formatMessage('validator.cssValidationFailed', { error: error.message }),
|
|
438
441
|
line: 1,
|
|
439
442
|
column: 1,
|
|
440
443
|
rule: 'css-validation-error',
|
|
441
|
-
severity: '
|
|
442
|
-
source: 'css-validator'
|
|
444
|
+
severity: 'error',
|
|
445
|
+
source: 'css-validator'
|
|
443
446
|
});
|
|
447
|
+
results.isValid = false;
|
|
444
448
|
}
|
|
445
449
|
|
|
446
450
|
return results;
|
|
@@ -453,20 +457,16 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
|
|
|
453
457
|
* @returns {Object} Combined validation results from all CSS blocks
|
|
454
458
|
*/
|
|
455
459
|
export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormatter) => {
|
|
456
|
-
if (!html) {
|
|
457
|
-
return {
|
|
458
|
-
isValid: true, errors: [], warnings: [], info: [],
|
|
459
|
-
};
|
|
460
|
-
}
|
|
460
|
+
if (!html) return { isValid: true, errors: [], warnings: [], info: [] };
|
|
461
461
|
|
|
462
462
|
// Extract CSS from style tags
|
|
463
463
|
const styleTagPattern = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
464
464
|
let match;
|
|
465
|
-
|
|
465
|
+
let allResults = {
|
|
466
466
|
isValid: true,
|
|
467
467
|
errors: [],
|
|
468
468
|
warnings: [],
|
|
469
|
-
info: []
|
|
469
|
+
info: []
|
|
470
470
|
};
|
|
471
471
|
|
|
472
472
|
while ((match = styleTagPattern.exec(html)) !== null) {
|
|
@@ -483,18 +483,19 @@ export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormat
|
|
|
483
483
|
}
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
// Check for unclosed style tags
|
|
486
|
+
// Check for unclosed style tags
|
|
487
487
|
const unclosedStylePattern = /<style[^>]*>(?![\s\S]*?<\/style>)/gi;
|
|
488
488
|
if (unclosedStylePattern.test(html)) {
|
|
489
|
-
allResults.
|
|
490
|
-
type: '
|
|
489
|
+
allResults.errors.push({
|
|
490
|
+
type: 'error',
|
|
491
491
|
message: formatMessage('validator.unclosedCssRule'),
|
|
492
492
|
line: 1,
|
|
493
493
|
column: 1,
|
|
494
494
|
rule: 'unclosed-style-tag',
|
|
495
|
-
severity: '
|
|
496
|
-
source: 'css-validator'
|
|
495
|
+
severity: 'error',
|
|
496
|
+
source: 'css-validator'
|
|
497
497
|
});
|
|
498
|
+
allResults.isValid = false;
|
|
498
499
|
}
|
|
499
500
|
|
|
500
501
|
return allResults;
|
|
@@ -503,5 +504,5 @@ export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormat
|
|
|
503
504
|
export default {
|
|
504
505
|
validateHTML,
|
|
505
506
|
validateCSS,
|
|
506
|
-
extractAndValidateCSS
|
|
507
|
+
extractAndValidateCSS
|
|
507
508
|
};
|