@mulmoclaude/core 4.4.0 → 4.4.2
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/helps/custom-view.md +69 -1
- package/dist/plugin-vue/index.cjs +201 -66
- package/dist/plugin-vue/index.cjs.map +1 -1
- package/dist/plugin-vue/index.js +201 -66
- package/dist/plugin-vue/index.js.map +1 -1
- package/dist/scheduler/index.cjs +116 -63
- package/dist/scheduler/index.cjs.map +1 -1
- package/dist/scheduler/index.js +117 -64
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/schedule-window.d.ts +35 -0
- package/dist/scheduler/task-manager.d.ts +2 -8
- package/package.json +6 -6
package/dist/plugin-vue/index.js
CHANGED
|
@@ -87,7 +87,7 @@ function useClipboardCopy(resetMs = DEFAULT_RESET_MS) {
|
|
|
87
87
|
}
|
|
88
88
|
//#endregion
|
|
89
89
|
//#region ../../node_modules/dompurify/dist/purify.es.mjs
|
|
90
|
-
/*! @license DOMPurify 3.4.
|
|
90
|
+
/*! @license DOMPurify 3.4.14 | (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.14/LICENSE */
|
|
91
91
|
function _arrayLikeToArray(r, a) {
|
|
92
92
|
(null == a || a > r.length) && (a = r.length);
|
|
93
93
|
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
|
|
@@ -816,6 +816,7 @@ var svg = freeze([
|
|
|
816
816
|
"patterncontentunits",
|
|
817
817
|
"patterntransform",
|
|
818
818
|
"patternunits",
|
|
819
|
+
"pointer-events",
|
|
819
820
|
"points",
|
|
820
821
|
"preservealpha",
|
|
821
822
|
"preserveaspectratio",
|
|
@@ -870,6 +871,7 @@ var svg = freeze([
|
|
|
870
871
|
"u2",
|
|
871
872
|
"unicode",
|
|
872
873
|
"values",
|
|
874
|
+
"vector-effect",
|
|
873
875
|
"viewbox",
|
|
874
876
|
"visibility",
|
|
875
877
|
"version",
|
|
@@ -983,6 +985,24 @@ var NODE_TYPE = {
|
|
|
983
985
|
documentFragment: 11,
|
|
984
986
|
notation: 12
|
|
985
987
|
};
|
|
988
|
+
var LITERAL_TEXT_ELEMENT_NAMES = [
|
|
989
|
+
"style",
|
|
990
|
+
"script",
|
|
991
|
+
"xmp",
|
|
992
|
+
"iframe",
|
|
993
|
+
"noembed",
|
|
994
|
+
"noframes",
|
|
995
|
+
"plaintext",
|
|
996
|
+
"noscript"
|
|
997
|
+
];
|
|
998
|
+
var LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, LITERAL_TEXT_ELEMENT_NAMES));
|
|
999
|
+
var LITERAL_TEXT_CLOSE = function() {
|
|
1000
|
+
const map = {};
|
|
1001
|
+
arrayForEach(LITERAL_TEXT_ELEMENT_NAMES, (name) => {
|
|
1002
|
+
map[name] = seal(new RegExp("</" + name + "(?=[\\t\\n\\f\\r />])", "i"));
|
|
1003
|
+
});
|
|
1004
|
+
return freeze(map);
|
|
1005
|
+
}();
|
|
986
1006
|
var getGlobal = function getGlobal() {
|
|
987
1007
|
return typeof window === "undefined" ? null : window;
|
|
988
1008
|
};
|
|
@@ -1042,10 +1062,25 @@ var _createHooksMap = function _createHooksMap() {
|
|
|
1042
1062
|
var _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
|
|
1043
1063
|
return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
|
|
1044
1064
|
};
|
|
1065
|
+
/**
|
|
1066
|
+
* Resolve an object-valued configuration option: a prototype-free clone
|
|
1067
|
+
* of cfg[key] when it is an own, truthy object property, else a fresh
|
|
1068
|
+
* fallback built by makeFallback (fresh on every parse, so a previous
|
|
1069
|
+
* parse can never leak state into the next one).
|
|
1070
|
+
*
|
|
1071
|
+
* @param cfg the cloned, prototype-free configuration object
|
|
1072
|
+
* @param key the configuration property to read
|
|
1073
|
+
* @param makeFallback builds the fallback value when the option is absent
|
|
1074
|
+
* @returns the resolved object
|
|
1075
|
+
*/
|
|
1076
|
+
var _resolveObjectOption = function _resolveObjectOption(cfg, key, makeFallback) {
|
|
1077
|
+
const value = objectHasOwnProperty(cfg, key) ? cfg[key] : void 0;
|
|
1078
|
+
return value && typeof value === "object" ? clone(value) : makeFallback();
|
|
1079
|
+
};
|
|
1045
1080
|
function createDOMPurify() {
|
|
1046
1081
|
let window = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
|
|
1047
1082
|
const DOMPurify = (root) => createDOMPurify(root);
|
|
1048
|
-
DOMPurify.version = "3.4.
|
|
1083
|
+
DOMPurify.version = "3.4.14";
|
|
1049
1084
|
DOMPurify.removed = [];
|
|
1050
1085
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
1051
1086
|
DOMPurify.isSupported = false;
|
|
@@ -1070,6 +1105,12 @@ function createDOMPurify() {
|
|
|
1070
1105
|
const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeType") : null;
|
|
1071
1106
|
const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeName") : null;
|
|
1072
1107
|
const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, "ownerDocument") : null;
|
|
1108
|
+
const _readNodeType = function _readNodeType(node) {
|
|
1109
|
+
return getNodeType ? getNodeType(node) : node.nodeType;
|
|
1110
|
+
};
|
|
1111
|
+
const _readNodeName = function _readNodeName(node) {
|
|
1112
|
+
return getNodeName ? getNodeName(node) : node.nodeName;
|
|
1113
|
+
};
|
|
1073
1114
|
if (typeof HTMLTemplateElement === "function") {
|
|
1074
1115
|
const template = document.createElement("template");
|
|
1075
1116
|
if (template.content && template.content.ownerDocument) document = template.content.ownerDocument;
|
|
@@ -1327,9 +1368,9 @@ function createDOMPurify() {
|
|
|
1327
1368
|
IN_PLACE = cfg.IN_PLACE || false;
|
|
1328
1369
|
IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI;
|
|
1329
1370
|
NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE;
|
|
1330
|
-
MATHML_TEXT_INTEGRATION_POINTS =
|
|
1331
|
-
HTML_INTEGRATION_POINTS =
|
|
1332
|
-
const customElementHandling =
|
|
1371
|
+
MATHML_TEXT_INTEGRATION_POINTS = _resolveObjectOption(cfg, "MATHML_TEXT_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS));
|
|
1372
|
+
HTML_INTEGRATION_POINTS = _resolveObjectOption(cfg, "HTML_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS));
|
|
1373
|
+
const customElementHandling = _resolveObjectOption(cfg, "CUSTOM_ELEMENT_HANDLING", () => create(null));
|
|
1333
1374
|
CUSTOM_ELEMENT_HANDLING = create(null);
|
|
1334
1375
|
if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck;
|
|
1335
1376
|
if (objectHasOwnProperty(customElementHandling, "attributeNameCheck") && isRegexOrFunction(customElementHandling.attributeNameCheck)) CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck;
|
|
@@ -1376,11 +1417,6 @@ function createDOMPurify() {
|
|
|
1376
1417
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
1377
1418
|
}
|
|
1378
1419
|
}
|
|
1379
|
-
if (objectHasOwnProperty(cfg, "ADD_URI_SAFE_ATTR") && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
1380
|
-
if (objectHasOwnProperty(cfg, "FORBID_CONTENTS") && arrayIsArray(cfg.FORBID_CONTENTS)) {
|
|
1381
|
-
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
1382
|
-
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
1383
|
-
}
|
|
1384
1420
|
if (objectHasOwnProperty(cfg, "ADD_FORBID_CONTENTS") && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
|
|
1385
1421
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
1386
1422
|
addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
|
|
@@ -1497,6 +1533,32 @@ function createDOMPurify() {
|
|
|
1497
1533
|
}
|
|
1498
1534
|
};
|
|
1499
1535
|
/**
|
|
1536
|
+
* _stripAttributeNode
|
|
1537
|
+
*
|
|
1538
|
+
* Remove a single Attr node case/namespace-exactly on an attribute-teardown
|
|
1539
|
+
* path. Name-based removeAttribute() ASCII-lowercases its lookup key for an
|
|
1540
|
+
* HTML element in an HTML document and so silently misses a case-preserved
|
|
1541
|
+
* handler (e.g. `ONERROR` off an XML/XHTML import) - the same defect
|
|
1542
|
+
* _removeAttribute() was fixed for, which a name-based call would reintroduce
|
|
1543
|
+
* on these IN_PLACE teardown paths. Unlike _removeAttribute this does not
|
|
1544
|
+
* record into DOMPurify.removed: the neutralize passes intentionally do not
|
|
1545
|
+
* book-keep. A clobbered/detached node falls back to best-effort name-based
|
|
1546
|
+
* removal.
|
|
1547
|
+
*
|
|
1548
|
+
* @param element the element to strip the attribute from
|
|
1549
|
+
* @param attribute the Attr node to remove
|
|
1550
|
+
* @param name the attribute's name, for the fallback path
|
|
1551
|
+
*/
|
|
1552
|
+
const _stripAttributeNode = function _stripAttributeNode(element, attribute, name) {
|
|
1553
|
+
try {
|
|
1554
|
+
element.removeAttributeNode(attribute);
|
|
1555
|
+
} catch (_) {
|
|
1556
|
+
try {
|
|
1557
|
+
element.removeAttribute(name);
|
|
1558
|
+
} catch (_) {}
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
/**
|
|
1500
1562
|
* _neutralizeRoot
|
|
1501
1563
|
*
|
|
1502
1564
|
* Fail-closed teardown of an in-place root after the sanitize walk aborts
|
|
@@ -1530,30 +1592,45 @@ function createDOMPurify() {
|
|
|
1530
1592
|
if (attributes) for (let i = attributes.length - 1; i >= 0; --i) {
|
|
1531
1593
|
const attribute = attributes[i];
|
|
1532
1594
|
const name = attribute && attribute.name;
|
|
1533
|
-
if (typeof name === "string")
|
|
1534
|
-
root.removeAttribute(name);
|
|
1535
|
-
} catch (_) {}
|
|
1595
|
+
if (typeof name === "string") _stripAttributeNode(root, attribute, name);
|
|
1536
1596
|
}
|
|
1537
1597
|
};
|
|
1538
1598
|
/**
|
|
1539
1599
|
* _removeAttribute
|
|
1540
1600
|
*
|
|
1601
|
+
* Name-based getAttributeNode()/removeAttribute() ASCII-lowercase their
|
|
1602
|
+
* lookup key for HTML elements in an HTML document, so they silently miss an
|
|
1603
|
+
* attribute whose stored qualified name still contains uppercase ASCII
|
|
1604
|
+
* letters. That happens when the node came from a case-preserving source
|
|
1605
|
+
* (an XML/XHTML document imported via importNode(), or createAttributeNS()),
|
|
1606
|
+
* where e.g. `ONERROR` survives the walk: the policy check lowercases to
|
|
1607
|
+
* `onerror` and rejects it, but `removeAttribute('ONERROR')` looks up
|
|
1608
|
+
* `onerror` and finds nothing. Remove the exact Attr node instead, which is
|
|
1609
|
+
* case- and namespace-exact, and fall back to name-based removal only when
|
|
1610
|
+
* the caller could not supply the node.
|
|
1611
|
+
*
|
|
1541
1612
|
* @param name an Attribute name
|
|
1542
1613
|
* @param element a DOM node
|
|
1614
|
+
* @param attr the exact Attr node to remove, when the caller has it
|
|
1543
1615
|
*/
|
|
1544
|
-
const _removeAttribute = function _removeAttribute(name, element) {
|
|
1616
|
+
const _removeAttribute = function _removeAttribute(name, element, attr) {
|
|
1617
|
+
if (!attr) try {
|
|
1618
|
+
attr = element.getAttributeNode(name);
|
|
1619
|
+
} catch (_) {
|
|
1620
|
+
attr = null;
|
|
1621
|
+
}
|
|
1622
|
+
arrayPush(DOMPurify.removed, {
|
|
1623
|
+
attribute: attr || null,
|
|
1624
|
+
from: element
|
|
1625
|
+
});
|
|
1545
1626
|
try {
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
from: element
|
|
1549
|
-
});
|
|
1627
|
+
if (attr) element.removeAttributeNode(attr);
|
|
1628
|
+
else element.removeAttribute(name);
|
|
1550
1629
|
} catch (_) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
});
|
|
1630
|
+
try {
|
|
1631
|
+
element.removeAttribute(name);
|
|
1632
|
+
} catch (_) {}
|
|
1555
1633
|
}
|
|
1556
|
-
element.removeAttribute(name);
|
|
1557
1634
|
if (name === "is") if (RETURN_DOM || RETURN_DOM_FRAGMENT) try {
|
|
1558
1635
|
_forceRemove(element);
|
|
1559
1636
|
} catch (_) {}
|
|
@@ -1578,9 +1655,7 @@ function createDOMPurify() {
|
|
|
1578
1655
|
const attribute = attributes[i];
|
|
1579
1656
|
const name = attribute && attribute.name;
|
|
1580
1657
|
if (typeof name !== "string" || ALLOWED_ATTR[transformCaseFunc(name)]) continue;
|
|
1581
|
-
|
|
1582
|
-
element.removeAttribute(name);
|
|
1583
|
-
} catch (_) {}
|
|
1658
|
+
_stripAttributeNode(element, attribute, name);
|
|
1584
1659
|
}
|
|
1585
1660
|
};
|
|
1586
1661
|
/**
|
|
@@ -1609,7 +1684,7 @@ function createDOMPurify() {
|
|
|
1609
1684
|
const stack = [root];
|
|
1610
1685
|
while (stack.length > 0) {
|
|
1611
1686
|
const node = stack.pop();
|
|
1612
|
-
if ((
|
|
1687
|
+
if (_readNodeType(node) === NODE_TYPE.element) _stripDisallowedAttributes(node);
|
|
1613
1688
|
const childNodes = getChildNodes(node);
|
|
1614
1689
|
if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]);
|
|
1615
1690
|
}
|
|
@@ -1649,12 +1724,28 @@ function createDOMPurify() {
|
|
|
1649
1724
|
*
|
|
1650
1725
|
* @param root the in-place root to sweep
|
|
1651
1726
|
*/
|
|
1727
|
+
/**
|
|
1728
|
+
* Central policy for declarative-partial-updates patch-linkage attributes,
|
|
1729
|
+
* shared by the _neutralizePatchLinkage pre-pass and _isValidAttribute so
|
|
1730
|
+
* the two sites cannot drift: `patchsrc` always links, `for` links
|
|
1731
|
+
* everywhere except on <label>/<output>, and the whole policy is gated on
|
|
1732
|
+
* SAFE_FOR_XML (see the rationale block in _isValidAttribute).
|
|
1733
|
+
*
|
|
1734
|
+
* @param lcName the transformCaseFunc'd attribute name
|
|
1735
|
+
* @param lcTag the transformCaseFunc'd tag name of the carrying element
|
|
1736
|
+
* @return true if the attribute is patch linkage and must be dropped
|
|
1737
|
+
*/
|
|
1738
|
+
const _isPatchLinkageAttribute = function _isPatchLinkageAttribute(lcName, lcTag) {
|
|
1739
|
+
if (!SAFE_FOR_XML) return false;
|
|
1740
|
+
if (lcName === "patchsrc") return true;
|
|
1741
|
+
return lcName === "for" && lcTag !== "label" && lcTag !== "output";
|
|
1742
|
+
};
|
|
1652
1743
|
const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
|
|
1653
1744
|
if (!SAFE_FOR_XML) return;
|
|
1654
1745
|
const stack = [root];
|
|
1655
1746
|
while (stack.length > 0) {
|
|
1656
1747
|
const node = stack.pop();
|
|
1657
|
-
const nodeType =
|
|
1748
|
+
const nodeType = _readNodeType(node);
|
|
1658
1749
|
if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
|
|
1659
1750
|
try {
|
|
1660
1751
|
remove(node);
|
|
@@ -1663,10 +1754,10 @@ function createDOMPurify() {
|
|
|
1663
1754
|
}
|
|
1664
1755
|
if (nodeType === NODE_TYPE.element) {
|
|
1665
1756
|
const element = node;
|
|
1666
|
-
const lcTag = transformCaseFunc(
|
|
1757
|
+
const lcTag = transformCaseFunc(_readNodeName(node));
|
|
1667
1758
|
try {
|
|
1668
1759
|
if (element.hasAttribute && element.hasAttribute("patchsrc")) element.removeAttribute("patchsrc");
|
|
1669
|
-
if (element.hasAttribute && element.hasAttribute("for") &&
|
|
1760
|
+
if (element.hasAttribute && element.hasAttribute("for") && _isPatchLinkageAttribute("for", lcTag)) element.removeAttribute("for");
|
|
1670
1761
|
} catch (_) {}
|
|
1671
1762
|
}
|
|
1672
1763
|
const childNodes = getChildNodes(node);
|
|
@@ -1835,12 +1926,32 @@ function createDOMPurify() {
|
|
|
1835
1926
|
*/
|
|
1836
1927
|
const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
|
|
1837
1928
|
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) return true;
|
|
1838
|
-
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === "
|
|
1929
|
+
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;
|
|
1839
1930
|
if (currentNode.nodeType === NODE_TYPE.processingInstruction) return true;
|
|
1840
1931
|
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) return true;
|
|
1841
1932
|
return false;
|
|
1842
1933
|
};
|
|
1843
1934
|
/**
|
|
1935
|
+
* Evaluate a CUSTOM_ELEMENT_HANDLING check (a RegExp or a predicate
|
|
1936
|
+
* function, per the validation in _parseConfig) against a name.
|
|
1937
|
+
* Additional arguments are forwarded to predicate functions - the
|
|
1938
|
+
* attributeNameCheck predicate receives the tag name as its second
|
|
1939
|
+
* argument. A null/absent check never matches.
|
|
1940
|
+
*
|
|
1941
|
+
* @param check the configured tagNameCheck / attributeNameCheck value
|
|
1942
|
+
* @param name the name to test
|
|
1943
|
+
* @param args extra arguments forwarded to a predicate function
|
|
1944
|
+
* @return true if the check matches the name
|
|
1945
|
+
*/
|
|
1946
|
+
const _matchesNameCheck = function _matchesNameCheck(check, name) {
|
|
1947
|
+
if (check instanceof RegExp) return regExpTest(check, name);
|
|
1948
|
+
if (check instanceof Function) {
|
|
1949
|
+
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) args[_key - 2] = arguments[_key];
|
|
1950
|
+
return Boolean(check(name, ...args));
|
|
1951
|
+
}
|
|
1952
|
+
return false;
|
|
1953
|
+
};
|
|
1954
|
+
/**
|
|
1844
1955
|
* Handle a node whose tag is forbidden or not allowlisted: keep
|
|
1845
1956
|
* allowed custom elements (false return exits _sanitizeElements
|
|
1846
1957
|
* early - the namespace and fallback-tag removal checks are
|
|
@@ -1858,10 +1969,7 @@ function createDOMPurify() {
|
|
|
1858
1969
|
* @return true if the node was removed, false if kept
|
|
1859
1970
|
*/
|
|
1860
1971
|
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
|
|
1861
|
-
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName))
|
|
1862
|
-
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1863
|
-
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1864
|
-
}
|
|
1972
|
+
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1865
1973
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1866
1974
|
const parentNode = getParentNode(currentNode);
|
|
1867
1975
|
const childNodes = getChildNodes(currentNode);
|
|
@@ -1895,6 +2003,42 @@ function createDOMPurify() {
|
|
|
1895
2003
|
return set === defaultSet || set === setConfigSet ? clone(set) : set;
|
|
1896
2004
|
};
|
|
1897
2005
|
/**
|
|
2006
|
+
* Shared guard for a node that a hook has detached from the walk tree,
|
|
2007
|
+
* used after each element-hook site in _sanitizeElements. Detaching is a
|
|
2008
|
+
* long-standing user pattern (issue #469; draw.io-style foreignObject
|
|
2009
|
+
* filtering). Per the cached, unclobberable parentNode getter the node is
|
|
2010
|
+
* genuinely out of the tree, so it can reach neither the serialized
|
|
2011
|
+
* output nor an IN_PLACE live tree; treat it as removed and stop
|
|
2012
|
+
* processing it. Without this guard, the unsafe-node / namespace checks
|
|
2013
|
+
* would call _forceRemove on a parentless node and hit the REPORT-3
|
|
2014
|
+
* fail-closed throw — which exists for nodes DOMPurify wants gone but
|
|
2015
|
+
* *cannot* detach (clobbered / parentless roots), the opposite of a node
|
|
2016
|
+
* that is already safely gone. The walk root is exempt: a detached
|
|
2017
|
+
* IN_PLACE root is legitimate input and must still be fully sanitized,
|
|
2018
|
+
* and a kill-decision on it must keep hitting the REPORT-3 throw.
|
|
2019
|
+
*
|
|
2020
|
+
* Nodes detached by hooks stay the hook's responsibility for placement:
|
|
2021
|
+
* they are not recorded in DOMPurify.removed, so the post-walk IN_PLACE
|
|
2022
|
+
* pass (which iterates DOMPurify.removed) does not reach them. But a
|
|
2023
|
+
* hook-detached subtree can still hold a queued resource-event handler -
|
|
2024
|
+
* e.g. an <img onload> that began loading when the caller built the live
|
|
2025
|
+
* tree - which fires in page scope after sanitize returns even though the
|
|
2026
|
+
* handler never reached the returned tree. That is the audit-5 F1 hazard,
|
|
2027
|
+
* and the documented node.remove() hook pattern walks straight into it.
|
|
2028
|
+
* So on the IN_PLACE path we neutralize the detached subtree inline,
|
|
2029
|
+
* stripping its non-allow-listed attributes before returning, exactly as
|
|
2030
|
+
* the post-walk pass does for _forceRemove'd subtrees.
|
|
2031
|
+
*
|
|
2032
|
+
* @param currentNode the node a hook may have detached
|
|
2033
|
+
* @param root the current walk root
|
|
2034
|
+
* @return true if the node is detached and now handled, false otherwise
|
|
2035
|
+
*/
|
|
2036
|
+
const _handleHookDetachedNode = function _handleHookDetachedNode(currentNode, root) {
|
|
2037
|
+
if (currentNode === root || getParentNode(currentNode) !== null) return false;
|
|
2038
|
+
if (IN_PLACE) _neutralizeSubtree(currentNode);
|
|
2039
|
+
return true;
|
|
2040
|
+
};
|
|
2041
|
+
/**
|
|
1898
2042
|
* _sanitizeElements
|
|
1899
2043
|
*
|
|
1900
2044
|
* @protect nodeName
|
|
@@ -1905,24 +2049,18 @@ function createDOMPurify() {
|
|
|
1905
2049
|
*/
|
|
1906
2050
|
const _sanitizeElements = function _sanitizeElements(currentNode, root) {
|
|
1907
2051
|
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
1908
|
-
if (currentNode
|
|
1909
|
-
if (IN_PLACE) _neutralizeSubtree(currentNode);
|
|
1910
|
-
return true;
|
|
1911
|
-
}
|
|
2052
|
+
if (_handleHookDetachedNode(currentNode, root)) return true;
|
|
1912
2053
|
if (_isClobbered(currentNode)) {
|
|
1913
2054
|
_forceRemove(currentNode);
|
|
1914
2055
|
return true;
|
|
1915
2056
|
}
|
|
1916
|
-
const tagName = transformCaseFunc(
|
|
2057
|
+
const tagName = transformCaseFunc(_readNodeName(currentNode));
|
|
1917
2058
|
ALLOWED_TAGS = _forkSharedAllowlist(hooks.uponSanitizeElement, ALLOWED_TAGS, DEFAULT_ALLOWED_TAGS, SET_CONFIG_ALLOWED_TAGS);
|
|
1918
2059
|
_executeHooks(hooks.uponSanitizeElement, currentNode, {
|
|
1919
2060
|
tagName,
|
|
1920
2061
|
allowedTags: ALLOWED_TAGS
|
|
1921
2062
|
});
|
|
1922
|
-
if (currentNode
|
|
1923
|
-
if (IN_PLACE) _neutralizeSubtree(currentNode);
|
|
1924
|
-
return true;
|
|
1925
|
-
}
|
|
2063
|
+
if (_handleHookDetachedNode(currentNode, root)) return true;
|
|
1926
2064
|
if (_isUnsafeNode(currentNode, tagName)) {
|
|
1927
2065
|
_forceRemove(currentNode);
|
|
1928
2066
|
return true;
|
|
@@ -1932,7 +2070,7 @@ function createDOMPurify() {
|
|
|
1932
2070
|
if (removed === false) _executeHooks(hooks.afterSanitizeElements, currentNode, null);
|
|
1933
2071
|
return removed;
|
|
1934
2072
|
}
|
|
1935
|
-
if ((
|
|
2073
|
+
if (_readNodeType(currentNode) === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
|
|
1936
2074
|
_forceRemove(currentNode);
|
|
1937
2075
|
return true;
|
|
1938
2076
|
}
|
|
@@ -1960,20 +2098,17 @@ function createDOMPurify() {
|
|
|
1960
2098
|
*/
|
|
1961
2099
|
const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1962
2100
|
if (FORBID_ATTR[lcName]) return false;
|
|
1963
|
-
if (
|
|
1964
|
-
if (SAFE_FOR_XML && lcName === "for" && lcTag !== "label" && lcTag !== "output") return false;
|
|
2101
|
+
if (_isPatchLinkageAttribute(lcName, lcTag)) return false;
|
|
1965
2102
|
if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in document || value in formElement)) return false;
|
|
1966
2103
|
const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
|
|
1967
|
-
if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName));
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
else if (value) return false;
|
|
1976
|
-
return true;
|
|
2104
|
+
if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) return true;
|
|
2105
|
+
if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) return true;
|
|
2106
|
+
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);
|
|
2107
|
+
if (URI_SAFE_ATTRIBUTES[lcName]) return true;
|
|
2108
|
+
if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ""))) return true;
|
|
2109
|
+
if ((lcName === "src" || lcName === "xlink:href" || lcName === "href") && lcTag !== "script" && stringIndexOf(value, "data:") === 0 && DATA_URI_TAGS[lcTag]) return true;
|
|
2110
|
+
if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ""))) return true;
|
|
2111
|
+
return !value;
|
|
1977
2112
|
};
|
|
1978
2113
|
const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, [
|
|
1979
2114
|
"annotation-xml",
|
|
@@ -2075,29 +2210,29 @@ function createDOMPurify() {
|
|
|
2075
2210
|
_executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
|
|
2076
2211
|
value = hookEvent.attrValue;
|
|
2077
2212
|
if (SANITIZE_NAMED_PROPS && (lcName === "id" || lcName === "name") && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
|
|
2078
|
-
_removeAttribute(name, currentNode);
|
|
2213
|
+
_removeAttribute(name, currentNode, attr);
|
|
2079
2214
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
2080
2215
|
}
|
|
2081
2216
|
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
|
|
2082
|
-
_removeAttribute(name, currentNode);
|
|
2217
|
+
_removeAttribute(name, currentNode, attr);
|
|
2083
2218
|
continue;
|
|
2084
2219
|
}
|
|
2085
2220
|
if (lcName === "attributename" && stringMatch(value, "href")) {
|
|
2086
|
-
_removeAttribute(name, currentNode);
|
|
2221
|
+
_removeAttribute(name, currentNode, attr);
|
|
2087
2222
|
continue;
|
|
2088
2223
|
}
|
|
2089
2224
|
if (hookEvent.forceKeepAttr) continue;
|
|
2090
2225
|
if (!hookEvent.keepAttr) {
|
|
2091
|
-
_removeAttribute(name, currentNode);
|
|
2226
|
+
_removeAttribute(name, currentNode, attr);
|
|
2092
2227
|
continue;
|
|
2093
2228
|
}
|
|
2094
2229
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
|
|
2095
|
-
_removeAttribute(name, currentNode);
|
|
2230
|
+
_removeAttribute(name, currentNode, attr);
|
|
2096
2231
|
continue;
|
|
2097
2232
|
}
|
|
2098
2233
|
if (SAFE_FOR_TEMPLATES) value = _stripTemplateExpressions(value);
|
|
2099
2234
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
2100
|
-
_removeAttribute(name, currentNode);
|
|
2235
|
+
_removeAttribute(name, currentNode, attr);
|
|
2101
2236
|
continue;
|
|
2102
2237
|
}
|
|
2103
2238
|
value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
|
|
@@ -2119,7 +2254,7 @@ function createDOMPurify() {
|
|
|
2119
2254
|
_sanitizeElements(shadowNode, fragment);
|
|
2120
2255
|
_sanitizeAttributes(shadowNode);
|
|
2121
2256
|
if (_isDocumentFragment(shadowNode.content)) _sanitizeShadowDOM2(shadowNode.content);
|
|
2122
|
-
if ((
|
|
2257
|
+
if (_readNodeType(shadowNode) === NODE_TYPE.element) {
|
|
2123
2258
|
const innerSr = getShadowRoot(shadowNode);
|
|
2124
2259
|
if (_isDocumentFragment(innerSr)) {
|
|
2125
2260
|
_sanitizeAttachedShadowRoots(innerSr);
|
|
@@ -2160,7 +2295,7 @@ function createDOMPurify() {
|
|
|
2160
2295
|
continue;
|
|
2161
2296
|
}
|
|
2162
2297
|
const node = item.node;
|
|
2163
|
-
const isElement = (
|
|
2298
|
+
const isElement = _readNodeType(node) === NODE_TYPE.element;
|
|
2164
2299
|
const childNodes = getChildNodes(node);
|
|
2165
2300
|
if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push({
|
|
2166
2301
|
node: childNodes[i],
|
|
@@ -2211,7 +2346,7 @@ function createDOMPurify() {
|
|
|
2211
2346
|
const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
|
|
2212
2347
|
if (inPlace) {
|
|
2213
2348
|
_neutralizePatchLinkage(dirty);
|
|
2214
|
-
const nn =
|
|
2349
|
+
const nn = _readNodeName(dirty);
|
|
2215
2350
|
if (typeof nn === "string") {
|
|
2216
2351
|
const tagName = transformCaseFunc(nn);
|
|
2217
2352
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|