@capillarytech/creatives-library 8.0.271 → 8.0.273
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 +2 -1
- package/initialReducer.js +2 -0
- package/package.json +1 -1
- package/services/api.js +10 -0
- package/services/tests/api.test.js +34 -0
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +17 -35
- package/tests/integration/TemplateCreation/api-response.js +31 -1
- package/tests/integration/TemplateCreation/msw-handler.js +2 -0
- package/utils/common.js +5 -0
- package/utils/commonUtils.js +28 -5
- package/utils/imageUrlUpload.js +13 -14
- package/utils/tests/commonUtil.test.js +224 -0
- package/utils/tests/imageUrlUpload.test.js +298 -0
- package/utils/transformTemplateConfig.js +0 -10
- package/v2Components/CapDeviceContent/index.js +61 -56
- package/v2Components/CapTagList/index.js +6 -1
- package/v2Components/CapTagListWithInput/index.js +5 -1
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
- package/v2Components/ErrorInfoNote/constants.js +1 -0
- package/v2Components/ErrorInfoNote/index.js +402 -72
- package/v2Components/ErrorInfoNote/messages.js +32 -6
- package/v2Components/ErrorInfoNote/style.scss +278 -6
- package/v2Components/FormBuilder/tests/index.test.js +13 -4
- package/v2Components/HtmlEditor/HTMLEditor.js +418 -99
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +870 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1882 -133
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
- package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
- package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -1
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +31 -6
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +7 -10
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +18 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +36 -31
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +46 -34
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +52 -46
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +277 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +295 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
- package/v2Components/HtmlEditor/constants.js +45 -20
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +351 -16
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
- package/v2Components/HtmlEditor/hooks/useValidation.js +213 -56
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +102 -94
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +214 -45
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
- package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +158 -124
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
- package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
- package/v2Components/HtmlEditor/utils/validationConstants.js +38 -0
- package/v2Components/MobilePushPreviewV2/constants.js +6 -0
- package/v2Components/MobilePushPreviewV2/index.js +33 -7
- package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
- package/v2Components/TemplatePreview/index.js +47 -32
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
- package/v2Containers/BeeEditor/index.js +172 -90
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
- package/v2Containers/BeePopupEditor/constants.js +10 -0
- package/v2Containers/BeePopupEditor/index.js +194 -0
- package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +156 -13
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
- package/v2Containers/CreativesContainer/constants.js +1 -0
- package/v2Containers/CreativesContainer/index.js +251 -47
- package/v2Containers/CreativesContainer/messages.js +8 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +103 -0
- package/v2Containers/Email/actions.js +7 -0
- package/v2Containers/Email/constants.js +5 -1
- package/v2Containers/Email/index.js +234 -29
- package/v2Containers/Email/messages.js +32 -0
- package/v2Containers/Email/reducer.js +12 -1
- package/v2Containers/Email/sagas.js +61 -7
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
- package/v2Containers/Email/tests/reducer.test.js +46 -0
- package/v2Containers/Email/tests/sagas.test.js +320 -29
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1246 -0
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +212 -21
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2614 -0
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
- package/v2Containers/EmailWrapper/constants.js +2 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +627 -79
- package/v2Containers/EmailWrapper/index.js +103 -23
- package/v2Containers/EmailWrapper/messages.js +65 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +955 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +596 -82
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
- package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
- package/v2Containers/InApp/actions.js +7 -0
- package/v2Containers/InApp/constants.js +20 -4
- package/v2Containers/InApp/index.js +802 -360
- package/v2Containers/InApp/index.scss +4 -3
- package/v2Containers/InApp/messages.js +7 -3
- package/v2Containers/InApp/reducer.js +21 -3
- package/v2Containers/InApp/sagas.js +29 -9
- package/v2Containers/InApp/selectors.js +25 -5
- package/v2Containers/InApp/tests/index.test.js +154 -50
- package/v2Containers/InApp/tests/reducer.test.js +34 -0
- package/v2Containers/InApp/tests/sagas.test.js +61 -9
- package/v2Containers/InApp/tests/selectors.test.js +612 -0
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
- package/v2Containers/InAppWrapper/constants.js +16 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
- package/v2Containers/InAppWrapper/index.js +148 -0
- package/v2Containers/InAppWrapper/messages.js +49 -0
- package/v2Containers/InappAdvance/index.js +1099 -0
- package/v2Containers/InappAdvance/index.scss +10 -0
- package/v2Containers/InappAdvance/tests/index.test.js +448 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
- package/v2Containers/MobilePush/Create/index.js +1 -1
- package/v2Containers/MobilePush/Edit/index.js +10 -6
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
- package/v2Containers/TagList/index.js +62 -19
- package/v2Containers/Templates/_templates.scss +60 -1
- package/v2Containers/Templates/index.js +89 -4
- package/v2Containers/Templates/messages.js +4 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
|
@@ -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,8 +126,9 @@ 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
|
-
|
|
129
|
+
source: 'sanitizer',
|
|
130
|
+
rule: 'sanitizer.invalidInput', // Rule Group #1 – blocking error for UI gating
|
|
131
|
+
}],
|
|
131
132
|
};
|
|
132
133
|
}
|
|
133
134
|
|
|
@@ -137,7 +138,7 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
137
138
|
sanitized: '',
|
|
138
139
|
isClean: true,
|
|
139
140
|
removedElements: [],
|
|
140
|
-
warnings: []
|
|
141
|
+
warnings: [],
|
|
141
142
|
};
|
|
142
143
|
}
|
|
143
144
|
|
|
@@ -159,7 +160,7 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
159
160
|
sanitized: '',
|
|
160
161
|
isClean: true,
|
|
161
162
|
removedElements: [],
|
|
162
|
-
warnings: []
|
|
163
|
+
warnings: [],
|
|
163
164
|
};
|
|
164
165
|
|
|
165
166
|
try {
|
|
@@ -188,8 +189,8 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
188
189
|
CUSTOM_ELEMENT_HANDLING: {
|
|
189
190
|
tagNameCheck: null,
|
|
190
191
|
attributeNameCheck: null,
|
|
191
|
-
allowCustomizedBuiltInElements: false
|
|
192
|
-
}
|
|
192
|
+
allowCustomizedBuiltInElements: false,
|
|
193
|
+
},
|
|
193
194
|
};
|
|
194
195
|
|
|
195
196
|
// Sanitize the content directly
|
|
@@ -202,12 +203,12 @@ export const sanitizeHTML = (html, variant = SANITIZER_VARIANTS.EMAIL, level = S
|
|
|
202
203
|
|
|
203
204
|
// Add variant-specific warnings (check original HTML before sanitization)
|
|
204
205
|
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'
|
|
210
|
+
source: 'sanitizer',
|
|
211
|
+
rule: 'sanitizer.sanitizationFailed', // Rule Group #1 – blocking error for UI gating
|
|
211
212
|
});
|
|
212
213
|
result.sanitized = ''; // Return empty content if sanitization fails
|
|
213
214
|
result.isClean = false;
|
|
@@ -229,30 +230,30 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
229
230
|
if (variant === SANITIZER_VARIANTS.EMAIL) {
|
|
230
231
|
// Check for potentially problematic email elements
|
|
231
232
|
const emailProblematicElements = ['<video', '<audio', '<canvas'];
|
|
232
|
-
if (emailProblematicElements.some(element => html.includes(element))) {
|
|
233
|
+
if (emailProblematicElements.some((element) => html.includes(element))) {
|
|
233
234
|
result.warnings.push({
|
|
234
235
|
type: 'warning',
|
|
235
236
|
message: formatMessage('sanitizer.emailMultimediaNotSupported'),
|
|
236
|
-
source: 'email-compatibility'
|
|
237
|
+
source: 'email-compatibility',
|
|
237
238
|
});
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
const problematicStyles = ['position: fixed', 'position: sticky', 'position:fixed', 'position:sticky'];
|
|
241
|
-
if (problematicStyles.some(style => html.includes(style))) {
|
|
242
|
+
if (problematicStyles.some((style) => html.includes(style))) {
|
|
242
243
|
result.warnings.push({
|
|
243
244
|
type: 'warning',
|
|
244
245
|
message: formatMessage('sanitizer.emailPositioningNotSupported'),
|
|
245
|
-
source: 'email-compatibility'
|
|
246
|
+
source: 'email-compatibility',
|
|
246
247
|
});
|
|
247
248
|
}
|
|
248
249
|
|
|
249
250
|
// Check for CSS Grid/Flexbox which may have limited email support
|
|
250
251
|
const modernCssFeatures = ['display: grid', 'display: flex', 'display:grid', 'display:flex'];
|
|
251
|
-
if (modernCssFeatures.some(feature => html.includes(feature))) {
|
|
252
|
+
if (modernCssFeatures.some((feature) => html.includes(feature))) {
|
|
252
253
|
result.warnings.push({
|
|
253
254
|
type: 'info',
|
|
254
255
|
message: formatMessage('sanitizer.emailModernCssLimited'),
|
|
255
|
-
source: 'email-compatibility'
|
|
256
|
+
source: 'email-compatibility',
|
|
256
257
|
});
|
|
257
258
|
}
|
|
258
259
|
} else if (variant === SANITIZER_VARIANTS.INAPP) {
|
|
@@ -261,7 +262,7 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
261
262
|
result.warnings.push({
|
|
262
263
|
type: 'info',
|
|
263
264
|
message: formatMessage('sanitizer.mobileTablesNotFriendly'),
|
|
264
|
-
source: 'mobile-optimization'
|
|
265
|
+
source: 'mobile-optimization',
|
|
265
266
|
});
|
|
266
267
|
}
|
|
267
268
|
|
|
@@ -270,7 +271,7 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
270
271
|
result.warnings.push({
|
|
271
272
|
type: 'info',
|
|
272
273
|
message: formatMessage('sanitizer.mobileRelativeFontSizes'),
|
|
273
|
-
source: 'mobile-optimization'
|
|
274
|
+
source: 'mobile-optimization',
|
|
274
275
|
});
|
|
275
276
|
}
|
|
276
277
|
|
|
@@ -279,7 +280,7 @@ const addVariantWarnings = (html, variant, result, formatMessage = defaultMessag
|
|
|
279
280
|
result.warnings.push({
|
|
280
281
|
type: 'info',
|
|
281
282
|
message: formatMessage('sanitizer.mobileFixedWidthsProblematic'),
|
|
282
|
-
source: 'mobile-optimization'
|
|
283
|
+
source: 'mobile-optimization',
|
|
283
284
|
});
|
|
284
285
|
}
|
|
285
286
|
}
|
|
@@ -302,9 +303,9 @@ export const prepareForProduction = (html, variant = SANITIZER_VARIANTS.EMAIL, f
|
|
|
302
303
|
warnings: [{
|
|
303
304
|
type: 'error',
|
|
304
305
|
message: formatMessage('sanitizer.invalidInputNonEmpty'),
|
|
305
|
-
source: 'production-validator'
|
|
306
|
+
source: 'production-validator',
|
|
306
307
|
}],
|
|
307
|
-
recommendations: [formatMessage('sanitizer.productionValidHtml')]
|
|
308
|
+
recommendations: [formatMessage('sanitizer.productionValidHtml')],
|
|
308
309
|
};
|
|
309
310
|
}
|
|
310
311
|
|
|
@@ -316,7 +317,7 @@ export const prepareForProduction = (html, variant = SANITIZER_VARIANTS.EMAIL, f
|
|
|
316
317
|
isProductionReady: sanitizeResult.isClean,
|
|
317
318
|
securityIssues: sanitizeResult.removedElements,
|
|
318
319
|
warnings: sanitizeResult.warnings,
|
|
319
|
-
recommendations: []
|
|
320
|
+
recommendations: [],
|
|
320
321
|
};
|
|
321
322
|
|
|
322
323
|
// Add production readiness recommendations
|
|
@@ -355,9 +356,7 @@ export const isContentSafe = (html) => {
|
|
|
355
356
|
if (!html || typeof html !== 'string') return true;
|
|
356
357
|
|
|
357
358
|
// Create dynamic patterns from constants
|
|
358
|
-
const protocolPatterns = DANGEROUS_PROTOCOLS.map(protocol =>
|
|
359
|
-
new RegExp(protocol.replace(':', '\\:'), 'gi')
|
|
360
|
-
);
|
|
359
|
+
const protocolPatterns = DANGEROUS_PROTOCOLS.map((protocol) => new RegExp(protocol.replace(':', '\\:'), 'gi'));
|
|
361
360
|
|
|
362
361
|
const eventHandlerPattern = new RegExp(EVENT_HANDLERS.join('|'), 'gi');
|
|
363
362
|
|
|
@@ -369,10 +368,10 @@ export const isContentSafe = (html) => {
|
|
|
369
368
|
/<object/gi,
|
|
370
369
|
/<embed/gi,
|
|
371
370
|
/<applet/gi,
|
|
372
|
-
/<form/gi
|
|
371
|
+
/<form/gi,
|
|
373
372
|
];
|
|
374
373
|
|
|
375
|
-
return !dangerousPatterns.some(pattern => pattern.test(html));
|
|
374
|
+
return !dangerousPatterns.some((pattern) => pattern.test(html));
|
|
376
375
|
};
|
|
377
376
|
|
|
378
377
|
/**
|
|
@@ -395,7 +394,7 @@ export const findUnsafeContent = (html) => {
|
|
|
395
394
|
'Iframe': /<iframe[^>]*>/gi,
|
|
396
395
|
'Object/Embed': /<(object|embed)[^>]*>/gi,
|
|
397
396
|
'Applet': /<applet[^>]*>/gi,
|
|
398
|
-
'Form': /<form[^>]*>/gi
|
|
397
|
+
'Form': /<form[^>]*>/gi,
|
|
399
398
|
};
|
|
400
399
|
|
|
401
400
|
Object.entries(patterns).forEach(([name, pattern]) => {
|
|
@@ -406,7 +405,7 @@ export const findUnsafeContent = (html) => {
|
|
|
406
405
|
type: name,
|
|
407
406
|
content: match[0],
|
|
408
407
|
position: match.index,
|
|
409
|
-
length: match[0].length
|
|
408
|
+
length: match[0].length,
|
|
410
409
|
});
|
|
411
410
|
|
|
412
411
|
// Prevent infinite loop for global patterns
|
|
@@ -429,5 +428,5 @@ export default {
|
|
|
429
428
|
// Include constants in default export for convenience
|
|
430
429
|
VARIANTS: SANITIZER_VARIANTS,
|
|
431
430
|
SECURITY_LEVELS,
|
|
432
|
-
CONTENT_LIMITS
|
|
431
|
+
CONTENT_LIMITS,
|
|
433
432
|
};
|
|
@@ -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(issue => {
|
|
101
|
+
htmlHintResults.forEach((issue) => {
|
|
102
102
|
const error = {
|
|
103
103
|
type: issue.type,
|
|
104
104
|
message: issue.message,
|
|
@@ -106,66 +106,64 @@ 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 === '
|
|
113
|
-
results.errors.push(error);
|
|
114
|
-
results.isValid = false;
|
|
115
|
-
} else if (error.severity === 'warning') {
|
|
112
|
+
if (error.severity === 'warning') {
|
|
116
113
|
results.warnings.push(error);
|
|
117
|
-
} else {
|
|
114
|
+
} else if (error.severity === 'info') {
|
|
118
115
|
results.info.push(error);
|
|
116
|
+
} else {
|
|
117
|
+
results.warnings.push(error);
|
|
119
118
|
}
|
|
120
119
|
});
|
|
121
|
-
|
|
122
|
-
// Run custom validations
|
|
123
|
-
runCustomValidations(html, variant, results, formatMessage);
|
|
124
|
-
|
|
125
|
-
// Run Liquid template validation
|
|
126
|
-
runLiquidValidation(html, variant, results, formatMessage);
|
|
127
|
-
|
|
128
120
|
} catch (error) {
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
// HTMLHint failed, but we still want to run custom validations and Liquid validation
|
|
122
|
+
results.warnings.push({
|
|
123
|
+
type: 'warning',
|
|
131
124
|
message: formatMessage('validator.validationFailed', { error: error.message }),
|
|
132
125
|
line: 1,
|
|
133
126
|
column: 1,
|
|
134
127
|
rule: 'validation-error',
|
|
135
|
-
severity: '
|
|
136
|
-
source: 'validator'
|
|
128
|
+
severity: 'warning',
|
|
129
|
+
source: 'validator',
|
|
137
130
|
});
|
|
138
|
-
results.isValid = false;
|
|
139
131
|
}
|
|
140
132
|
|
|
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
|
+
|
|
141
138
|
return results;
|
|
142
139
|
};
|
|
143
140
|
|
|
144
141
|
/**
|
|
145
|
-
* Determines severity level based on error type and rule
|
|
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.
|
|
146
146
|
*/
|
|
147
147
|
const getSeverityLevel = (type, ruleId) => {
|
|
148
|
-
const
|
|
148
|
+
const warningRules = [
|
|
149
149
|
'tag-pair',
|
|
150
150
|
'attr-no-duplication',
|
|
151
151
|
'id-unique',
|
|
152
|
-
'spec-char-escape'
|
|
153
|
-
];
|
|
154
|
-
|
|
155
|
-
const warningRules = [
|
|
152
|
+
'spec-char-escape',
|
|
156
153
|
'tagname-lowercase',
|
|
157
154
|
'attr-lowercase',
|
|
158
155
|
'attr-value-double-quotes',
|
|
159
|
-
'alt-require'
|
|
156
|
+
'alt-require',
|
|
160
157
|
];
|
|
161
158
|
|
|
162
|
-
if (
|
|
163
|
-
return '
|
|
164
|
-
}
|
|
159
|
+
if (warningRules.includes(ruleId)) {
|
|
160
|
+
return 'warning';
|
|
161
|
+
}
|
|
162
|
+
// Downgrade HTMLHint "error" type to warning (Rule Group #1 is sanitizer-only)
|
|
163
|
+
if (type === 'error') {
|
|
165
164
|
return 'warning';
|
|
166
|
-
} else {
|
|
167
|
-
return 'info';
|
|
168
165
|
}
|
|
166
|
+
return 'info';
|
|
169
167
|
};
|
|
170
168
|
|
|
171
169
|
/**
|
|
@@ -177,6 +175,7 @@ const getSeverityLevel = (type, ruleId) => {
|
|
|
177
175
|
*/
|
|
178
176
|
const runCustomValidations = (html, variant, results, formatMessage = defaultMessageFormatter) => {
|
|
179
177
|
// Check for unsafe protocols using RegExp.exec loop
|
|
178
|
+
// These are BLOCKING ERRORS (Rule Group #1: sanitizer.dangerousProtocolDetected)
|
|
180
179
|
const unsafeProtocolsRegex = new RegExp(CUSTOM_VALIDATIONS.UNSAFE_PROTOCOLS.source, CUSTOM_VALIDATIONS.UNSAFE_PROTOCOLS.flags);
|
|
181
180
|
unsafeProtocolsRegex.lastIndex = 0; // Reset lastIndex before running
|
|
182
181
|
let match;
|
|
@@ -186,9 +185,9 @@ const runCustomValidations = (html, variant, results, formatMessage = defaultMes
|
|
|
186
185
|
message: formatMessage('validator.unsafeProtocolDetected', { protocol: match[0] }),
|
|
187
186
|
line: getLineNumber(html, match.index),
|
|
188
187
|
column: 1,
|
|
189
|
-
rule: '
|
|
188
|
+
rule: 'sanitizer.dangerousProtocolDetected',
|
|
190
189
|
severity: 'error',
|
|
191
|
-
source: 'custom'
|
|
190
|
+
source: 'custom',
|
|
192
191
|
});
|
|
193
192
|
results.isValid = false;
|
|
194
193
|
|
|
@@ -209,7 +208,7 @@ const runCustomValidations = (html, variant, results, formatMessage = defaultMes
|
|
|
209
208
|
column: 1,
|
|
210
209
|
rule: 'script-tag-warning',
|
|
211
210
|
severity: 'warning',
|
|
212
|
-
source: 'custom'
|
|
211
|
+
source: 'custom',
|
|
213
212
|
});
|
|
214
213
|
|
|
215
214
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -245,7 +244,7 @@ const validateEmailSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
245
244
|
column: 1,
|
|
246
245
|
rule: 'outlook-compatibility',
|
|
247
246
|
severity: 'warning',
|
|
248
|
-
source: 'email-specific'
|
|
247
|
+
source: 'email-specific',
|
|
249
248
|
});
|
|
250
249
|
|
|
251
250
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -265,7 +264,7 @@ const validateEmailSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
265
264
|
column: 1,
|
|
266
265
|
rule: 'email-css-compatibility',
|
|
267
266
|
severity: 'warning',
|
|
268
|
-
source: 'email-specific'
|
|
267
|
+
source: 'email-specific',
|
|
269
268
|
});
|
|
270
269
|
|
|
271
270
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -294,7 +293,7 @@ const validateInAppSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
294
293
|
column: 1,
|
|
295
294
|
rule: 'mobile-compatibility',
|
|
296
295
|
severity: 'warning',
|
|
297
|
-
source: 'inapp-specific'
|
|
296
|
+
source: 'inapp-specific',
|
|
298
297
|
});
|
|
299
298
|
|
|
300
299
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -314,7 +313,7 @@ const validateInAppSpecific = (html, results, formatMessage = defaultMessageForm
|
|
|
314
313
|
column: 1,
|
|
315
314
|
rule: 'mobile-image-size',
|
|
316
315
|
severity: 'info',
|
|
317
|
-
source: 'inapp-specific'
|
|
316
|
+
source: 'inapp-specific',
|
|
318
317
|
});
|
|
319
318
|
|
|
320
319
|
// Guard against zero-length matches to avoid infinite loops
|
|
@@ -336,8 +335,9 @@ const runLiquidValidation = (html, variant, results, formatMessage = defaultMess
|
|
|
336
335
|
const liquidResults = validateLiquidHTML(html, variant);
|
|
337
336
|
|
|
338
337
|
// 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.map((e) => ({ ...e, severity: 'error' })));
|
|
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,16 +396,15 @@ 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.warnings.push({
|
|
400
|
+
type: 'warning',
|
|
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: 'warning',
|
|
406
|
+
source: 'css-validator',
|
|
407
407
|
});
|
|
408
|
-
results.isValid = false;
|
|
409
408
|
|
|
410
409
|
// Guard against zero-length matches to avoid infinite loops
|
|
411
410
|
if (match[0].length === 0) {
|
|
@@ -417,34 +416,31 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
|
|
|
417
416
|
const emptyRulesRegex = new RegExp(validationPatterns.emptyRule.source, validationPatterns.emptyRule.flags);
|
|
418
417
|
emptyRulesRegex.lastIndex = 0; // Reset lastIndex before running
|
|
419
418
|
while ((match = emptyRulesRegex.exec(css)) !== null) {
|
|
420
|
-
results.
|
|
421
|
-
type: '
|
|
419
|
+
results.warnings.push({
|
|
420
|
+
type: 'warning',
|
|
422
421
|
message: formatMessage('validator.emptyCssRule'),
|
|
423
422
|
line: getLineNumber(css, match.index),
|
|
424
423
|
column: 1,
|
|
425
424
|
rule: 'empty-rule',
|
|
426
|
-
severity: '
|
|
427
|
-
source: 'css-validator'
|
|
425
|
+
severity: 'warning',
|
|
426
|
+
source: 'css-validator',
|
|
428
427
|
});
|
|
429
|
-
results.isValid = false;
|
|
430
428
|
|
|
431
429
|
// Guard against zero-length matches to avoid infinite loops
|
|
432
430
|
if (match[0].length === 0) {
|
|
433
431
|
emptyRulesRegex.lastIndex++;
|
|
434
432
|
}
|
|
435
433
|
}
|
|
436
|
-
|
|
437
434
|
} catch (error) {
|
|
438
|
-
results.
|
|
439
|
-
type: '
|
|
435
|
+
results.warnings.push({
|
|
436
|
+
type: 'warning',
|
|
440
437
|
message: formatMessage('validator.cssValidationFailed', { error: error.message }),
|
|
441
438
|
line: 1,
|
|
442
439
|
column: 1,
|
|
443
440
|
rule: 'css-validation-error',
|
|
444
|
-
severity: '
|
|
445
|
-
source: 'css-validator'
|
|
441
|
+
severity: 'warning',
|
|
442
|
+
source: 'css-validator',
|
|
446
443
|
});
|
|
447
|
-
results.isValid = false;
|
|
448
444
|
}
|
|
449
445
|
|
|
450
446
|
return results;
|
|
@@ -457,16 +453,20 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
|
|
|
457
453
|
* @returns {Object} Combined validation results from all CSS blocks
|
|
458
454
|
*/
|
|
459
455
|
export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormatter) => {
|
|
460
|
-
if (!html)
|
|
456
|
+
if (!html) {
|
|
457
|
+
return {
|
|
458
|
+
isValid: true, errors: [], warnings: [], info: [],
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
461
|
|
|
462
462
|
// Extract CSS from style tags
|
|
463
463
|
const styleTagPattern = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
464
464
|
let match;
|
|
465
|
-
|
|
465
|
+
const 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,19 +483,18 @@ export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormat
|
|
|
483
483
|
}
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
// Check for unclosed style tags
|
|
486
|
+
// Check for unclosed style tags (warning only for CKEditor legacy compatibility)
|
|
487
487
|
const unclosedStylePattern = /<style[^>]*>(?![\s\S]*?<\/style>)/gi;
|
|
488
488
|
if (unclosedStylePattern.test(html)) {
|
|
489
|
-
allResults.
|
|
490
|
-
type: '
|
|
489
|
+
allResults.warnings.push({
|
|
490
|
+
type: 'warning',
|
|
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: 'warning',
|
|
496
|
+
source: 'css-validator',
|
|
497
497
|
});
|
|
498
|
-
allResults.isValid = false;
|
|
499
498
|
}
|
|
500
499
|
|
|
501
500
|
return allResults;
|
|
@@ -504,5 +503,5 @@ export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormat
|
|
|
504
503
|
export default {
|
|
505
504
|
validateHTML,
|
|
506
505
|
validateCSS,
|
|
507
|
-
extractAndValidateCSS
|
|
506
|
+
extractAndValidateCSS,
|
|
508
507
|
};
|