@elementor/editor-canvas 4.3.0-1047 → 4.3.0-1049
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/dist/index.js +58 -18
- package/dist/index.mjs +65 -21
- package/package.json +20 -20
- package/src/init-settings-transformers.ts +2 -0
- package/src/legacy/replacements/inline-editing/__tests__/inline-editing-eligibility.test.ts +7 -7
- package/src/legacy/replacements/inline-editing/inline-editing-elements.tsx +22 -9
- package/src/legacy/replacements/inline-editing/inline-editing-eligibility.ts +12 -3
- package/src/legacy/tabs-model-extensions.ts +2 -5
- package/src/transformers/settings/escaped-html-transformer.ts +6 -0
- package/src/utils/__tests__/sanitize-escaped-html.test.ts +105 -0
- package/src/utils/sanitize-escaped-html.ts +32 -0
package/dist/index.js
CHANGED
|
@@ -2037,6 +2037,40 @@ var dateTimeTransformer = createTransformer((values) => {
|
|
|
2037
2037
|
}).join(" ");
|
|
2038
2038
|
});
|
|
2039
2039
|
|
|
2040
|
+
// src/utils/sanitize-escaped-html.ts
|
|
2041
|
+
var import_dompurify = __toESM(require("dompurify"));
|
|
2042
|
+
var ALLOWED_NON_OPERATIONAL_ATTRS = [
|
|
2043
|
+
"href",
|
|
2044
|
+
"target",
|
|
2045
|
+
"class",
|
|
2046
|
+
"id",
|
|
2047
|
+
"style",
|
|
2048
|
+
"title",
|
|
2049
|
+
"lang",
|
|
2050
|
+
"dir",
|
|
2051
|
+
"role"
|
|
2052
|
+
];
|
|
2053
|
+
function getAllowedHtmlWrapperTags() {
|
|
2054
|
+
return window.elementorCommon?.config?.allowedHTMLWrapperTags ?? [];
|
|
2055
|
+
}
|
|
2056
|
+
function sanitizeEscapedHtml(value) {
|
|
2057
|
+
if (!value) {
|
|
2058
|
+
return "";
|
|
2059
|
+
}
|
|
2060
|
+
const allowedTags = [...getAllowedHtmlWrapperTags()];
|
|
2061
|
+
return import_dompurify.default.sanitize(value, {
|
|
2062
|
+
ALLOWED_TAGS: allowedTags,
|
|
2063
|
+
ALLOWED_ATTR: [...ALLOWED_NON_OPERATIONAL_ATTRS],
|
|
2064
|
+
ALLOW_DATA_ATTR: true,
|
|
2065
|
+
ALLOW_ARIA_ATTR: true
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// src/transformers/settings/escaped-html-transformer.ts
|
|
2070
|
+
var escapedHtmlTransformer = createTransformer((value) => {
|
|
2071
|
+
return sanitizeEscapedHtml(value);
|
|
2072
|
+
});
|
|
2073
|
+
|
|
2040
2074
|
// src/transformers/settings/html-v2-transformer.ts
|
|
2041
2075
|
var htmlV2Transformer = createTransformer((value) => {
|
|
2042
2076
|
return value?.content ?? "";
|
|
@@ -2114,11 +2148,11 @@ var plainTransformer = createTransformer((value) => {
|
|
|
2114
2148
|
});
|
|
2115
2149
|
|
|
2116
2150
|
// src/transformers/shared/svg-src-transformer.ts
|
|
2117
|
-
var
|
|
2151
|
+
var import_dompurify2 = __toESM(require("dompurify"));
|
|
2118
2152
|
var import_wp_media2 = require("@elementor/wp-media");
|
|
2119
2153
|
var SVG_INLINE_STYLES = "width: 100%; height: 100%; overflow: unset;";
|
|
2120
2154
|
function processSvgContent(svgText) {
|
|
2121
|
-
const sanitized =
|
|
2155
|
+
const sanitized = import_dompurify2.default.sanitize(svgText, {
|
|
2122
2156
|
USE_PROFILES: { svg: true, svgFilters: true }
|
|
2123
2157
|
});
|
|
2124
2158
|
const parser = new DOMParser();
|
|
@@ -2189,7 +2223,7 @@ var videoSrcTransformer = createTransformer(async (value) => {
|
|
|
2189
2223
|
|
|
2190
2224
|
// src/init-settings-transformers.ts
|
|
2191
2225
|
function initSettingsTransformers() {
|
|
2192
|
-
settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("query", queryTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("svg-src", svgSrcTransformer).register("video-src", videoSrcTransformer).register("attributes", attributesTransformer).register("date-time", dateTimeTransformer).register("html-v2", htmlV2Transformer).register("html-v3", htmlV3Transformer).register("date-range", dateRangeTransformer).register("time-range", timeRangeTransformer).registerFallback(plainTransformer);
|
|
2226
|
+
settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("query", queryTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("svg-src", svgSrcTransformer).register("video-src", videoSrcTransformer).register("attributes", attributesTransformer).register("date-time", dateTimeTransformer).register("html-v2", htmlV2Transformer).register("html-v3", htmlV3Transformer).register("escaped-html", escapedHtmlTransformer).register("date-range", dateRangeTransformer).register("time-range", timeRangeTransformer).registerFallback(plainTransformer);
|
|
2193
2227
|
}
|
|
2194
2228
|
|
|
2195
2229
|
// src/transformers/styles/background-color-overlay-transformer.ts
|
|
@@ -2578,11 +2612,11 @@ function createDomRenderer() {
|
|
|
2578
2612
|
render: environment.render
|
|
2579
2613
|
};
|
|
2580
2614
|
}
|
|
2581
|
-
function
|
|
2615
|
+
function getAllowedHtmlWrapperTags2() {
|
|
2582
2616
|
return window.elementorCommon?.config?.allowedHTMLWrapperTags ?? [];
|
|
2583
2617
|
}
|
|
2584
2618
|
function escapeHtmlTag(value) {
|
|
2585
|
-
const allowedTags =
|
|
2619
|
+
const allowedTags = getAllowedHtmlWrapperTags2();
|
|
2586
2620
|
const normalizedTag = value?.toLowerCase?.() ?? "";
|
|
2587
2621
|
return allowedTags.includes(normalizedTag) ? value : "div";
|
|
2588
2622
|
}
|
|
@@ -3516,7 +3550,7 @@ var import_editor_props3 = require("@elementor/editor-props");
|
|
|
3516
3550
|
var hasKey = (propType) => {
|
|
3517
3551
|
return "key" in propType;
|
|
3518
3552
|
};
|
|
3519
|
-
var TEXT_PROP_TYPE_KEYS = /* @__PURE__ */ new Set([import_editor_props3.htmlV3PropTypeUtil.key, import_editor_props3.stringPropTypeUtil.key]);
|
|
3553
|
+
var TEXT_PROP_TYPE_KEYS = /* @__PURE__ */ new Set([import_editor_props3.escapedHtmlPropTypeUtil.key, import_editor_props3.htmlV3PropTypeUtil.key, import_editor_props3.stringPropTypeUtil.key]);
|
|
3520
3554
|
var isCoreTextPropTypeKey = (key) => {
|
|
3521
3555
|
return TEXT_PROP_TYPE_KEYS.has(key);
|
|
3522
3556
|
};
|
|
@@ -3536,7 +3570,7 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
|
|
|
3536
3570
|
if (rawValue === null || rawValue === void 0) {
|
|
3537
3571
|
return isAllowedBySchema(propTypeFromSchema);
|
|
3538
3572
|
}
|
|
3539
|
-
return import_editor_props3.htmlV3PropTypeUtil.isValid(rawValue) || import_editor_props3.stringPropTypeUtil.isValid(rawValue);
|
|
3573
|
+
return import_editor_props3.escapedHtmlPropTypeUtil.isValid(rawValue) || import_editor_props3.htmlV3PropTypeUtil.isValid(rawValue) || import_editor_props3.stringPropTypeUtil.isValid(rawValue);
|
|
3540
3574
|
};
|
|
3541
3575
|
|
|
3542
3576
|
// src/legacy/replacements/inline-editing/inline-editing-elements.tsx
|
|
@@ -3621,17 +3655,26 @@ var InlineEditingReplacement = class extends ReplacementBase {
|
|
|
3621
3655
|
}
|
|
3622
3656
|
getExtractedContentValue() {
|
|
3623
3657
|
const propValue = this.getInlineEditablePropValue();
|
|
3658
|
+
if (import_editor_props4.escapedHtmlPropTypeUtil.isValid(propValue)) {
|
|
3659
|
+
return import_editor_props4.escapedHtmlPropTypeUtil.extract(propValue) ?? "";
|
|
3660
|
+
}
|
|
3624
3661
|
const extracted = import_editor_props4.htmlV3PropTypeUtil.extract(propValue);
|
|
3625
3662
|
return import_editor_props4.stringPropTypeUtil.extract(extracted?.content ?? null) ?? "";
|
|
3626
3663
|
}
|
|
3664
|
+
createContentPropValue(value) {
|
|
3665
|
+
const content = value || "";
|
|
3666
|
+
const propTypeKey = this.getInlineEditablePropTypeKey();
|
|
3667
|
+
if (propTypeKey === import_editor_props4.htmlV3PropTypeUtil.key) {
|
|
3668
|
+
return import_editor_props4.htmlV3PropTypeUtil.create({
|
|
3669
|
+
content: import_editor_props4.stringPropTypeUtil.create(content),
|
|
3670
|
+
children: []
|
|
3671
|
+
});
|
|
3672
|
+
}
|
|
3673
|
+
return import_editor_props4.escapedHtmlPropTypeUtil.create(content);
|
|
3674
|
+
}
|
|
3627
3675
|
setContentValue(value) {
|
|
3628
3676
|
const settingKey = this.getInlineEditablePropertyName();
|
|
3629
|
-
const
|
|
3630
|
-
const parsed = (0, import_editor_props4.parseHtmlChildren)(html);
|
|
3631
|
-
const valueToSave = import_editor_props4.htmlV3PropTypeUtil.create({
|
|
3632
|
-
content: parsed.content ? import_editor_props4.stringPropTypeUtil.create(parsed.content) : null,
|
|
3633
|
-
children: parsed.children
|
|
3634
|
-
});
|
|
3677
|
+
const valueToSave = this.createContentPropValue(value);
|
|
3635
3678
|
(0, import_editor_v1_adapters13.undoable)(
|
|
3636
3679
|
{
|
|
3637
3680
|
do: () => {
|
|
@@ -3660,7 +3703,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
|
|
|
3660
3703
|
return null;
|
|
3661
3704
|
}
|
|
3662
3705
|
if (propType.kind === "union") {
|
|
3663
|
-
const textKeys = [import_editor_props4.htmlV3PropTypeUtil.key, import_editor_props4.stringPropTypeUtil.key];
|
|
3706
|
+
const textKeys = [import_editor_props4.escapedHtmlPropTypeUtil.key, import_editor_props4.htmlV3PropTypeUtil.key, import_editor_props4.stringPropTypeUtil.key];
|
|
3664
3707
|
for (const key of textKeys) {
|
|
3665
3708
|
if (propType.prop_types[key]) {
|
|
3666
3709
|
return key;
|
|
@@ -3932,10 +3975,7 @@ var tabModelExtensions = {
|
|
|
3932
3975
|
...paragraphElement,
|
|
3933
3976
|
settings: {
|
|
3934
3977
|
...paragraphElement.settings,
|
|
3935
|
-
paragraph: import_editor_props5.
|
|
3936
|
-
content: import_editor_props5.stringPropTypeUtil.create(`Tab ${position}`),
|
|
3937
|
-
children: []
|
|
3938
|
-
})
|
|
3978
|
+
paragraph: import_editor_props5.escapedHtmlPropTypeUtil.create(`Tab ${position}`)
|
|
3939
3979
|
}
|
|
3940
3980
|
};
|
|
3941
3981
|
return [updatedParagraph, ...elements.slice(1)];
|
package/dist/index.mjs
CHANGED
|
@@ -1989,6 +1989,40 @@ var dateTimeTransformer = createTransformer((values) => {
|
|
|
1989
1989
|
}).join(" ");
|
|
1990
1990
|
});
|
|
1991
1991
|
|
|
1992
|
+
// src/utils/sanitize-escaped-html.ts
|
|
1993
|
+
import DOMPurify from "dompurify";
|
|
1994
|
+
var ALLOWED_NON_OPERATIONAL_ATTRS = [
|
|
1995
|
+
"href",
|
|
1996
|
+
"target",
|
|
1997
|
+
"class",
|
|
1998
|
+
"id",
|
|
1999
|
+
"style",
|
|
2000
|
+
"title",
|
|
2001
|
+
"lang",
|
|
2002
|
+
"dir",
|
|
2003
|
+
"role"
|
|
2004
|
+
];
|
|
2005
|
+
function getAllowedHtmlWrapperTags() {
|
|
2006
|
+
return window.elementorCommon?.config?.allowedHTMLWrapperTags ?? [];
|
|
2007
|
+
}
|
|
2008
|
+
function sanitizeEscapedHtml(value) {
|
|
2009
|
+
if (!value) {
|
|
2010
|
+
return "";
|
|
2011
|
+
}
|
|
2012
|
+
const allowedTags = [...getAllowedHtmlWrapperTags()];
|
|
2013
|
+
return DOMPurify.sanitize(value, {
|
|
2014
|
+
ALLOWED_TAGS: allowedTags,
|
|
2015
|
+
ALLOWED_ATTR: [...ALLOWED_NON_OPERATIONAL_ATTRS],
|
|
2016
|
+
ALLOW_DATA_ATTR: true,
|
|
2017
|
+
ALLOW_ARIA_ATTR: true
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
// src/transformers/settings/escaped-html-transformer.ts
|
|
2022
|
+
var escapedHtmlTransformer = createTransformer((value) => {
|
|
2023
|
+
return sanitizeEscapedHtml(value);
|
|
2024
|
+
});
|
|
2025
|
+
|
|
1992
2026
|
// src/transformers/settings/html-v2-transformer.ts
|
|
1993
2027
|
var htmlV2Transformer = createTransformer((value) => {
|
|
1994
2028
|
return value?.content ?? "";
|
|
@@ -2066,11 +2100,11 @@ var plainTransformer = createTransformer((value) => {
|
|
|
2066
2100
|
});
|
|
2067
2101
|
|
|
2068
2102
|
// src/transformers/shared/svg-src-transformer.ts
|
|
2069
|
-
import
|
|
2103
|
+
import DOMPurify2 from "dompurify";
|
|
2070
2104
|
import { getMediaAttachment as getMediaAttachment2 } from "@elementor/wp-media";
|
|
2071
2105
|
var SVG_INLINE_STYLES = "width: 100%; height: 100%; overflow: unset;";
|
|
2072
2106
|
function processSvgContent(svgText) {
|
|
2073
|
-
const sanitized =
|
|
2107
|
+
const sanitized = DOMPurify2.sanitize(svgText, {
|
|
2074
2108
|
USE_PROFILES: { svg: true, svgFilters: true }
|
|
2075
2109
|
});
|
|
2076
2110
|
const parser = new DOMParser();
|
|
@@ -2141,7 +2175,7 @@ var videoSrcTransformer = createTransformer(async (value) => {
|
|
|
2141
2175
|
|
|
2142
2176
|
// src/init-settings-transformers.ts
|
|
2143
2177
|
function initSettingsTransformers() {
|
|
2144
|
-
settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("query", queryTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("svg-src", svgSrcTransformer).register("video-src", videoSrcTransformer).register("attributes", attributesTransformer).register("date-time", dateTimeTransformer).register("html-v2", htmlV2Transformer).register("html-v3", htmlV3Transformer).register("date-range", dateRangeTransformer).register("time-range", timeRangeTransformer).registerFallback(plainTransformer);
|
|
2178
|
+
settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("query", queryTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("svg-src", svgSrcTransformer).register("video-src", videoSrcTransformer).register("attributes", attributesTransformer).register("date-time", dateTimeTransformer).register("html-v2", htmlV2Transformer).register("html-v3", htmlV3Transformer).register("escaped-html", escapedHtmlTransformer).register("date-range", dateRangeTransformer).register("time-range", timeRangeTransformer).registerFallback(plainTransformer);
|
|
2145
2179
|
}
|
|
2146
2180
|
|
|
2147
2181
|
// src/transformers/styles/background-color-overlay-transformer.ts
|
|
@@ -2530,11 +2564,11 @@ function createDomRenderer() {
|
|
|
2530
2564
|
render: environment.render
|
|
2531
2565
|
};
|
|
2532
2566
|
}
|
|
2533
|
-
function
|
|
2567
|
+
function getAllowedHtmlWrapperTags2() {
|
|
2534
2568
|
return window.elementorCommon?.config?.allowedHTMLWrapperTags ?? [];
|
|
2535
2569
|
}
|
|
2536
2570
|
function escapeHtmlTag(value) {
|
|
2537
|
-
const allowedTags =
|
|
2571
|
+
const allowedTags = getAllowedHtmlWrapperTags2();
|
|
2538
2572
|
const normalizedTag = value?.toLowerCase?.() ?? "";
|
|
2539
2573
|
return allowedTags.includes(normalizedTag) ? value : "div";
|
|
2540
2574
|
}
|
|
@@ -3193,8 +3227,8 @@ import { createRoot } from "react-dom/client";
|
|
|
3193
3227
|
import * as React11 from "react";
|
|
3194
3228
|
import { getContainer as getContainer2, getElementLabel, getElementType as getElementType2 } from "@elementor/editor-elements";
|
|
3195
3229
|
import {
|
|
3230
|
+
escapedHtmlPropTypeUtil as escapedHtmlPropTypeUtil2,
|
|
3196
3231
|
htmlV3PropTypeUtil as htmlV3PropTypeUtil2,
|
|
3197
|
-
parseHtmlChildren,
|
|
3198
3232
|
stringPropTypeUtil as stringPropTypeUtil2
|
|
3199
3233
|
} from "@elementor/editor-props";
|
|
3200
3234
|
import { __privateRunCommandSync as runCommandSync, getCurrentEditMode, undoable } from "@elementor/editor-v1-adapters";
|
|
@@ -3473,11 +3507,15 @@ var InlineEditingToolbar = ({ anchor, editor, id }) => {
|
|
|
3473
3507
|
};
|
|
3474
3508
|
|
|
3475
3509
|
// src/legacy/replacements/inline-editing/inline-editing-eligibility.ts
|
|
3476
|
-
import {
|
|
3510
|
+
import {
|
|
3511
|
+
escapedHtmlPropTypeUtil,
|
|
3512
|
+
htmlV3PropTypeUtil,
|
|
3513
|
+
stringPropTypeUtil
|
|
3514
|
+
} from "@elementor/editor-props";
|
|
3477
3515
|
var hasKey = (propType) => {
|
|
3478
3516
|
return "key" in propType;
|
|
3479
3517
|
};
|
|
3480
|
-
var TEXT_PROP_TYPE_KEYS = /* @__PURE__ */ new Set([htmlV3PropTypeUtil.key, stringPropTypeUtil.key]);
|
|
3518
|
+
var TEXT_PROP_TYPE_KEYS = /* @__PURE__ */ new Set([escapedHtmlPropTypeUtil.key, htmlV3PropTypeUtil.key, stringPropTypeUtil.key]);
|
|
3481
3519
|
var isCoreTextPropTypeKey = (key) => {
|
|
3482
3520
|
return TEXT_PROP_TYPE_KEYS.has(key);
|
|
3483
3521
|
};
|
|
@@ -3497,7 +3535,7 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
|
|
|
3497
3535
|
if (rawValue === null || rawValue === void 0) {
|
|
3498
3536
|
return isAllowedBySchema(propTypeFromSchema);
|
|
3499
3537
|
}
|
|
3500
|
-
return htmlV3PropTypeUtil.isValid(rawValue) || stringPropTypeUtil.isValid(rawValue);
|
|
3538
|
+
return escapedHtmlPropTypeUtil.isValid(rawValue) || htmlV3PropTypeUtil.isValid(rawValue) || stringPropTypeUtil.isValid(rawValue);
|
|
3501
3539
|
};
|
|
3502
3540
|
|
|
3503
3541
|
// src/legacy/replacements/inline-editing/inline-editing-elements.tsx
|
|
@@ -3582,17 +3620,26 @@ var InlineEditingReplacement = class extends ReplacementBase {
|
|
|
3582
3620
|
}
|
|
3583
3621
|
getExtractedContentValue() {
|
|
3584
3622
|
const propValue = this.getInlineEditablePropValue();
|
|
3623
|
+
if (escapedHtmlPropTypeUtil2.isValid(propValue)) {
|
|
3624
|
+
return escapedHtmlPropTypeUtil2.extract(propValue) ?? "";
|
|
3625
|
+
}
|
|
3585
3626
|
const extracted = htmlV3PropTypeUtil2.extract(propValue);
|
|
3586
3627
|
return stringPropTypeUtil2.extract(extracted?.content ?? null) ?? "";
|
|
3587
3628
|
}
|
|
3629
|
+
createContentPropValue(value) {
|
|
3630
|
+
const content = value || "";
|
|
3631
|
+
const propTypeKey = this.getInlineEditablePropTypeKey();
|
|
3632
|
+
if (propTypeKey === htmlV3PropTypeUtil2.key) {
|
|
3633
|
+
return htmlV3PropTypeUtil2.create({
|
|
3634
|
+
content: stringPropTypeUtil2.create(content),
|
|
3635
|
+
children: []
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
return escapedHtmlPropTypeUtil2.create(content);
|
|
3639
|
+
}
|
|
3588
3640
|
setContentValue(value) {
|
|
3589
3641
|
const settingKey = this.getInlineEditablePropertyName();
|
|
3590
|
-
const
|
|
3591
|
-
const parsed = parseHtmlChildren(html);
|
|
3592
|
-
const valueToSave = htmlV3PropTypeUtil2.create({
|
|
3593
|
-
content: parsed.content ? stringPropTypeUtil2.create(parsed.content) : null,
|
|
3594
|
-
children: parsed.children
|
|
3595
|
-
});
|
|
3642
|
+
const valueToSave = this.createContentPropValue(value);
|
|
3596
3643
|
undoable(
|
|
3597
3644
|
{
|
|
3598
3645
|
do: () => {
|
|
@@ -3621,7 +3668,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
|
|
|
3621
3668
|
return null;
|
|
3622
3669
|
}
|
|
3623
3670
|
if (propType.kind === "union") {
|
|
3624
|
-
const textKeys = [htmlV3PropTypeUtil2.key, stringPropTypeUtil2.key];
|
|
3671
|
+
const textKeys = [escapedHtmlPropTypeUtil2.key, htmlV3PropTypeUtil2.key, stringPropTypeUtil2.key];
|
|
3625
3672
|
for (const key of textKeys) {
|
|
3626
3673
|
if (propType.prop_types[key]) {
|
|
3627
3674
|
return key;
|
|
@@ -3877,7 +3924,7 @@ function createNestedTemplatedType(type, renderer, element) {
|
|
|
3877
3924
|
}
|
|
3878
3925
|
|
|
3879
3926
|
// src/legacy/tabs-model-extensions.ts
|
|
3880
|
-
import {
|
|
3927
|
+
import { escapedHtmlPropTypeUtil as escapedHtmlPropTypeUtil3 } from "@elementor/editor-props";
|
|
3881
3928
|
var tabModelExtensions = {
|
|
3882
3929
|
modifyDefaultChildren(elements) {
|
|
3883
3930
|
if (!Array.isArray(elements) || elements.length === 0) {
|
|
@@ -3893,10 +3940,7 @@ var tabModelExtensions = {
|
|
|
3893
3940
|
...paragraphElement,
|
|
3894
3941
|
settings: {
|
|
3895
3942
|
...paragraphElement.settings,
|
|
3896
|
-
paragraph:
|
|
3897
|
-
content: stringPropTypeUtil3.create(`Tab ${position}`),
|
|
3898
|
-
children: []
|
|
3899
|
-
})
|
|
3943
|
+
paragraph: escapedHtmlPropTypeUtil3.create(`Tab ${position}`)
|
|
3900
3944
|
}
|
|
3901
3945
|
};
|
|
3902
3946
|
return [updatedParagraph, ...elements.slice(1)];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-canvas",
|
|
3
3
|
"description": "Elementor Editor Canvas",
|
|
4
|
-
"version": "4.3.0-
|
|
4
|
+
"version": "4.3.0-1049",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -37,26 +37,26 @@
|
|
|
37
37
|
"react-dom": "^18.3.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@elementor/editor": "4.3.0-
|
|
41
|
-
"@elementor/editor-controls": "4.3.0-
|
|
42
|
-
"@elementor/editor-documents": "4.3.0-
|
|
43
|
-
"@elementor/editor-elements": "4.3.0-
|
|
44
|
-
"@elementor/editor-interactions": "4.3.0-
|
|
45
|
-
"@elementor/editor-mcp": "4.3.0-
|
|
46
|
-
"@elementor/editor-notifications": "4.3.0-
|
|
47
|
-
"@elementor/editor-props": "4.3.0-
|
|
48
|
-
"@elementor/editor-responsive": "4.3.0-
|
|
49
|
-
"@elementor/editor-styles": "4.3.0-
|
|
50
|
-
"@elementor/editor-styles-repository": "4.3.0-
|
|
51
|
-
"@elementor/editor-ui": "4.3.0-
|
|
52
|
-
"@elementor/editor-v1-adapters": "4.3.0-
|
|
53
|
-
"@elementor/events": "4.3.0-
|
|
54
|
-
"@elementor/http-client": "4.3.0-
|
|
55
|
-
"@elementor/schema": "4.3.0-
|
|
56
|
-
"@elementor/twing": "4.3.0-
|
|
40
|
+
"@elementor/editor": "4.3.0-1049",
|
|
41
|
+
"@elementor/editor-controls": "4.3.0-1049",
|
|
42
|
+
"@elementor/editor-documents": "4.3.0-1049",
|
|
43
|
+
"@elementor/editor-elements": "4.3.0-1049",
|
|
44
|
+
"@elementor/editor-interactions": "4.3.0-1049",
|
|
45
|
+
"@elementor/editor-mcp": "4.3.0-1049",
|
|
46
|
+
"@elementor/editor-notifications": "4.3.0-1049",
|
|
47
|
+
"@elementor/editor-props": "4.3.0-1049",
|
|
48
|
+
"@elementor/editor-responsive": "4.3.0-1049",
|
|
49
|
+
"@elementor/editor-styles": "4.3.0-1049",
|
|
50
|
+
"@elementor/editor-styles-repository": "4.3.0-1049",
|
|
51
|
+
"@elementor/editor-ui": "4.3.0-1049",
|
|
52
|
+
"@elementor/editor-v1-adapters": "4.3.0-1049",
|
|
53
|
+
"@elementor/events": "4.3.0-1049",
|
|
54
|
+
"@elementor/http-client": "4.3.0-1049",
|
|
55
|
+
"@elementor/schema": "4.3.0-1049",
|
|
56
|
+
"@elementor/twing": "4.3.0-1049",
|
|
57
57
|
"@elementor/ui": "1.37.5",
|
|
58
|
-
"@elementor/utils": "4.3.0-
|
|
59
|
-
"@elementor/wp-media": "4.3.0-
|
|
58
|
+
"@elementor/utils": "4.3.0-1049",
|
|
59
|
+
"@elementor/wp-media": "4.3.0-1049",
|
|
60
60
|
"@floating-ui/react": "^0.27.5",
|
|
61
61
|
"@wordpress/i18n": "^5.13.0",
|
|
62
62
|
"dompurify": "^3.2.6"
|
|
@@ -3,6 +3,7 @@ import { attributesTransformer } from './transformers/settings/attributes-transf
|
|
|
3
3
|
import { createClassesTransformer } from './transformers/settings/classes-transformer';
|
|
4
4
|
import { dateRangeTransformer } from './transformers/settings/date-range-transformer';
|
|
5
5
|
import { dateTimeTransformer } from './transformers/settings/date-time-transformer';
|
|
6
|
+
import { escapedHtmlTransformer } from './transformers/settings/escaped-html-transformer';
|
|
6
7
|
import { htmlV2Transformer } from './transformers/settings/html-v2-transformer';
|
|
7
8
|
import { htmlV3Transformer } from './transformers/settings/html-v3-transformer';
|
|
8
9
|
import { linkTransformer } from './transformers/settings/link-transformer';
|
|
@@ -27,6 +28,7 @@ export function initSettingsTransformers() {
|
|
|
27
28
|
.register( 'date-time', dateTimeTransformer )
|
|
28
29
|
.register( 'html-v2', htmlV2Transformer )
|
|
29
30
|
.register( 'html-v3', htmlV3Transformer )
|
|
31
|
+
.register( 'escaped-html', escapedHtmlTransformer )
|
|
30
32
|
.register( 'date-range', dateRangeTransformer )
|
|
31
33
|
.register( 'time-range', timeRangeTransformer )
|
|
32
34
|
.registerFallback( plainTransformer );
|
|
@@ -13,10 +13,10 @@ const createUnionPropType = ( keys: string[] ): PropType =>
|
|
|
13
13
|
} ) as PropType;
|
|
14
14
|
|
|
15
15
|
describe( 'isInlineEditingAllowed', () => {
|
|
16
|
-
it( 'should allow inline editing for html
|
|
16
|
+
it( 'should allow inline editing for escaped-html prop values', () => {
|
|
17
17
|
expect(
|
|
18
18
|
isInlineEditingAllowed( {
|
|
19
|
-
rawValue: { $$type: 'html
|
|
19
|
+
rawValue: { $$type: 'escaped-html', value: 'Hello' },
|
|
20
20
|
propTypeFromSchema: null,
|
|
21
21
|
} )
|
|
22
22
|
).toBe( true );
|
|
@@ -40,11 +40,11 @@ describe( 'isInlineEditingAllowed', () => {
|
|
|
40
40
|
).toBe( false );
|
|
41
41
|
} );
|
|
42
42
|
|
|
43
|
-
it( 'should allow when value is unset but schema key is html
|
|
43
|
+
it( 'should allow when value is unset but schema key is escaped-html', () => {
|
|
44
44
|
expect(
|
|
45
45
|
isInlineEditingAllowed( {
|
|
46
46
|
rawValue: undefined,
|
|
47
|
-
propTypeFromSchema: createPlainPropType( 'html
|
|
47
|
+
propTypeFromSchema: createPlainPropType( 'escaped-html' ),
|
|
48
48
|
} )
|
|
49
49
|
).toBe( true );
|
|
50
50
|
} );
|
|
@@ -67,16 +67,16 @@ describe( 'isInlineEditingAllowed', () => {
|
|
|
67
67
|
).toBe( false );
|
|
68
68
|
} );
|
|
69
69
|
|
|
70
|
-
it( 'should allow when value is unset and union schema includes html
|
|
70
|
+
it( 'should allow when value is unset and union schema includes escaped-html', () => {
|
|
71
71
|
expect(
|
|
72
72
|
isInlineEditingAllowed( {
|
|
73
73
|
rawValue: undefined,
|
|
74
|
-
propTypeFromSchema: createUnionPropType( [ 'dynamic', 'html
|
|
74
|
+
propTypeFromSchema: createUnionPropType( [ 'dynamic', 'escaped-html' ] ),
|
|
75
75
|
} )
|
|
76
76
|
).toBe( true );
|
|
77
77
|
} );
|
|
78
78
|
|
|
79
|
-
it( 'should disallow when value is unset and union schema does not include html-v3/string', () => {
|
|
79
|
+
it( 'should disallow when value is unset and union schema does not include escaped-html/html-v3/string', () => {
|
|
80
80
|
expect(
|
|
81
81
|
isInlineEditingAllowed( {
|
|
82
82
|
rawValue: undefined,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { getContainer, getElementLabel, getElementType } from '@elementor/editor-elements';
|
|
3
3
|
import {
|
|
4
|
+
escapedHtmlPropTypeUtil,
|
|
4
5
|
htmlV3PropTypeUtil,
|
|
5
|
-
parseHtmlChildren,
|
|
6
6
|
type PropType,
|
|
7
7
|
type PropValue,
|
|
8
8
|
stringPropTypeUtil,
|
|
@@ -126,20 +126,33 @@ export default class InlineEditingReplacement extends ReplacementBase {
|
|
|
126
126
|
|
|
127
127
|
getExtractedContentValue() {
|
|
128
128
|
const propValue = this.getInlineEditablePropValue();
|
|
129
|
+
|
|
130
|
+
if ( escapedHtmlPropTypeUtil.isValid( propValue ) ) {
|
|
131
|
+
return escapedHtmlPropTypeUtil.extract( propValue ) ?? '';
|
|
132
|
+
}
|
|
133
|
+
|
|
129
134
|
const extracted = htmlV3PropTypeUtil.extract( propValue );
|
|
130
135
|
|
|
131
136
|
return stringPropTypeUtil.extract( extracted?.content ?? null ) ?? '';
|
|
132
137
|
}
|
|
133
138
|
|
|
139
|
+
createContentPropValue( value: string | null ): PropValue {
|
|
140
|
+
const content = value || '';
|
|
141
|
+
const propTypeKey = this.getInlineEditablePropTypeKey();
|
|
142
|
+
|
|
143
|
+
if ( propTypeKey === htmlV3PropTypeUtil.key ) {
|
|
144
|
+
return htmlV3PropTypeUtil.create( {
|
|
145
|
+
content: stringPropTypeUtil.create( content ),
|
|
146
|
+
children: [],
|
|
147
|
+
} );
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return escapedHtmlPropTypeUtil.create( content );
|
|
151
|
+
}
|
|
152
|
+
|
|
134
153
|
setContentValue( value: string | null ) {
|
|
135
154
|
const settingKey = this.getInlineEditablePropertyName();
|
|
136
|
-
const
|
|
137
|
-
const parsed = parseHtmlChildren( html );
|
|
138
|
-
|
|
139
|
-
const valueToSave = htmlV3PropTypeUtil.create( {
|
|
140
|
-
content: parsed.content ? stringPropTypeUtil.create( parsed.content ) : null,
|
|
141
|
-
children: parsed.children,
|
|
142
|
-
} );
|
|
155
|
+
const valueToSave = this.createContentPropValue( value );
|
|
143
156
|
|
|
144
157
|
undoable(
|
|
145
158
|
{
|
|
@@ -174,7 +187,7 @@ export default class InlineEditingReplacement extends ReplacementBase {
|
|
|
174
187
|
}
|
|
175
188
|
|
|
176
189
|
if ( propType.kind === 'union' ) {
|
|
177
|
-
const textKeys = [ htmlV3PropTypeUtil.key, stringPropTypeUtil.key ];
|
|
190
|
+
const textKeys = [ escapedHtmlPropTypeUtil.key, htmlV3PropTypeUtil.key, stringPropTypeUtil.key ];
|
|
178
191
|
|
|
179
192
|
for ( const key of textKeys ) {
|
|
180
193
|
if ( propType.prop_types[ key ] ) {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
escapedHtmlPropTypeUtil,
|
|
3
|
+
htmlV3PropTypeUtil,
|
|
4
|
+
type PropType,
|
|
5
|
+
stringPropTypeUtil,
|
|
6
|
+
} from '@elementor/editor-props';
|
|
2
7
|
|
|
3
8
|
type InlineEditingEligibilityArgs = {
|
|
4
9
|
rawValue: unknown;
|
|
@@ -9,7 +14,7 @@ const hasKey = ( propType: PropType ): propType is PropType & { key: unknown } =
|
|
|
9
14
|
return 'key' in propType;
|
|
10
15
|
};
|
|
11
16
|
|
|
12
|
-
const TEXT_PROP_TYPE_KEYS = new Set( [ htmlV3PropTypeUtil.key, stringPropTypeUtil.key ] );
|
|
17
|
+
const TEXT_PROP_TYPE_KEYS = new Set( [ escapedHtmlPropTypeUtil.key, htmlV3PropTypeUtil.key, stringPropTypeUtil.key ] );
|
|
13
18
|
|
|
14
19
|
const isCoreTextPropTypeKey = ( key: unknown ): boolean => {
|
|
15
20
|
return ( TEXT_PROP_TYPE_KEYS as Set< unknown > ).has( key );
|
|
@@ -36,5 +41,9 @@ export const isInlineEditingAllowed = ( { rawValue, propTypeFromSchema }: Inline
|
|
|
36
41
|
return isAllowedBySchema( propTypeFromSchema );
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
return
|
|
44
|
+
return (
|
|
45
|
+
escapedHtmlPropTypeUtil.isValid( rawValue ) ||
|
|
46
|
+
htmlV3PropTypeUtil.isValid( rawValue ) ||
|
|
47
|
+
stringPropTypeUtil.isValid( rawValue )
|
|
48
|
+
);
|
|
40
49
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { escapedHtmlPropTypeUtil } from '@elementor/editor-props';
|
|
2
2
|
|
|
3
3
|
import { type ModelExtensions } from './create-nested-templated-element-type';
|
|
4
4
|
import { registerModelExtensions } from './init-legacy-views';
|
|
@@ -23,10 +23,7 @@ const tabModelExtensions: ModelExtensions = {
|
|
|
23
23
|
...paragraphElement,
|
|
24
24
|
settings: {
|
|
25
25
|
...paragraphElement.settings,
|
|
26
|
-
paragraph:
|
|
27
|
-
content: stringPropTypeUtil.create( `Tab ${ position }` ),
|
|
28
|
-
children: [],
|
|
29
|
-
} ),
|
|
26
|
+
paragraph: escapedHtmlPropTypeUtil.create( `Tab ${ position }` ),
|
|
30
27
|
},
|
|
31
28
|
};
|
|
32
29
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { sanitizeEscapedHtml } from '../../utils/sanitize-escaped-html';
|
|
2
|
+
import { createTransformer } from '../create-transformer';
|
|
3
|
+
|
|
4
|
+
export const escapedHtmlTransformer = createTransformer( ( value: string | null ) => {
|
|
5
|
+
return sanitizeEscapedHtml( value );
|
|
6
|
+
} );
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { sanitizeEscapedHtml } from '../sanitize-escaped-html';
|
|
2
|
+
|
|
3
|
+
const TEST_ALLOWED_HTML_WRAPPER_TAGS = [ 'a', 'div', 'span', 'strong' ];
|
|
4
|
+
|
|
5
|
+
describe( 'sanitizeEscapedHtml', () => {
|
|
6
|
+
beforeEach( () => {
|
|
7
|
+
window.elementorCommon = {
|
|
8
|
+
config: {
|
|
9
|
+
allowedHTMLWrapperTags: TEST_ALLOWED_HTML_WRAPPER_TAGS,
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
} );
|
|
13
|
+
|
|
14
|
+
afterEach( () => {
|
|
15
|
+
delete window.elementorCommon;
|
|
16
|
+
} );
|
|
17
|
+
|
|
18
|
+
it( 'returns an empty string for nullish values', () => {
|
|
19
|
+
// Arrange & Act & Assert.
|
|
20
|
+
expect( sanitizeEscapedHtml( null ) ).toBe( '' );
|
|
21
|
+
expect( sanitizeEscapedHtml( '' ) ).toBe( '' );
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
it( 'keeps allowed tags and strips disallowed tags', () => {
|
|
25
|
+
// Arrange.
|
|
26
|
+
const value = 'Hello <script>alert(1)</script><strong>world</strong>';
|
|
27
|
+
|
|
28
|
+
// Act.
|
|
29
|
+
const result = sanitizeEscapedHtml( value );
|
|
30
|
+
|
|
31
|
+
// Assert.
|
|
32
|
+
expect( result ).toBe( 'Hello <strong>world</strong>' );
|
|
33
|
+
} );
|
|
34
|
+
|
|
35
|
+
it( 'fails closed when the localized allowlist is missing', () => {
|
|
36
|
+
// Arrange.
|
|
37
|
+
delete window.elementorCommon;
|
|
38
|
+
const value = '<strong>world</strong>';
|
|
39
|
+
|
|
40
|
+
// Act.
|
|
41
|
+
const result = sanitizeEscapedHtml( value );
|
|
42
|
+
|
|
43
|
+
// Assert.
|
|
44
|
+
expect( result ).toBe( 'world' );
|
|
45
|
+
} );
|
|
46
|
+
|
|
47
|
+
it( 'strips javascript href from links', () => {
|
|
48
|
+
// Arrange.
|
|
49
|
+
const value = '<a href="javascript:alert(1)">click</a>';
|
|
50
|
+
|
|
51
|
+
// Act.
|
|
52
|
+
const result = sanitizeEscapedHtml( value );
|
|
53
|
+
|
|
54
|
+
// Assert.
|
|
55
|
+
expect( result ).not.toContain( 'javascript:' );
|
|
56
|
+
expect( result ).toContain( 'click' );
|
|
57
|
+
} );
|
|
58
|
+
|
|
59
|
+
it( 'strips data href from links', () => {
|
|
60
|
+
// Arrange.
|
|
61
|
+
const value = '<a href="data:text/html,<script>alert(1)</script>">click</a>';
|
|
62
|
+
|
|
63
|
+
// Act.
|
|
64
|
+
const result = sanitizeEscapedHtml( value );
|
|
65
|
+
|
|
66
|
+
// Assert.
|
|
67
|
+
expect( result ).not.toMatch( /<a\s+href=/i );
|
|
68
|
+
expect( result ).toContain( 'click' );
|
|
69
|
+
} );
|
|
70
|
+
|
|
71
|
+
it( 'preserves non-operational attributes', () => {
|
|
72
|
+
// Arrange.
|
|
73
|
+
const value =
|
|
74
|
+
'<span id="e-1" class="foo bar" style="color: red;" title="t" lang="en" dir="ltr" role="text" data-x="1" aria-label="label">world</span>';
|
|
75
|
+
|
|
76
|
+
// Act.
|
|
77
|
+
const result = sanitizeEscapedHtml( value );
|
|
78
|
+
|
|
79
|
+
// Assert.
|
|
80
|
+
expect( result ).toContain( 'id="e-1"' );
|
|
81
|
+
expect( result ).toContain( 'class="foo bar"' );
|
|
82
|
+
expect( result ).toContain( 'style="color: red;"' );
|
|
83
|
+
expect( result ).toContain( 'title="t"' );
|
|
84
|
+
expect( result ).toContain( 'lang="en"' );
|
|
85
|
+
expect( result ).toContain( 'dir="ltr"' );
|
|
86
|
+
expect( result ).toContain( 'role="text"' );
|
|
87
|
+
expect( result ).toContain( 'data-x="1"' );
|
|
88
|
+
expect( result ).toContain( 'aria-label="label"' );
|
|
89
|
+
expect( result ).toContain( 'world' );
|
|
90
|
+
} );
|
|
91
|
+
|
|
92
|
+
it( 'strips functional event-handler attributes', () => {
|
|
93
|
+
// Arrange.
|
|
94
|
+
const value = '<span id="e-1" onclick="evil()" onmouseover="evil()">world</span>';
|
|
95
|
+
|
|
96
|
+
// Act.
|
|
97
|
+
const result = sanitizeEscapedHtml( value );
|
|
98
|
+
|
|
99
|
+
// Assert.
|
|
100
|
+
expect( result ).toContain( 'id="e-1"' );
|
|
101
|
+
expect( result ).not.toContain( 'onclick' );
|
|
102
|
+
expect( result ).not.toContain( 'onmouseover' );
|
|
103
|
+
expect( result ).toContain( 'world' );
|
|
104
|
+
} );
|
|
105
|
+
} );
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import DOMPurify from 'dompurify';
|
|
2
|
+
|
|
3
|
+
const ALLOWED_NON_OPERATIONAL_ATTRS = [
|
|
4
|
+
'href',
|
|
5
|
+
'target',
|
|
6
|
+
'class',
|
|
7
|
+
'id',
|
|
8
|
+
'style',
|
|
9
|
+
'title',
|
|
10
|
+
'lang',
|
|
11
|
+
'dir',
|
|
12
|
+
'role',
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
function getAllowedHtmlWrapperTags(): readonly string[] {
|
|
16
|
+
return window.elementorCommon?.config?.allowedHTMLWrapperTags ?? [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function sanitizeEscapedHtml( value: string | null ): string {
|
|
20
|
+
if ( ! value ) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const allowedTags = [ ...getAllowedHtmlWrapperTags() ];
|
|
25
|
+
|
|
26
|
+
return DOMPurify.sanitize( value, {
|
|
27
|
+
ALLOWED_TAGS: allowedTags,
|
|
28
|
+
ALLOWED_ATTR: [ ...ALLOWED_NON_OPERATIONAL_ATTRS ],
|
|
29
|
+
ALLOW_DATA_ATTR: true,
|
|
30
|
+
ALLOW_ARIA_ATTR: true,
|
|
31
|
+
} );
|
|
32
|
+
}
|