@knime/kds-rich-text-editor 10.1.0 → 10.2.0
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/CHANGELOG.md +9 -0
- package/dist/index.js +316 -88
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -9,8 +9,11 @@ import Link from "@tiptap/extension-link";
|
|
|
9
9
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
10
10
|
import TextAlign from "@tiptap/extension-text-align";
|
|
11
11
|
import Paragraph from "@tiptap/extension-paragraph";
|
|
12
|
-
import './index.css';//#region ../../node_modules/.pnpm/dompurify@3.4.
|
|
13
|
-
/*! @license DOMPurify 3.4.
|
|
12
|
+
import './index.css';//#region ../../node_modules/.pnpm/dompurify@3.4.16/node_modules/dompurify/dist/purify.es.mjs
|
|
13
|
+
/*! @license DOMPurify 3.4.16 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.16/LICENSE */
|
|
14
|
+
function _OverloadYield(e, d) {
|
|
15
|
+
this.v = e, this.k = d;
|
|
16
|
+
}
|
|
14
17
|
function _arrayLikeToArray(r, a) {
|
|
15
18
|
(null == a || a > r.length) && (a = r.length);
|
|
16
19
|
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
|
|
@@ -22,12 +25,14 @@ function _arrayWithHoles(r) {
|
|
|
22
25
|
function _iterableToArrayLimit(r, l) {
|
|
23
26
|
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
24
27
|
if (null != t) {
|
|
25
|
-
var e, n, i, u, a = [], f =
|
|
28
|
+
var e, n, i, u, a = [], f = !0, o = !1;
|
|
26
29
|
try {
|
|
27
|
-
if (i = (t = t.call(r)).next, 0 === l)
|
|
28
|
-
|
|
30
|
+
if (i = (t = t.call(r)).next, 0 === l) {
|
|
31
|
+
if (Object(t) !== t) return;
|
|
32
|
+
f = !1;
|
|
33
|
+
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
|
|
29
34
|
} catch (r) {
|
|
30
|
-
o =
|
|
35
|
+
o = !0, n = r;
|
|
31
36
|
} finally {
|
|
32
37
|
try {
|
|
33
38
|
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
|
|
@@ -41,6 +46,7 @@ function _iterableToArrayLimit(r, l) {
|
|
|
41
46
|
function _nonIterableRest() {
|
|
42
47
|
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
43
48
|
}
|
|
49
|
+
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
|
|
44
50
|
function _slicedToArray(r, e) {
|
|
45
51
|
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
|
|
46
52
|
}
|
|
@@ -51,9 +57,64 @@ function _unsupportedIterableToArray(r, a) {
|
|
|
51
57
|
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
|
-
|
|
55
|
-
var
|
|
56
|
-
|
|
60
|
+
function AsyncGenerator(e) {
|
|
61
|
+
var t, n;
|
|
62
|
+
function resume(t, n) {
|
|
63
|
+
try {
|
|
64
|
+
var r = e[t](n), o = r.value, u = o instanceof _OverloadYield;
|
|
65
|
+
Promise.resolve(u ? o.v : o).then(function(n) {
|
|
66
|
+
if (u) {
|
|
67
|
+
var i = "return" === t && o.k ? t : "next";
|
|
68
|
+
if (!o.k || n.done) return resume(i, n);
|
|
69
|
+
n = e[i](n).value;
|
|
70
|
+
}
|
|
71
|
+
settle(!!r.done, n);
|
|
72
|
+
}, function(e) {
|
|
73
|
+
resume("throw", e);
|
|
74
|
+
});
|
|
75
|
+
} catch (e) {
|
|
76
|
+
settle(2, e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function settle(e, r) {
|
|
80
|
+
2 === e ? t.reject(r) : t.resolve({
|
|
81
|
+
value: r,
|
|
82
|
+
done: e
|
|
83
|
+
}), (t = t.next) ? resume(t.key, t.arg) : n = null;
|
|
84
|
+
}
|
|
85
|
+
this._invoke = function(e, r) {
|
|
86
|
+
return new Promise(function(o, u) {
|
|
87
|
+
var i = {
|
|
88
|
+
key: e,
|
|
89
|
+
arg: r,
|
|
90
|
+
resolve: o,
|
|
91
|
+
reject: u,
|
|
92
|
+
next: null
|
|
93
|
+
};
|
|
94
|
+
n ? n = n.next = i : (t = n = i, resume(e, r));
|
|
95
|
+
});
|
|
96
|
+
}, "function" != typeof e.return && (this.return = void 0);
|
|
97
|
+
}
|
|
98
|
+
AsyncGenerator.prototype["function" == typeof Symbol && Symbol.asyncIterator || "@@asyncIterator"] = function() {
|
|
99
|
+
return this;
|
|
100
|
+
}, AsyncGenerator.prototype.next = function(e) {
|
|
101
|
+
return this._invoke("next", e);
|
|
102
|
+
}, AsyncGenerator.prototype.throw = function(e) {
|
|
103
|
+
return this._invoke("throw", e);
|
|
104
|
+
}, AsyncGenerator.prototype.return = function(e) {
|
|
105
|
+
return this._invoke("return", e);
|
|
106
|
+
};
|
|
107
|
+
var entries = Object.entries;
|
|
108
|
+
var setPrototypeOf = Object.setPrototypeOf;
|
|
109
|
+
var isFrozen = Object.isFrozen;
|
|
110
|
+
var getPrototypeOf = Object.getPrototypeOf;
|
|
111
|
+
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
112
|
+
var freeze = Object.freeze;
|
|
113
|
+
var seal = Object.seal;
|
|
114
|
+
var create = Object.create;
|
|
115
|
+
var _ref = typeof Reflect !== "undefined" && Reflect;
|
|
116
|
+
var apply = _ref.apply;
|
|
117
|
+
var construct = _ref.construct;
|
|
57
118
|
if (!freeze) freeze = function freeze(x) {
|
|
58
119
|
return x;
|
|
59
120
|
};
|
|
@@ -69,9 +130,11 @@ if (!construct) construct = function construct(Func) {
|
|
|
69
130
|
return new Func(...args);
|
|
70
131
|
};
|
|
71
132
|
var arrayForEach = unapply(Array.prototype.forEach);
|
|
133
|
+
Array.prototype.indexOf;
|
|
72
134
|
var arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
|
|
73
135
|
var arrayPop = unapply(Array.prototype.pop);
|
|
74
136
|
var arrayPush = unapply(Array.prototype.push);
|
|
137
|
+
Array.prototype.slice;
|
|
75
138
|
var arraySplice = unapply(Array.prototype.splice);
|
|
76
139
|
var arrayIsArray = Array.isArray;
|
|
77
140
|
var stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
@@ -731,6 +794,7 @@ var svg = freeze([
|
|
|
731
794
|
"patterncontentunits",
|
|
732
795
|
"patterntransform",
|
|
733
796
|
"patternunits",
|
|
797
|
+
"pointer-events",
|
|
734
798
|
"points",
|
|
735
799
|
"preservealpha",
|
|
736
800
|
"preserveaspectratio",
|
|
@@ -785,6 +849,7 @@ var svg = freeze([
|
|
|
785
849
|
"u2",
|
|
786
850
|
"unicode",
|
|
787
851
|
"values",
|
|
852
|
+
"vector-effect",
|
|
788
853
|
"viewbox",
|
|
789
854
|
"visibility",
|
|
790
855
|
"version",
|
|
@@ -898,6 +963,24 @@ var NODE_TYPE = {
|
|
|
898
963
|
documentFragment: 11,
|
|
899
964
|
notation: 12
|
|
900
965
|
};
|
|
966
|
+
var LITERAL_TEXT_ELEMENT_NAMES = [
|
|
967
|
+
"style",
|
|
968
|
+
"script",
|
|
969
|
+
"xmp",
|
|
970
|
+
"iframe",
|
|
971
|
+
"noembed",
|
|
972
|
+
"noframes",
|
|
973
|
+
"plaintext",
|
|
974
|
+
"noscript"
|
|
975
|
+
];
|
|
976
|
+
var LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, LITERAL_TEXT_ELEMENT_NAMES));
|
|
977
|
+
var LITERAL_TEXT_CLOSE = function() {
|
|
978
|
+
const map = {};
|
|
979
|
+
arrayForEach(LITERAL_TEXT_ELEMENT_NAMES, (name) => {
|
|
980
|
+
map[name] = seal(new RegExp("</" + name + "(?=[\\t\\n\\f\\r />])", "i"));
|
|
981
|
+
});
|
|
982
|
+
return freeze(map);
|
|
983
|
+
}();
|
|
901
984
|
var getGlobal = function getGlobal() {
|
|
902
985
|
return typeof window === "undefined" ? null : window;
|
|
903
986
|
};
|
|
@@ -957,10 +1040,25 @@ var _createHooksMap = function _createHooksMap() {
|
|
|
957
1040
|
var _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
|
|
958
1041
|
return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
|
|
959
1042
|
};
|
|
1043
|
+
/**
|
|
1044
|
+
* Resolve an object-valued configuration option: a prototype-free clone
|
|
1045
|
+
* of cfg[key] when it is an own, truthy object property, else a fresh
|
|
1046
|
+
* fallback built by makeFallback (fresh on every parse, so a previous
|
|
1047
|
+
* parse can never leak state into the next one).
|
|
1048
|
+
*
|
|
1049
|
+
* @param cfg the cloned, prototype-free configuration object
|
|
1050
|
+
* @param key the configuration property to read
|
|
1051
|
+
* @param makeFallback builds the fallback value when the option is absent
|
|
1052
|
+
* @returns the resolved object
|
|
1053
|
+
*/
|
|
1054
|
+
var _resolveObjectOption = function _resolveObjectOption(cfg, key, makeFallback) {
|
|
1055
|
+
const value = objectHasOwnProperty(cfg, key) ? cfg[key] : void 0;
|
|
1056
|
+
return value && typeof value === "object" ? clone(value) : makeFallback();
|
|
1057
|
+
};
|
|
960
1058
|
function createDOMPurify() {
|
|
961
1059
|
let window = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
|
|
962
1060
|
const DOMPurify = (root) => createDOMPurify(root);
|
|
963
|
-
DOMPurify.version = "3.4.
|
|
1061
|
+
DOMPurify.version = "3.4.16";
|
|
964
1062
|
DOMPurify.removed = [];
|
|
965
1063
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
966
1064
|
DOMPurify.isSupported = false;
|
|
@@ -977,6 +1075,7 @@ function createDOMPurify() {
|
|
|
977
1075
|
const ElementPrototype = Element.prototype;
|
|
978
1076
|
const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
|
|
979
1077
|
const remove = lookupGetter(ElementPrototype, "remove");
|
|
1078
|
+
const removeAttributeNode = lookupGetter(ElementPrototype, "removeAttributeNode");
|
|
980
1079
|
const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
|
|
981
1080
|
const getChildNodes = lookupGetter(ElementPrototype, "childNodes");
|
|
982
1081
|
const getParentNode = lookupGetter(ElementPrototype, "parentNode");
|
|
@@ -985,6 +1084,12 @@ function createDOMPurify() {
|
|
|
985
1084
|
const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeType") : null;
|
|
986
1085
|
const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeName") : null;
|
|
987
1086
|
const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, "ownerDocument") : null;
|
|
1087
|
+
const _readNodeType = function _readNodeType(node) {
|
|
1088
|
+
return getNodeType ? getNodeType(node) : node.nodeType;
|
|
1089
|
+
};
|
|
1090
|
+
const _readNodeName = function _readNodeName(node) {
|
|
1091
|
+
return getNodeName ? getNodeName(node) : node.nodeName;
|
|
1092
|
+
};
|
|
988
1093
|
if (typeof HTMLTemplateElement === "function") {
|
|
989
1094
|
const template = document.createElement("template");
|
|
990
1095
|
if (template.content && template.content.ownerDocument) document = template.content.ownerDocument;
|
|
@@ -1242,9 +1347,9 @@ function createDOMPurify() {
|
|
|
1242
1347
|
IN_PLACE = cfg.IN_PLACE || false;
|
|
1243
1348
|
IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI;
|
|
1244
1349
|
NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE;
|
|
1245
|
-
MATHML_TEXT_INTEGRATION_POINTS =
|
|
1246
|
-
HTML_INTEGRATION_POINTS =
|
|
1247
|
-
const customElementHandling =
|
|
1350
|
+
MATHML_TEXT_INTEGRATION_POINTS = _resolveObjectOption(cfg, "MATHML_TEXT_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS));
|
|
1351
|
+
HTML_INTEGRATION_POINTS = _resolveObjectOption(cfg, "HTML_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS));
|
|
1352
|
+
const customElementHandling = _resolveObjectOption(cfg, "CUSTOM_ELEMENT_HANDLING", () => create(null));
|
|
1248
1353
|
CUSTOM_ELEMENT_HANDLING = create(null);
|
|
1249
1354
|
if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck;
|
|
1250
1355
|
if (objectHasOwnProperty(customElementHandling, "attributeNameCheck") && isRegexOrFunction(customElementHandling.attributeNameCheck)) CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck;
|
|
@@ -1291,11 +1396,6 @@ function createDOMPurify() {
|
|
|
1291
1396
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
1292
1397
|
}
|
|
1293
1398
|
}
|
|
1294
|
-
if (objectHasOwnProperty(cfg, "ADD_URI_SAFE_ATTR") && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
1295
|
-
if (objectHasOwnProperty(cfg, "FORBID_CONTENTS") && arrayIsArray(cfg.FORBID_CONTENTS)) {
|
|
1296
|
-
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
1297
|
-
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
1298
|
-
}
|
|
1299
1399
|
if (objectHasOwnProperty(cfg, "ADD_FORBID_CONTENTS") && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
|
|
1300
1400
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
1301
1401
|
addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
|
|
@@ -1412,6 +1512,32 @@ function createDOMPurify() {
|
|
|
1412
1512
|
}
|
|
1413
1513
|
};
|
|
1414
1514
|
/**
|
|
1515
|
+
* _stripAttributeNode
|
|
1516
|
+
*
|
|
1517
|
+
* Remove a single Attr node case/namespace-exactly on an attribute-teardown
|
|
1518
|
+
* path. Name-based removeAttribute() ASCII-lowercases its lookup key for an
|
|
1519
|
+
* HTML element in an HTML document and so silently misses a case-preserved
|
|
1520
|
+
* handler (e.g. `ONERROR` off an XML/XHTML import) - the same defect
|
|
1521
|
+
* _removeAttribute() was fixed for, which a name-based call would reintroduce
|
|
1522
|
+
* on these IN_PLACE teardown paths. Unlike _removeAttribute this does not
|
|
1523
|
+
* record into DOMPurify.removed: the neutralize passes intentionally do not
|
|
1524
|
+
* book-keep. A clobbered/detached node falls back to best-effort name-based
|
|
1525
|
+
* removal.
|
|
1526
|
+
*
|
|
1527
|
+
* @param element the element to strip the attribute from
|
|
1528
|
+
* @param attribute the Attr node to remove
|
|
1529
|
+
* @param name the attribute's name, for the fallback path
|
|
1530
|
+
*/
|
|
1531
|
+
const _stripAttributeNode = function _stripAttributeNode(element, attribute, name) {
|
|
1532
|
+
try {
|
|
1533
|
+
removeAttributeNode(element, attribute);
|
|
1534
|
+
} catch (_) {
|
|
1535
|
+
try {
|
|
1536
|
+
element.removeAttribute(name);
|
|
1537
|
+
} catch (_) {}
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
/**
|
|
1415
1541
|
* _neutralizeRoot
|
|
1416
1542
|
*
|
|
1417
1543
|
* Fail-closed teardown of an in-place root after the sanitize walk aborts
|
|
@@ -1445,30 +1571,45 @@ function createDOMPurify() {
|
|
|
1445
1571
|
if (attributes) for (let i = attributes.length - 1; i >= 0; --i) {
|
|
1446
1572
|
const attribute = attributes[i];
|
|
1447
1573
|
const name = attribute && attribute.name;
|
|
1448
|
-
if (typeof name === "string")
|
|
1449
|
-
root.removeAttribute(name);
|
|
1450
|
-
} catch (_) {}
|
|
1574
|
+
if (typeof name === "string") _stripAttributeNode(root, attribute, name);
|
|
1451
1575
|
}
|
|
1452
1576
|
};
|
|
1453
1577
|
/**
|
|
1454
1578
|
* _removeAttribute
|
|
1455
1579
|
*
|
|
1580
|
+
* Name-based getAttributeNode()/removeAttribute() ASCII-lowercase their
|
|
1581
|
+
* lookup key for HTML elements in an HTML document, so they silently miss an
|
|
1582
|
+
* attribute whose stored qualified name still contains uppercase ASCII
|
|
1583
|
+
* letters. That happens when the node came from a case-preserving source
|
|
1584
|
+
* (an XML/XHTML document imported via importNode(), or createAttributeNS()),
|
|
1585
|
+
* where e.g. `ONERROR` survives the walk: the policy check lowercases to
|
|
1586
|
+
* `onerror` and rejects it, but `removeAttribute('ONERROR')` looks up
|
|
1587
|
+
* `onerror` and finds nothing. Remove the exact Attr node instead, which is
|
|
1588
|
+
* case- and namespace-exact, and fall back to name-based removal only when
|
|
1589
|
+
* the caller could not supply the node.
|
|
1590
|
+
*
|
|
1456
1591
|
* @param name an Attribute name
|
|
1457
1592
|
* @param element a DOM node
|
|
1593
|
+
* @param attr the exact Attr node to remove, when the caller has it
|
|
1458
1594
|
*/
|
|
1459
|
-
const _removeAttribute = function _removeAttribute(name, element) {
|
|
1595
|
+
const _removeAttribute = function _removeAttribute(name, element, attr) {
|
|
1596
|
+
if (!attr) try {
|
|
1597
|
+
attr = element.getAttributeNode(name);
|
|
1598
|
+
} catch (_) {
|
|
1599
|
+
attr = null;
|
|
1600
|
+
}
|
|
1601
|
+
arrayPush(DOMPurify.removed, {
|
|
1602
|
+
attribute: attr || null,
|
|
1603
|
+
from: element
|
|
1604
|
+
});
|
|
1460
1605
|
try {
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
from: element
|
|
1464
|
-
});
|
|
1606
|
+
if (attr) removeAttributeNode(element, attr);
|
|
1607
|
+
else element.removeAttribute(name);
|
|
1465
1608
|
} catch (_) {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
});
|
|
1609
|
+
try {
|
|
1610
|
+
element.removeAttribute(name);
|
|
1611
|
+
} catch (_) {}
|
|
1470
1612
|
}
|
|
1471
|
-
element.removeAttribute(name);
|
|
1472
1613
|
if (name === "is") if (RETURN_DOM || RETURN_DOM_FRAGMENT) try {
|
|
1473
1614
|
_forceRemove(element);
|
|
1474
1615
|
} catch (_) {}
|
|
@@ -1493,9 +1634,7 @@ function createDOMPurify() {
|
|
|
1493
1634
|
const attribute = attributes[i];
|
|
1494
1635
|
const name = attribute && attribute.name;
|
|
1495
1636
|
if (typeof name !== "string" || ALLOWED_ATTR[transformCaseFunc(name)]) continue;
|
|
1496
|
-
|
|
1497
|
-
element.removeAttribute(name);
|
|
1498
|
-
} catch (_) {}
|
|
1637
|
+
_stripAttributeNode(element, attribute, name);
|
|
1499
1638
|
}
|
|
1500
1639
|
};
|
|
1501
1640
|
/**
|
|
@@ -1524,7 +1663,7 @@ function createDOMPurify() {
|
|
|
1524
1663
|
const stack = [root];
|
|
1525
1664
|
while (stack.length > 0) {
|
|
1526
1665
|
const node = stack.pop();
|
|
1527
|
-
if ((
|
|
1666
|
+
if (_readNodeType(node) === NODE_TYPE.element) _stripDisallowedAttributes(node);
|
|
1528
1667
|
const childNodes = getChildNodes(node);
|
|
1529
1668
|
if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]);
|
|
1530
1669
|
}
|
|
@@ -1564,12 +1703,28 @@ function createDOMPurify() {
|
|
|
1564
1703
|
*
|
|
1565
1704
|
* @param root the in-place root to sweep
|
|
1566
1705
|
*/
|
|
1706
|
+
/**
|
|
1707
|
+
* Central policy for declarative-partial-updates patch-linkage attributes,
|
|
1708
|
+
* shared by the _neutralizePatchLinkage pre-pass and _isValidAttribute so
|
|
1709
|
+
* the two sites cannot drift: `patchsrc` always links, `for` links
|
|
1710
|
+
* everywhere except on <label>/<output>, and the whole policy is gated on
|
|
1711
|
+
* SAFE_FOR_XML (see the rationale block in _isValidAttribute).
|
|
1712
|
+
*
|
|
1713
|
+
* @param lcName the transformCaseFunc'd attribute name
|
|
1714
|
+
* @param lcTag the transformCaseFunc'd tag name of the carrying element
|
|
1715
|
+
* @return true if the attribute is patch linkage and must be dropped
|
|
1716
|
+
*/
|
|
1717
|
+
const _isPatchLinkageAttribute = function _isPatchLinkageAttribute(lcName, lcTag) {
|
|
1718
|
+
if (!SAFE_FOR_XML) return false;
|
|
1719
|
+
if (lcName === "patchsrc") return true;
|
|
1720
|
+
return lcName === "for" && lcTag !== "label" && lcTag !== "output";
|
|
1721
|
+
};
|
|
1567
1722
|
const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
|
|
1568
1723
|
if (!SAFE_FOR_XML) return;
|
|
1569
1724
|
const stack = [root];
|
|
1570
1725
|
while (stack.length > 0) {
|
|
1571
1726
|
const node = stack.pop();
|
|
1572
|
-
const nodeType =
|
|
1727
|
+
const nodeType = _readNodeType(node);
|
|
1573
1728
|
if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
|
|
1574
1729
|
try {
|
|
1575
1730
|
remove(node);
|
|
@@ -1578,10 +1733,10 @@ function createDOMPurify() {
|
|
|
1578
1733
|
}
|
|
1579
1734
|
if (nodeType === NODE_TYPE.element) {
|
|
1580
1735
|
const element = node;
|
|
1581
|
-
const lcTag = transformCaseFunc(
|
|
1736
|
+
const lcTag = transformCaseFunc(_readNodeName(node));
|
|
1582
1737
|
try {
|
|
1583
1738
|
if (element.hasAttribute && element.hasAttribute("patchsrc")) element.removeAttribute("patchsrc");
|
|
1584
|
-
if (element.hasAttribute && element.hasAttribute("for") &&
|
|
1739
|
+
if (element.hasAttribute && element.hasAttribute("for") && _isPatchLinkageAttribute("for", lcTag)) element.removeAttribute("for");
|
|
1585
1740
|
} catch (_) {}
|
|
1586
1741
|
}
|
|
1587
1742
|
const childNodes = getChildNodes(node);
|
|
@@ -1692,7 +1847,7 @@ function createDOMPurify() {
|
|
|
1692
1847
|
const realTagName = getNodeName ? getNodeName(element) : null;
|
|
1693
1848
|
if (typeof realTagName !== "string") return false;
|
|
1694
1849
|
if (transformCaseFunc(realTagName) !== "form") return false;
|
|
1695
|
-
return typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || element.attributes !== getAttributes(element) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function" || element.nodeType !== getNodeType(element) || element.childNodes !== getChildNodes(element);
|
|
1850
|
+
return typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || element.attributes !== getAttributes(element) || typeof element.removeAttribute !== "function" || typeof element.removeAttributeNode !== "function" || typeof element.getAttributeNode !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function" || element.nodeType !== getNodeType(element) || element.childNodes !== getChildNodes(element);
|
|
1696
1851
|
};
|
|
1697
1852
|
/**
|
|
1698
1853
|
* Checks whether the given value is a DocumentFragment from any realm.
|
|
@@ -1750,12 +1905,32 @@ function createDOMPurify() {
|
|
|
1750
1905
|
*/
|
|
1751
1906
|
const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
|
|
1752
1907
|
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) return true;
|
|
1753
|
-
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === "
|
|
1908
|
+
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && LITERAL_TEXT_ELEMENTS[tagName] && (_isNode(currentNode.firstElementChild) || typeof currentNode.textContent === "string" && regExpTest(LITERAL_TEXT_CLOSE[tagName], currentNode.textContent))) return true;
|
|
1754
1909
|
if (currentNode.nodeType === NODE_TYPE.processingInstruction) return true;
|
|
1755
1910
|
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) return true;
|
|
1756
1911
|
return false;
|
|
1757
1912
|
};
|
|
1758
1913
|
/**
|
|
1914
|
+
* Evaluate a CUSTOM_ELEMENT_HANDLING check (a RegExp or a predicate
|
|
1915
|
+
* function, per the validation in _parseConfig) against a name.
|
|
1916
|
+
* Additional arguments are forwarded to predicate functions - the
|
|
1917
|
+
* attributeNameCheck predicate receives the tag name as its second
|
|
1918
|
+
* argument. A null/absent check never matches.
|
|
1919
|
+
*
|
|
1920
|
+
* @param check the configured tagNameCheck / attributeNameCheck value
|
|
1921
|
+
* @param name the name to test
|
|
1922
|
+
* @param args extra arguments forwarded to a predicate function
|
|
1923
|
+
* @return true if the check matches the name
|
|
1924
|
+
*/
|
|
1925
|
+
const _matchesNameCheck = function _matchesNameCheck(check, name) {
|
|
1926
|
+
if (check instanceof RegExp) return regExpTest(check, name);
|
|
1927
|
+
if (check instanceof Function) {
|
|
1928
|
+
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) args[_key - 2] = arguments[_key];
|
|
1929
|
+
return Boolean(check(name, ...args));
|
|
1930
|
+
}
|
|
1931
|
+
return false;
|
|
1932
|
+
};
|
|
1933
|
+
/**
|
|
1759
1934
|
* Handle a node whose tag is forbidden or not allowlisted: keep
|
|
1760
1935
|
* allowed custom elements (false return exits _sanitizeElements
|
|
1761
1936
|
* early - the namespace and fallback-tag removal checks are
|
|
@@ -1773,10 +1948,7 @@ function createDOMPurify() {
|
|
|
1773
1948
|
* @return true if the node was removed, false if kept
|
|
1774
1949
|
*/
|
|
1775
1950
|
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
|
|
1776
|
-
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName))
|
|
1777
|
-
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1778
|
-
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1779
|
-
}
|
|
1951
|
+
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1780
1952
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1781
1953
|
const parentNode = getParentNode(currentNode);
|
|
1782
1954
|
const childNodes = getChildNodes(currentNode);
|
|
@@ -1810,6 +1982,42 @@ function createDOMPurify() {
|
|
|
1810
1982
|
return set === defaultSet || set === setConfigSet ? clone(set) : set;
|
|
1811
1983
|
};
|
|
1812
1984
|
/**
|
|
1985
|
+
* Shared guard for a node that a hook has detached from the walk tree,
|
|
1986
|
+
* used after each element-hook site in _sanitizeElements. Detaching is a
|
|
1987
|
+
* long-standing user pattern (issue #469; draw.io-style foreignObject
|
|
1988
|
+
* filtering). Per the cached, unclobberable parentNode getter the node is
|
|
1989
|
+
* genuinely out of the tree, so it can reach neither the serialized
|
|
1990
|
+
* output nor an IN_PLACE live tree; treat it as removed and stop
|
|
1991
|
+
* processing it. Without this guard, the unsafe-node / namespace checks
|
|
1992
|
+
* would call _forceRemove on a parentless node and hit the REPORT-3
|
|
1993
|
+
* fail-closed throw — which exists for nodes DOMPurify wants gone but
|
|
1994
|
+
* *cannot* detach (clobbered / parentless roots), the opposite of a node
|
|
1995
|
+
* that is already safely gone. The walk root is exempt: a detached
|
|
1996
|
+
* IN_PLACE root is legitimate input and must still be fully sanitized,
|
|
1997
|
+
* and a kill-decision on it must keep hitting the REPORT-3 throw.
|
|
1998
|
+
*
|
|
1999
|
+
* Nodes detached by hooks stay the hook's responsibility for placement:
|
|
2000
|
+
* they are not recorded in DOMPurify.removed, so the post-walk IN_PLACE
|
|
2001
|
+
* pass (which iterates DOMPurify.removed) does not reach them. But a
|
|
2002
|
+
* hook-detached subtree can still hold a queued resource-event handler -
|
|
2003
|
+
* e.g. an <img onload> that began loading when the caller built the live
|
|
2004
|
+
* tree - which fires in page scope after sanitize returns even though the
|
|
2005
|
+
* handler never reached the returned tree. That is the audit-5 F1 hazard,
|
|
2006
|
+
* and the documented node.remove() hook pattern walks straight into it.
|
|
2007
|
+
* So on the IN_PLACE path we neutralize the detached subtree inline,
|
|
2008
|
+
* stripping its non-allow-listed attributes before returning, exactly as
|
|
2009
|
+
* the post-walk pass does for _forceRemove'd subtrees.
|
|
2010
|
+
*
|
|
2011
|
+
* @param currentNode the node a hook may have detached
|
|
2012
|
+
* @param root the current walk root
|
|
2013
|
+
* @return true if the node is detached and now handled, false otherwise
|
|
2014
|
+
*/
|
|
2015
|
+
const _handleHookDetachedNode = function _handleHookDetachedNode(currentNode, root) {
|
|
2016
|
+
if (currentNode === root || getParentNode(currentNode) !== null) return false;
|
|
2017
|
+
if (IN_PLACE) _neutralizeSubtree(currentNode);
|
|
2018
|
+
return true;
|
|
2019
|
+
};
|
|
2020
|
+
/**
|
|
1813
2021
|
* _sanitizeElements
|
|
1814
2022
|
*
|
|
1815
2023
|
* @protect nodeName
|
|
@@ -1820,34 +2028,31 @@ function createDOMPurify() {
|
|
|
1820
2028
|
*/
|
|
1821
2029
|
const _sanitizeElements = function _sanitizeElements(currentNode, root) {
|
|
1822
2030
|
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
1823
|
-
if (currentNode
|
|
1824
|
-
if (IN_PLACE) _neutralizeSubtree(currentNode);
|
|
1825
|
-
return true;
|
|
1826
|
-
}
|
|
2031
|
+
if (_handleHookDetachedNode(currentNode, root)) return true;
|
|
1827
2032
|
if (_isClobbered(currentNode)) {
|
|
1828
2033
|
_forceRemove(currentNode);
|
|
1829
2034
|
return true;
|
|
1830
2035
|
}
|
|
1831
|
-
const tagName = transformCaseFunc(
|
|
2036
|
+
const tagName = transformCaseFunc(_readNodeName(currentNode));
|
|
1832
2037
|
ALLOWED_TAGS = _forkSharedAllowlist(hooks.uponSanitizeElement, ALLOWED_TAGS, DEFAULT_ALLOWED_TAGS, SET_CONFIG_ALLOWED_TAGS);
|
|
1833
2038
|
_executeHooks(hooks.uponSanitizeElement, currentNode, {
|
|
1834
2039
|
tagName,
|
|
1835
2040
|
allowedTags: ALLOWED_TAGS
|
|
1836
2041
|
});
|
|
1837
|
-
if (currentNode
|
|
1838
|
-
if (IN_PLACE) _neutralizeSubtree(currentNode);
|
|
1839
|
-
return true;
|
|
1840
|
-
}
|
|
2042
|
+
if (_handleHookDetachedNode(currentNode, root)) return true;
|
|
1841
2043
|
if (_isUnsafeNode(currentNode, tagName)) {
|
|
1842
2044
|
_forceRemove(currentNode);
|
|
1843
2045
|
return true;
|
|
1844
2046
|
}
|
|
1845
2047
|
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
|
|
1846
2048
|
const removed = _sanitizeDisallowedNode(currentNode, tagName, root);
|
|
1847
|
-
if (removed === false)
|
|
2049
|
+
if (removed === false) {
|
|
2050
|
+
_executeHooks(hooks.afterSanitizeElements, currentNode, null);
|
|
2051
|
+
if (_handleHookDetachedNode(currentNode, root)) return true;
|
|
2052
|
+
}
|
|
1848
2053
|
return removed;
|
|
1849
2054
|
}
|
|
1850
|
-
if ((
|
|
2055
|
+
if (_readNodeType(currentNode) === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
|
|
1851
2056
|
_forceRemove(currentNode);
|
|
1852
2057
|
return true;
|
|
1853
2058
|
}
|
|
@@ -1863,7 +2068,7 @@ function createDOMPurify() {
|
|
|
1863
2068
|
}
|
|
1864
2069
|
}
|
|
1865
2070
|
_executeHooks(hooks.afterSanitizeElements, currentNode, null);
|
|
1866
|
-
return
|
|
2071
|
+
return _handleHookDetachedNode(currentNode, root);
|
|
1867
2072
|
};
|
|
1868
2073
|
/**
|
|
1869
2074
|
* _isValidAttribute
|
|
@@ -1875,20 +2080,17 @@ function createDOMPurify() {
|
|
|
1875
2080
|
*/
|
|
1876
2081
|
const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1877
2082
|
if (FORBID_ATTR[lcName]) return false;
|
|
1878
|
-
if (
|
|
1879
|
-
if (SAFE_FOR_XML && lcName === "for" && lcTag !== "label" && lcTag !== "output") return false;
|
|
2083
|
+
if (_isPatchLinkageAttribute(lcName, lcTag)) return false;
|
|
1880
2084
|
if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in document || value in formElement)) return false;
|
|
1881
2085
|
const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
|
|
1882
|
-
if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName));
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
else if (value) return false;
|
|
1891
|
-
return true;
|
|
2086
|
+
if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) return true;
|
|
2087
|
+
if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) return true;
|
|
2088
|
+
if (!nameIsPermitted) return _isBasicCustomElement(lcTag) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName, lcTag) || lcName === "is" && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value);
|
|
2089
|
+
if (URI_SAFE_ATTRIBUTES[lcName]) return true;
|
|
2090
|
+
if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ""))) return true;
|
|
2091
|
+
if ((lcName === "src" || lcName === "xlink:href" || lcName === "href") && lcTag !== "script" && stringIndexOf(value, "data:") === 0 && DATA_URI_TAGS[lcTag]) return true;
|
|
2092
|
+
if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ""))) return true;
|
|
2093
|
+
return !value;
|
|
1892
2094
|
};
|
|
1893
2095
|
const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, [
|
|
1894
2096
|
"annotation-xml",
|
|
@@ -1933,24 +2135,38 @@ function createDOMPurify() {
|
|
|
1933
2135
|
/**
|
|
1934
2136
|
* Write a modified attribute value back onto the element. On
|
|
1935
2137
|
* success, re-probe for clobbering introduced by the new value and
|
|
1936
|
-
* remove the element when found; otherwise
|
|
1937
|
-
*
|
|
1938
|
-
*
|
|
2138
|
+
* remove the element when found; otherwise, when this writeback is the
|
|
2139
|
+
* recreate half of the SANITIZE_NAMED_PROPS remove-and-recreate, pop the
|
|
2140
|
+
* removal entry that path recorded so it does not show as removed. On
|
|
1939
2141
|
* failure, remove the attribute instead.
|
|
1940
2142
|
*
|
|
2143
|
+
* Returns true only on a clean write (the value was set and the new value
|
|
2144
|
+
* introduced no clobbering). The caller uses that, together with its own
|
|
2145
|
+
* knowledge of whether this attribute pushed a DOMPurify.removed record, to
|
|
2146
|
+
* decide whether to pop that record. The pop must happen ONLY for the
|
|
2147
|
+
* named-prop remove-and-recreate; popping on any other value change (trim,
|
|
2148
|
+
* template scrubbing, Trusted Types) would consume an unrelated _forceRemove
|
|
2149
|
+
* subtree-cleanup record and let that detached subtree keep a live event
|
|
2150
|
+
* handler through the IN_PLACE neutralization pass (SO-001).
|
|
2151
|
+
*
|
|
1941
2152
|
* @param currentNode the element carrying the attribute
|
|
1942
2153
|
* @param name the attribute name as present on the element
|
|
1943
2154
|
* @param namespaceURI the attribute's namespace, if any
|
|
1944
2155
|
* @param value the new attribute value
|
|
2156
|
+
* @return true if the value was written without introducing clobbering
|
|
1945
2157
|
*/
|
|
1946
2158
|
const _setAttributeValue = function _setAttributeValue(currentNode, name, namespaceURI, value) {
|
|
1947
2159
|
try {
|
|
1948
2160
|
if (namespaceURI) currentNode.setAttributeNS(namespaceURI, name, value);
|
|
1949
2161
|
else currentNode.setAttribute(name, value);
|
|
1950
|
-
if (_isClobbered(currentNode))
|
|
1951
|
-
|
|
2162
|
+
if (_isClobbered(currentNode)) {
|
|
2163
|
+
_forceRemove(currentNode);
|
|
2164
|
+
return false;
|
|
2165
|
+
}
|
|
2166
|
+
return true;
|
|
1952
2167
|
} catch (_) {
|
|
1953
2168
|
_removeAttribute(name, currentNode);
|
|
2169
|
+
return false;
|
|
1954
2170
|
}
|
|
1955
2171
|
};
|
|
1956
2172
|
/**
|
|
@@ -1962,9 +2178,11 @@ function createDOMPurify() {
|
|
|
1962
2178
|
* @protect setAttribute
|
|
1963
2179
|
*
|
|
1964
2180
|
* @param currentNode to sanitize
|
|
2181
|
+
* @param root the current walk root
|
|
1965
2182
|
*/
|
|
1966
|
-
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
2183
|
+
const _sanitizeAttributes = function _sanitizeAttributes(currentNode, root) {
|
|
1967
2184
|
_executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
|
|
2185
|
+
if (_handleHookDetachedNode(currentNode, root)) return;
|
|
1968
2186
|
const attributes = currentNode.attributes;
|
|
1969
2187
|
if (!attributes || _isClobbered(currentNode)) return;
|
|
1970
2188
|
ALLOWED_ATTR = _forkSharedAllowlist(hooks.uponSanitizeAttribute, ALLOWED_ATTR, DEFAULT_ALLOWED_ATTR, SET_CONFIG_ALLOWED_ATTR);
|
|
@@ -1983,6 +2201,7 @@ function createDOMPurify() {
|
|
|
1983
2201
|
const lcName = transformCaseFunc(name);
|
|
1984
2202
|
const initValue = attrValue;
|
|
1985
2203
|
let value = name === "value" ? initValue : stringTrim(initValue);
|
|
2204
|
+
let recreatedNamedProp = false;
|
|
1986
2205
|
hookEvent.attrName = lcName;
|
|
1987
2206
|
hookEvent.attrValue = value;
|
|
1988
2207
|
hookEvent.keepAttr = true;
|
|
@@ -1990,35 +2209,39 @@ function createDOMPurify() {
|
|
|
1990
2209
|
_executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
|
|
1991
2210
|
value = hookEvent.attrValue;
|
|
1992
2211
|
if (SANITIZE_NAMED_PROPS && (lcName === "id" || lcName === "name") && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
|
|
1993
|
-
_removeAttribute(name, currentNode);
|
|
2212
|
+
_removeAttribute(name, currentNode, attr);
|
|
1994
2213
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
2214
|
+
recreatedNamedProp = true;
|
|
1995
2215
|
}
|
|
1996
2216
|
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
|
|
1997
|
-
_removeAttribute(name, currentNode);
|
|
2217
|
+
_removeAttribute(name, currentNode, attr);
|
|
1998
2218
|
continue;
|
|
1999
2219
|
}
|
|
2000
2220
|
if (lcName === "attributename" && stringMatch(value, "href")) {
|
|
2001
|
-
_removeAttribute(name, currentNode);
|
|
2221
|
+
_removeAttribute(name, currentNode, attr);
|
|
2002
2222
|
continue;
|
|
2003
2223
|
}
|
|
2004
2224
|
if (hookEvent.forceKeepAttr) continue;
|
|
2005
2225
|
if (!hookEvent.keepAttr) {
|
|
2006
|
-
_removeAttribute(name, currentNode);
|
|
2226
|
+
_removeAttribute(name, currentNode, attr);
|
|
2007
2227
|
continue;
|
|
2008
2228
|
}
|
|
2009
2229
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
|
|
2010
|
-
_removeAttribute(name, currentNode);
|
|
2230
|
+
_removeAttribute(name, currentNode, attr);
|
|
2011
2231
|
continue;
|
|
2012
2232
|
}
|
|
2013
2233
|
if (SAFE_FOR_TEMPLATES) value = _stripTemplateExpressions(value);
|
|
2014
2234
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
2015
|
-
_removeAttribute(name, currentNode);
|
|
2235
|
+
_removeAttribute(name, currentNode, attr);
|
|
2016
2236
|
continue;
|
|
2017
2237
|
}
|
|
2018
2238
|
value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
|
|
2019
|
-
if (value !== initValue)
|
|
2239
|
+
if (value !== initValue) {
|
|
2240
|
+
if (_setAttributeValue(currentNode, name, namespaceURI, value) && recreatedNamedProp) arrayPop(DOMPurify.removed);
|
|
2241
|
+
}
|
|
2020
2242
|
}
|
|
2021
2243
|
_executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
|
|
2244
|
+
_handleHookDetachedNode(currentNode, root);
|
|
2022
2245
|
};
|
|
2023
2246
|
/**
|
|
2024
2247
|
* _sanitizeShadowDOM
|
|
@@ -2032,9 +2255,9 @@ function createDOMPurify() {
|
|
|
2032
2255
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
2033
2256
|
_executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
|
|
2034
2257
|
_sanitizeElements(shadowNode, fragment);
|
|
2035
|
-
_sanitizeAttributes(shadowNode);
|
|
2258
|
+
_sanitizeAttributes(shadowNode, fragment);
|
|
2036
2259
|
if (_isDocumentFragment(shadowNode.content)) _sanitizeShadowDOM2(shadowNode.content);
|
|
2037
|
-
if ((
|
|
2260
|
+
if (_readNodeType(shadowNode) === NODE_TYPE.element) {
|
|
2038
2261
|
const innerSr = getShadowRoot(shadowNode);
|
|
2039
2262
|
if (_isDocumentFragment(innerSr)) {
|
|
2040
2263
|
_sanitizeAttachedShadowRoots(innerSr);
|
|
@@ -2075,7 +2298,7 @@ function createDOMPurify() {
|
|
|
2075
2298
|
continue;
|
|
2076
2299
|
}
|
|
2077
2300
|
const node = item.node;
|
|
2078
|
-
const isElement = (
|
|
2301
|
+
const isElement = _readNodeType(node) === NODE_TYPE.element;
|
|
2079
2302
|
const childNodes = getChildNodes(node);
|
|
2080
2303
|
if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push({
|
|
2081
2304
|
node: childNodes[i],
|
|
@@ -2126,7 +2349,7 @@ function createDOMPurify() {
|
|
|
2126
2349
|
const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
|
|
2127
2350
|
if (inPlace) {
|
|
2128
2351
|
_neutralizePatchLinkage(dirty);
|
|
2129
|
-
const nn =
|
|
2352
|
+
const nn = _readNodeName(dirty);
|
|
2130
2353
|
if (typeof nn === "string") {
|
|
2131
2354
|
const tagName = transformCaseFunc(nn);
|
|
2132
2355
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
@@ -2150,7 +2373,7 @@ function createDOMPurify() {
|
|
|
2150
2373
|
if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === "BODY") body = importedNode;
|
|
2151
2374
|
else if (importedNode.nodeName === "HTML") body = importedNode;
|
|
2152
2375
|
else body.appendChild(importedNode);
|
|
2153
|
-
_sanitizeAttachedShadowRoots(
|
|
2376
|
+
_sanitizeAttachedShadowRoots(body);
|
|
2154
2377
|
} else {
|
|
2155
2378
|
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf("<") === -1) return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
|
|
2156
2379
|
body = _initDocument(dirty);
|
|
@@ -2162,7 +2385,7 @@ function createDOMPurify() {
|
|
|
2162
2385
|
const nodeIterator = _createNodeIterator(walkRoot);
|
|
2163
2386
|
while (currentNode = nodeIterator.nextNode()) {
|
|
2164
2387
|
_sanitizeElements(currentNode, walkRoot);
|
|
2165
|
-
_sanitizeAttributes(currentNode);
|
|
2388
|
+
_sanitizeAttributes(currentNode, walkRoot);
|
|
2166
2389
|
if (_isDocumentFragment(currentNode.content)) _sanitizeShadowDOM2(currentNode.content);
|
|
2167
2390
|
}
|
|
2168
2391
|
} catch (error) {
|
|
@@ -2175,9 +2398,14 @@ function createDOMPurify() {
|
|
|
2175
2398
|
throw error;
|
|
2176
2399
|
}
|
|
2177
2400
|
if (inPlace) {
|
|
2401
|
+
let rootWasRemoved = false;
|
|
2178
2402
|
arrayForEach(DOMPurify.removed, (entry) => {
|
|
2179
|
-
if (entry.element)
|
|
2403
|
+
if (entry.element) {
|
|
2404
|
+
if (entry.element === dirty) rootWasRemoved = true;
|
|
2405
|
+
_neutralizeSubtree(entry.element);
|
|
2406
|
+
}
|
|
2180
2407
|
});
|
|
2408
|
+
if (rootWasRemoved) throw typeErrorCreate("a node selected for removal could not be safely returned; refusing to sanitize in place");
|
|
2181
2409
|
if (SAFE_FOR_TEMPLATES) _scrubTemplateExpressions2(dirty);
|
|
2182
2410
|
return dirty;
|
|
2183
2411
|
}
|