@ukic/web-components 3.29.0 → 3.30.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/components/ic-accordion.js +53 -38
- package/components/ic-accordion.js.map +1 -1
- package/components/ic-menu2.js +274 -35
- package/components/ic-menu2.js.map +1 -1
- package/components/ic-navigation-button.js +8 -5
- package/components/ic-navigation-button.js.map +1 -1
- package/components/ic-pagination-bar.js.map +1 -1
- package/components/ic-skip-link.js.map +1 -1
- package/components/ic-status-tag.js.map +1 -1
- package/components/ic-switch.js.map +1 -1
- package/dist/cjs/ic-accordion.cjs.entry.js +53 -38
- package/dist/cjs/ic-accordion.cjs.entry.js.map +1 -1
- package/dist/cjs/ic-button_3.cjs.entry.js.map +1 -1
- package/dist/cjs/ic-horizontal-scroll.cjs.entry.js.map +1 -1
- package/dist/cjs/ic-input-component-container_3.cjs.entry.js +274 -35
- package/dist/cjs/ic-input-component-container_3.cjs.entry.js.map +1 -1
- package/dist/cjs/ic-navigation-button.cjs.entry.js +8 -5
- package/dist/cjs/ic-navigation-button.cjs.entry.js.map +1 -1
- package/dist/cjs/ic-section-container.cjs.entry.js.map +1 -1
- package/dist/collection/components/ic-accordion/ic-accordion.js +53 -38
- package/dist/collection/components/ic-accordion/ic-accordion.js.map +1 -1
- package/dist/collection/components/ic-navigation-button/ic-navigation-button.js +8 -5
- package/dist/collection/components/ic-navigation-button/ic-navigation-button.js.map +1 -1
- package/dist/components/ic-accordion.js +53 -38
- package/dist/components/ic-accordion.js.map +1 -1
- package/dist/components/ic-back-to-top.js.map +1 -1
- package/dist/components/ic-hero.js.map +1 -1
- package/dist/components/ic-menu2.js +274 -35
- package/dist/components/ic-menu2.js.map +1 -1
- package/dist/components/ic-navigation-button.js +8 -5
- package/dist/components/ic-navigation-button.js.map +1 -1
- package/dist/components/ic-pagination-bar.js.map +1 -1
- package/dist/components/ic-popover-menu.js.map +1 -1
- package/dist/components/ic-radio-group.js.map +1 -1
- package/dist/components/ic-radio-option.js.map +1 -1
- package/dist/components/ic-search-bar.js.map +1 -1
- package/dist/components/ic-skip-link.js.map +1 -1
- package/dist/components/ic-status-tag.js.map +1 -1
- package/dist/components/ic-switch.js.map +1 -1
- package/dist/core/core.esm.js +1 -1
- package/dist/core/{p-9ced00a7.entry.js → p-41bbdc4c.entry.js} +2 -2
- package/dist/core/{p-9ced00a7.entry.js.map → p-41bbdc4c.entry.js.map} +1 -1
- package/dist/core/p-8fc5e9f9.entry.js.map +1 -1
- package/dist/core/p-98dee727.entry.js.map +1 -1
- package/dist/core/{p-fc4b7d2b.entry.js → p-9cf35c23.entry.js} +2 -2
- package/dist/core/p-9cf35c23.entry.js.map +1 -0
- package/dist/core/p-a8d98164.entry.js.map +1 -1
- package/dist/core/p-ccba84b7.entry.js +3 -0
- package/dist/core/p-ccba84b7.entry.js.map +1 -0
- package/dist/esm/ic-accordion.entry.js +53 -38
- package/dist/esm/ic-accordion.entry.js.map +1 -1
- package/dist/esm/ic-button_3.entry.js.map +1 -1
- package/dist/esm/ic-horizontal-scroll.entry.js.map +1 -1
- package/dist/esm/ic-input-component-container_3.entry.js +274 -35
- package/dist/esm/ic-input-component-container_3.entry.js.map +1 -1
- package/dist/esm/ic-navigation-button.entry.js +8 -5
- package/dist/esm/ic-navigation-button.entry.js.map +1 -1
- package/dist/esm/ic-section-container.entry.js.map +1 -1
- package/dist/types/components/ic-accordion/ic-accordion.d.ts +2 -0
- package/hydrate/index.js +334 -77
- package/hydrate/index.mjs +334 -77
- package/package.json +1 -1
- package/dist/core/p-305d9639.entry.js +0 -3
- package/dist/core/p-305d9639.entry.js.map +0 -1
- package/dist/core/p-fc4b7d2b.entry.js.map +0 -1
package/hydrate/index.mjs
CHANGED
|
@@ -3862,16 +3862,9 @@ class Accordion {
|
|
|
3862
3862
|
};
|
|
3863
3863
|
// Set accordion animation
|
|
3864
3864
|
this.setAccordionAnimation = (el, duration, property, delay) => {
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
el.style.transitionDelay = delay;
|
|
3869
|
-
}
|
|
3870
|
-
else {
|
|
3871
|
-
el.style.transitionDuration = "0ms";
|
|
3872
|
-
el.style.transitionProperty = "none";
|
|
3873
|
-
el.style.transitionDelay = "0ms";
|
|
3874
|
-
}
|
|
3865
|
+
el.style.transitionDuration = `${duration}ms`;
|
|
3866
|
+
el.style.transitionProperty = property;
|
|
3867
|
+
el.style.transitionTimingFunction = delay;
|
|
3875
3868
|
};
|
|
3876
3869
|
this.setExpandedContentStyle = (ev, expandedContent) => {
|
|
3877
3870
|
if (ev.propertyName === "height" && expandedContent.clientHeight > 0) {
|
|
@@ -3885,30 +3878,52 @@ class Accordion {
|
|
|
3885
3878
|
}
|
|
3886
3879
|
};
|
|
3887
3880
|
this.animateExpandedContent = () => {
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
expandedContentEl.addEventListener("transitionend", (e) => {
|
|
3896
|
-
this.setExpandedContentStyle(e, expandedContentEl);
|
|
3897
|
-
});
|
|
3898
|
-
}
|
|
3899
|
-
else if (!this.expanded) {
|
|
3900
|
-
const expandedContentEl = this.expandedContentEl;
|
|
3901
|
-
expandedContentEl.style.height = `${expandedContentEl.scrollHeight}px`;
|
|
3902
|
-
if (expandedContentEl.scrollHeight > 0 && !this.expanded) {
|
|
3903
|
-
expandedContentEl.style.height = "0";
|
|
3904
|
-
this.setAccordionAnimation(expandedContentEl, "300", "height", "ease-in");
|
|
3905
|
-
expandedContentEl.classList.remove(EXPANDED_CONTENT_OPENED_CLASS);
|
|
3906
|
-
}
|
|
3907
|
-
expandedContentEl.addEventListener("transitionend", (e) => {
|
|
3908
|
-
this.hideExpandedContent(e, expandedContentEl);
|
|
3909
|
-
});
|
|
3910
|
-
}
|
|
3881
|
+
const expandedContentEl = this.expandedContentEl;
|
|
3882
|
+
if (!expandedContentEl)
|
|
3883
|
+
return;
|
|
3884
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
3885
|
+
if (this.expanded) {
|
|
3886
|
+
this.expandContent(expandedContentEl, prefersReducedMotion);
|
|
3887
|
+
return;
|
|
3911
3888
|
}
|
|
3889
|
+
this.collapseContent(expandedContentEl, prefersReducedMotion);
|
|
3890
|
+
};
|
|
3891
|
+
this.expandContent = (expandedContentEl, prefersReducedMotion) => {
|
|
3892
|
+
const elementHeight = expandedContentEl.scrollHeight;
|
|
3893
|
+
if (elementHeight <= 0)
|
|
3894
|
+
return;
|
|
3895
|
+
expandedContentEl.style.setProperty(this.CONTENT_VISIBILITY_PROPERTY, "visible");
|
|
3896
|
+
if (prefersReducedMotion) {
|
|
3897
|
+
expandedContentEl.classList.add(EXPANDED_CONTENT_OPENED_CLASS);
|
|
3898
|
+
expandedContentEl.style.height = "auto";
|
|
3899
|
+
return;
|
|
3900
|
+
}
|
|
3901
|
+
expandedContentEl.style.height = `${elementHeight}px`;
|
|
3902
|
+
this.setAccordionAnimation(expandedContentEl, "300", "height", "ease-out");
|
|
3903
|
+
expandedContentEl.addEventListener("transitionend", (e) => {
|
|
3904
|
+
this.setExpandedContentStyle(e, expandedContentEl);
|
|
3905
|
+
}, { once: true });
|
|
3906
|
+
};
|
|
3907
|
+
this.collapseContent = (expandedContentEl, prefersReducedMotion) => {
|
|
3908
|
+
const elementHeight = expandedContentEl.scrollHeight;
|
|
3909
|
+
if (elementHeight <= 0)
|
|
3910
|
+
return;
|
|
3911
|
+
// Set the content panel as pixels from "auto" so it can be animated
|
|
3912
|
+
expandedContentEl.style.height = `${elementHeight}px`;
|
|
3913
|
+
if (prefersReducedMotion) {
|
|
3914
|
+
expandedContentEl.style.height = "0";
|
|
3915
|
+
expandedContentEl.classList.remove(EXPANDED_CONTENT_OPENED_CLASS);
|
|
3916
|
+
expandedContentEl.style.setProperty(this.CONTENT_VISIBILITY_PROPERTY, "hidden");
|
|
3917
|
+
return;
|
|
3918
|
+
}
|
|
3919
|
+
requestAnimationFrame(() => {
|
|
3920
|
+
expandedContentEl.style.height = "0";
|
|
3921
|
+
});
|
|
3922
|
+
this.setAccordionAnimation(expandedContentEl, "300", "height", "ease-in");
|
|
3923
|
+
expandedContentEl.classList.remove(EXPANDED_CONTENT_OPENED_CLASS);
|
|
3924
|
+
expandedContentEl.addEventListener("transitionend", (e) => {
|
|
3925
|
+
this.hideExpandedContent(e, expandedContentEl);
|
|
3926
|
+
}, { once: true });
|
|
3912
3927
|
};
|
|
3913
3928
|
}
|
|
3914
3929
|
watchDisabledHandler() {
|
|
@@ -3945,19 +3960,19 @@ class Accordion {
|
|
|
3945
3960
|
}
|
|
3946
3961
|
render() {
|
|
3947
3962
|
const { size, disabled, expanded, theme } = this;
|
|
3948
|
-
return (hAsync(Host, { key: '
|
|
3963
|
+
return (hAsync(Host, { key: '840baa7799dc4c48c232a7dc170937bb563900d5', id: this.accordionId, class: {
|
|
3949
3964
|
["ic-accordion-disabled"]: !!disabled,
|
|
3950
3965
|
[`ic-theme-${theme}`]: theme !== "inherit",
|
|
3951
|
-
}, "aria-disabled": disabled ? "true" : "false" }, hAsync("button", { key: '
|
|
3966
|
+
}, "aria-disabled": disabled ? "true" : "false" }, hAsync("button", { key: 'dce03cb5ba0f61a1d5b75ab1f648eaa970d5d78e', ref: (el) => (this.accordionBtnHeading = el), id: `${this.accordionId}-button`, disabled: disabled, tabindex: disabled ? -1 : 0, class: {
|
|
3952
3967
|
[`${size}`]: true,
|
|
3953
3968
|
["section-button"]: true,
|
|
3954
3969
|
["section-button-open"]: !!expanded && !disabled,
|
|
3955
|
-
}, "aria-expanded": `${expanded}`, "aria-controls": "expanded-content-area", onClick: this.toggleExpanded }, isSlotUsed(this.el, "icon") && (hAsync("div", { key: '
|
|
3970
|
+
}, "aria-expanded": `${expanded}`, "aria-controls": "expanded-content-area", onClick: this.toggleExpanded }, isSlotUsed(this.el, "icon") && (hAsync("div", { key: '3301b5e4526831e7e8a4400d775f55d99ec29d27', class: "icon-container" }, hAsync("slot", { key: 'aa49cdcd343009d6d5b7e68f5c1013cd7fcc7638', name: "icon" }))), hAsync("ic-typography", { key: 'da324db78e29a81f0c5bb7209f870b8832c25d67', variant: "subtitle-large", class: "section-header" }, isSlotUsed(this.el, "heading") ? (hAsync("slot", { name: "heading" })) : (this.heading)), hAsync("span", { key: '9eb1350c8ae3f9c940e1299a17b3b510ccb66ef6', class: {
|
|
3956
3971
|
["expand-chevron"]: true,
|
|
3957
3972
|
["content-expanded-chevron"]: !!expanded && !disabled,
|
|
3958
|
-
}, "aria-hidden": "true", innerHTML: chevronIcon })), hAsync("div", { key: '
|
|
3973
|
+
}, "aria-hidden": "true", innerHTML: chevronIcon })), hAsync("div", { key: '9b04e4a6df4def67189609215cd4849890900146', class: {
|
|
3959
3974
|
["expanded-content"]: true,
|
|
3960
|
-
}, "aria-labelledby": `${this.accordionId}-button`, role: "region", "aria-hidden": `${!expanded}`, id: "expanded-content-area", ref: (el) => (this.expandedContentEl = el) }, hAsync("div", { key: '
|
|
3975
|
+
}, "aria-labelledby": `${this.accordionId}-button`, role: "region", "aria-hidden": `${!expanded}`, id: "expanded-content-area", ref: (el) => (this.expandedContentEl = el) }, hAsync("div", { key: '594f88da687634cbbad7e12d8df21a3dc7961a64', class: "expanded-content-inner" }, this.message ? (hAsync("ic-typography", { variant: "body" }, this.message)) : (hAsync("slot", null))))));
|
|
3961
3976
|
}
|
|
3962
3977
|
get el() { return getElement(this); }
|
|
3963
3978
|
static get watchers() { return {
|
|
@@ -10628,7 +10643,7 @@ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof win
|
|
|
10628
10643
|
|
|
10629
10644
|
var purify = {exports: {}};
|
|
10630
10645
|
|
|
10631
|
-
/*! @license DOMPurify 3.4.
|
|
10646
|
+
/*! @license DOMPurify 3.4.13 | (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.13/LICENSE */
|
|
10632
10647
|
|
|
10633
10648
|
(function (module, exports) {
|
|
10634
10649
|
(function (global, factory) {
|
|
@@ -10945,7 +10960,7 @@ var purify = {exports: {}};
|
|
|
10945
10960
|
const text = freeze(['#text']);
|
|
10946
10961
|
|
|
10947
10962
|
const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
|
|
10948
|
-
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
|
|
10963
|
+
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dominant-baseline', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-orientation', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
|
|
10949
10964
|
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
10950
10965
|
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
10951
10966
|
|
|
@@ -11058,7 +11073,7 @@ var purify = {exports: {}};
|
|
|
11058
11073
|
function createDOMPurify() {
|
|
11059
11074
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
11060
11075
|
const DOMPurify = root => createDOMPurify(root);
|
|
11061
|
-
DOMPurify.version = '3.4.
|
|
11076
|
+
DOMPurify.version = '3.4.13';
|
|
11062
11077
|
DOMPurify.removed = [];
|
|
11063
11078
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
11064
11079
|
// Not running in a browser, provide a factory function
|
|
@@ -11085,6 +11100,7 @@ var purify = {exports: {}};
|
|
|
11085
11100
|
const getAttributes = lookupGetter(ElementPrototype, 'attributes');
|
|
11086
11101
|
const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
|
|
11087
11102
|
const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
|
|
11103
|
+
const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, 'ownerDocument') : null;
|
|
11088
11104
|
// As per issue #47, the web-components registry is inherited by a
|
|
11089
11105
|
// new document created via createHTMLDocument. As per the spec
|
|
11090
11106
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
@@ -11736,6 +11752,13 @@ var purify = {exports: {}};
|
|
|
11736
11752
|
* @param root the in-place root to empty
|
|
11737
11753
|
*/
|
|
11738
11754
|
const _neutralizeRoot = function _neutralizeRoot(root) {
|
|
11755
|
+
/* Strip every disallowed attribute (on* handlers included) off the whole
|
|
11756
|
+
subtree BEFORE detaching anything. Detaching first would hand back
|
|
11757
|
+
handler-bearing originals (e.g. an already-loading `<img onerror>`)
|
|
11758
|
+
whose queued resource event still fires in page scope after we throw.
|
|
11759
|
+
Clobber-safe reads; a doomed clobbered node's own attributes are
|
|
11760
|
+
irrelevant while its non-clobbered descendants are reached and scrubbed. */
|
|
11761
|
+
_neutralizeSubtree(root);
|
|
11739
11762
|
const childNodes = getChildNodes(root);
|
|
11740
11763
|
if (childNodes) {
|
|
11741
11764
|
const snapshot = [];
|
|
@@ -11863,6 +11886,82 @@ var purify = {exports: {}};
|
|
|
11863
11886
|
}
|
|
11864
11887
|
}
|
|
11865
11888
|
};
|
|
11889
|
+
/**
|
|
11890
|
+
* _neutralizePatchLinkage
|
|
11891
|
+
*
|
|
11892
|
+
* IN_PLACE entry pre-pass (declarative-partial-updates / streaming
|
|
11893
|
+
* hardening, https://github.com/WICG/declarative-partial-updates).
|
|
11894
|
+
*
|
|
11895
|
+
* The main walk strips patch linkage (`for`/`patchsrc`) and removes range
|
|
11896
|
+
* markers (PIs / markup comments) node-by-node, in document order, AS it
|
|
11897
|
+
* reaches each node. On a live in-place root that leaves a window: from the
|
|
11898
|
+
* moment the root is connected until the walk arrives at a given node, that
|
|
11899
|
+
* node's linkage is live. A patch applied on connection/stream can fire as
|
|
11900
|
+
* a microtask during the walk and inject or teleport an unsanitized DOM
|
|
11901
|
+
* range into a region the iterator has already passed and will not revisit,
|
|
11902
|
+
* so the post-return "tree is sanitized" contract is violated. Sweep the
|
|
11903
|
+
* whole tree once up front and sever every linkage before the walk begins,
|
|
11904
|
+
* closing that window.
|
|
11905
|
+
*
|
|
11906
|
+
* This CANNOT undo a patch that already fired before sanitize ran — that is
|
|
11907
|
+
* the irreducible "do not IN_PLACE a live-connected attacker tree" caveat —
|
|
11908
|
+
* but it closes everything from sanitize-start onward. Gated on SAFE_FOR_XML
|
|
11909
|
+
* to group with the rest of the declarative-partial-updates handling and
|
|
11910
|
+
* stay overridable, consistent with the codebase.
|
|
11911
|
+
*
|
|
11912
|
+
* Clobber-safe traversal (cached childNodes getter); per-node try/catch so a
|
|
11913
|
+
* clobbered root cannot defeat the sweep of its non-clobbered descendants.
|
|
11914
|
+
*
|
|
11915
|
+
* NOTE (pending real-Chrome confirmation, see test/declarative-patch-probe
|
|
11916
|
+
* .html Q1): this mirrors the existing policy of keeping `for` on
|
|
11917
|
+
* <label>/<output>. If the shipping feature can drive a patch through a
|
|
11918
|
+
* surviving `for`-on-label/output + `id` pair, this pre-pass and the
|
|
11919
|
+
* attribute check at _isBasicCustomElement's caller must additionally drop
|
|
11920
|
+
* that pair on the IN_PLACE path. Left as-is until the taxonomy is verified.
|
|
11921
|
+
*
|
|
11922
|
+
* @param root the in-place root to sweep
|
|
11923
|
+
*/
|
|
11924
|
+
const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
|
|
11925
|
+
if (!SAFE_FOR_XML) {
|
|
11926
|
+
return;
|
|
11927
|
+
}
|
|
11928
|
+
const stack = [root];
|
|
11929
|
+
while (stack.length > 0) {
|
|
11930
|
+
const node = stack.pop();
|
|
11931
|
+
const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
|
|
11932
|
+
/* Remove range markers (the target side of a patch linkage): every
|
|
11933
|
+
processing instruction, and any markup-bearing comment. */
|
|
11934
|
+
if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
|
|
11935
|
+
try {
|
|
11936
|
+
remove(node);
|
|
11937
|
+
} catch (_) {
|
|
11938
|
+
/* Best-effort */
|
|
11939
|
+
}
|
|
11940
|
+
continue;
|
|
11941
|
+
}
|
|
11942
|
+
/* Strip patch-source attributes (the source side) off elements. */
|
|
11943
|
+
if (nodeType === NODE_TYPE.element) {
|
|
11944
|
+
const element = node;
|
|
11945
|
+
const lcTag = transformCaseFunc(getNodeName ? getNodeName(node) : node.nodeName);
|
|
11946
|
+
try {
|
|
11947
|
+
if (element.hasAttribute && element.hasAttribute('patchsrc')) {
|
|
11948
|
+
element.removeAttribute('patchsrc');
|
|
11949
|
+
}
|
|
11950
|
+
if (element.hasAttribute && element.hasAttribute('for') && lcTag !== 'label' && lcTag !== 'output') {
|
|
11951
|
+
element.removeAttribute('for');
|
|
11952
|
+
}
|
|
11953
|
+
} catch (_) {
|
|
11954
|
+
/* Clobbered removeAttribute/hasAttribute on a doomed node — ignore */
|
|
11955
|
+
}
|
|
11956
|
+
}
|
|
11957
|
+
const childNodes = getChildNodes(node);
|
|
11958
|
+
if (childNodes) {
|
|
11959
|
+
for (let i = childNodes.length - 1; i >= 0; --i) {
|
|
11960
|
+
stack.push(childNodes[i]);
|
|
11961
|
+
}
|
|
11962
|
+
}
|
|
11963
|
+
}
|
|
11964
|
+
};
|
|
11866
11965
|
/**
|
|
11867
11966
|
* _initDocument
|
|
11868
11967
|
*
|
|
@@ -11920,7 +12019,17 @@ var purify = {exports: {}};
|
|
|
11920
12019
|
* @return The created NodeIterator
|
|
11921
12020
|
*/
|
|
11922
12021
|
const _createNodeIterator = function _createNodeIterator(root) {
|
|
11923
|
-
|
|
12022
|
+
/* Read ownerDocument through the cached Node.prototype getter, never the
|
|
12023
|
+
direct property. HTMLFormElement has [LegacyOverrideBuiltIns], so a
|
|
12024
|
+
clobbering child (<input name="ownerDocument"> or a form-associated
|
|
12025
|
+
external input) shadows the prototype getter and makes a direct read
|
|
12026
|
+
return that <input>. createNodeIterator.call(<input>, ...) then throws
|
|
12027
|
+
"Illegal invocation", and on the IN_PLACE path that throw lands before
|
|
12028
|
+
the walk's fail-closed barrier - leaving the caller's live tree, with
|
|
12029
|
+
any already-armed handler in it, un-neutralized. The cached getter
|
|
12030
|
+
returns the real Document regardless of the clobber. */
|
|
12031
|
+
const doc = getOwnerDocument ? getOwnerDocument(root) : root.ownerDocument;
|
|
12032
|
+
return createNodeIterator.call(doc || root, root,
|
|
11924
12033
|
// eslint-disable-next-line no-bitwise
|
|
11925
12034
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
|
|
11926
12035
|
};
|
|
@@ -11960,7 +12069,11 @@ var purify = {exports: {}};
|
|
|
11960
12069
|
const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
|
|
11961
12070
|
var _node$querySelectorAl;
|
|
11962
12071
|
node.normalize();
|
|
11963
|
-
|
|
12072
|
+
/* Clobber-safe ownerDocument read, same reasoning as _createNodeIterator:
|
|
12073
|
+
under SAFE_FOR_TEMPLATES this runs on the live IN_PLACE root, which may
|
|
12074
|
+
carry a form-named-getter override of ownerDocument. */
|
|
12075
|
+
const doc = getOwnerDocument ? getOwnerDocument(node) : node.ownerDocument;
|
|
12076
|
+
const walker = createNodeIterator.call(doc || node, node,
|
|
11964
12077
|
// eslint-disable-next-line no-bitwise
|
|
11965
12078
|
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
|
|
11966
12079
|
let currentNode = walker.nextNode();
|
|
@@ -12113,15 +12226,21 @@ var purify = {exports: {}};
|
|
|
12113
12226
|
/**
|
|
12114
12227
|
* Handle a node whose tag is forbidden or not allowlisted: keep
|
|
12115
12228
|
* allowed custom elements (false return exits _sanitizeElements
|
|
12116
|
-
* early - namespace
|
|
12117
|
-
*
|
|
12118
|
-
*
|
|
12229
|
+
* early - the namespace and fallback-tag removal checks are
|
|
12230
|
+
* intentionally skipped for kept custom elements), else hoist
|
|
12231
|
+
* content per KEEP_CONTENT and remove.
|
|
12232
|
+
*
|
|
12233
|
+
* A kept custom element is the ONLY case in which this function
|
|
12234
|
+
* returns false, so the caller uses that return value to run the
|
|
12235
|
+
* afterSanitizeElements hook on the kept element and keep the
|
|
12236
|
+
* element-hook lifecycle consistent with normal allowlisted
|
|
12237
|
+
* elements (GHSA-c2j3-45gr-mqc4).
|
|
12119
12238
|
*
|
|
12120
12239
|
* @param currentNode the disallowed node
|
|
12121
12240
|
* @param tagName the node's transformCaseFunc'd tag name
|
|
12122
12241
|
* @return true if the node was removed, false if kept
|
|
12123
12242
|
*/
|
|
12124
|
-
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName) {
|
|
12243
|
+
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
|
|
12125
12244
|
/* Check if we have a custom element to handle */
|
|
12126
12245
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
12127
12246
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
@@ -12144,27 +12263,26 @@ var purify = {exports: {}};
|
|
|
12144
12263
|
const childNodes = getChildNodes(currentNode);
|
|
12145
12264
|
if (childNodes && parentNode) {
|
|
12146
12265
|
const childCount = childNodes.length;
|
|
12147
|
-
/*
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
event to neutralise.
|
|
12266
|
+
/* Hoist by moving each child up one level rather than deep-cloning
|
|
12267
|
+
it. Moving transfers every descendant exactly once, so a chain of
|
|
12268
|
+
nested disallowed elements costs O(n) instead of the O(n^2) that
|
|
12269
|
+
re-cloning the shrinking subtree at each level produced; it also
|
|
12270
|
+
empties the removed original, so `DOMPurify.removed` no longer
|
|
12271
|
+
pins whole subtrees. Moving preserves the in-place guarantee too:
|
|
12272
|
+
an original carrying already-queued resource events (`<img
|
|
12273
|
+
onerror>`, `<video>`/`<audio>` error, lazy/`onload`, …) is
|
|
12274
|
+
relocated and sanitised rather than left detached but still armed.
|
|
12275
|
+
The sole case that must clone is removing the walk root itself.
|
|
12276
|
+
The result is serialised from the root's subtree, so a restrictive
|
|
12277
|
+
ALLOWED_TAGS that strips the root (`body` on the string path) must
|
|
12278
|
+
leave the content inside it, which only cloning does. In IN_PLACE
|
|
12279
|
+
the root is pre-validated as an allowed tag and so is never removed
|
|
12280
|
+
here, so that path always takes the move branch.
|
|
12163
12281
|
`childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
|
|
12164
12282
|
valid whether we move (drops the trailing entry) or clone (leaves
|
|
12165
12283
|
the list intact). */
|
|
12166
12284
|
for (let i = childCount - 1; i >= 0; --i) {
|
|
12167
|
-
const hoisted =
|
|
12285
|
+
const hoisted = currentNode === root ? cloneNode(childNodes[i], true) : childNodes[i];
|
|
12168
12286
|
parentNode.insertBefore(hoisted, getNextSibling(currentNode));
|
|
12169
12287
|
}
|
|
12170
12288
|
}
|
|
@@ -12172,6 +12290,26 @@ var purify = {exports: {}};
|
|
|
12172
12290
|
_forceRemove(currentNode);
|
|
12173
12291
|
return true;
|
|
12174
12292
|
};
|
|
12293
|
+
/**
|
|
12294
|
+
* Fork a hook-mutable allowlist off its shared binding the first time a
|
|
12295
|
+
* (possibly lazily-installed) uponSanitize* hook is about to see it, so the
|
|
12296
|
+
* hook cannot widen the per-instance default or the setConfig binding by
|
|
12297
|
+
* reference and leak past the call. Returns the set unchanged once it is
|
|
12298
|
+
* already call-local, so repeated calls across elements are idempotent.
|
|
12299
|
+
*
|
|
12300
|
+
* @param hookList the uponSanitize* hook array for this event
|
|
12301
|
+
* @param set the current ALLOWED_TAGS / ALLOWED_ATTR binding
|
|
12302
|
+
* @param defaultSet the per-instance DEFAULT_ALLOWED_* constant
|
|
12303
|
+
* @param setConfigSet the captured setConfig() binding, or null
|
|
12304
|
+
* @return a call-local clone if a hook is present and set is still shared,
|
|
12305
|
+
* else set unchanged
|
|
12306
|
+
*/
|
|
12307
|
+
const _forkSharedAllowlist = function _forkSharedAllowlist(hookList, set, defaultSet, setConfigSet) {
|
|
12308
|
+
if (hookList.length === 0) {
|
|
12309
|
+
return set;
|
|
12310
|
+
}
|
|
12311
|
+
return set === defaultSet || set === setConfigSet ? clone(set) : set;
|
|
12312
|
+
};
|
|
12175
12313
|
/**
|
|
12176
12314
|
* _sanitizeElements
|
|
12177
12315
|
*
|
|
@@ -12181,9 +12319,20 @@ var purify = {exports: {}};
|
|
|
12181
12319
|
* @param currentNode to check for permission to exist
|
|
12182
12320
|
* @return true if node was killed, false if left alive
|
|
12183
12321
|
*/
|
|
12184
|
-
|
|
12322
|
+
// eslint-disable-next-line complexity
|
|
12323
|
+
const _sanitizeElements = function _sanitizeElements(currentNode, root) {
|
|
12185
12324
|
/* Execute a hook if present */
|
|
12186
12325
|
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
12326
|
+
/* A hook may have detached the node - treat it as removed (see the
|
|
12327
|
+
detached-node comment after the uponSanitizeElement hook below). On
|
|
12328
|
+
the IN_PLACE path, neutralize the detached subtree first so a queued
|
|
12329
|
+
resource handler on it cannot fire in page scope after we return. */
|
|
12330
|
+
if (currentNode !== root && getParentNode(currentNode) === null) {
|
|
12331
|
+
if (IN_PLACE) {
|
|
12332
|
+
_neutralizeSubtree(currentNode);
|
|
12333
|
+
}
|
|
12334
|
+
return true;
|
|
12335
|
+
}
|
|
12187
12336
|
/* Check if element is clobbered or can clobber */
|
|
12188
12337
|
if (_isClobbered(currentNode)) {
|
|
12189
12338
|
_forceRemove(currentNode);
|
|
@@ -12191,11 +12340,47 @@ var purify = {exports: {}};
|
|
|
12191
12340
|
}
|
|
12192
12341
|
/* Now let's check the element's type and name */
|
|
12193
12342
|
const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
|
|
12343
|
+
/* Close the pre-walk clone-guard's timing gap: an uponSanitizeElement
|
|
12344
|
+
hook may have been installed after that guard sampled the hook arrays
|
|
12345
|
+
(e.g. lazily from beforeSanitizeElements), leaving ALLOWED_TAGS still
|
|
12346
|
+
aliasing a shared binding that a widening hook would mutate by
|
|
12347
|
+
reference. Fork it before exposing it to the hook. */
|
|
12348
|
+
ALLOWED_TAGS = _forkSharedAllowlist(hooks.uponSanitizeElement, ALLOWED_TAGS, DEFAULT_ALLOWED_TAGS, SET_CONFIG_ALLOWED_TAGS);
|
|
12194
12349
|
/* Execute a hook if present */
|
|
12195
12350
|
_executeHooks(hooks.uponSanitizeElement, currentNode, {
|
|
12196
12351
|
tagName,
|
|
12197
12352
|
allowedTags: ALLOWED_TAGS
|
|
12198
12353
|
});
|
|
12354
|
+
/* A hook may have detached the node from the tree — a long-standing
|
|
12355
|
+
user pattern (issue #469; draw.io-style foreignObject filtering).
|
|
12356
|
+
Per the cached, unclobberable parentNode getter the node is
|
|
12357
|
+
genuinely out of the tree, so it can reach neither the serialized
|
|
12358
|
+
output nor an IN_PLACE live tree; treat it as removed and stop
|
|
12359
|
+
processing it. Without this guard, the unsafe-node / namespace
|
|
12360
|
+
checks below would call _forceRemove on a parentless node and hit
|
|
12361
|
+
the REPORT-3 fail-closed throw — which exists for nodes DOMPurify
|
|
12362
|
+
wants gone but *cannot* detach (clobbered / parentless roots), the
|
|
12363
|
+
opposite of a node that is already safely gone. The walk root is
|
|
12364
|
+
exempt: a detached IN_PLACE root is legitimate input and must still
|
|
12365
|
+
be fully sanitized, and a kill-decision on it must keep hitting the
|
|
12366
|
+
REPORT-3 throw. Nodes detached by hooks stay the hook's
|
|
12367
|
+
responsibility for placement: they are not recorded in
|
|
12368
|
+
DOMPurify.removed, so the post-walk IN_PLACE pass (which iterates
|
|
12369
|
+
DOMPurify.removed) does not reach them. But a hook-detached subtree
|
|
12370
|
+
can still hold a queued resource-event handler - e.g. an <img onload>
|
|
12371
|
+
that began loading when the caller built the live tree - which fires
|
|
12372
|
+
in page scope after sanitize returns even though the handler never
|
|
12373
|
+
reached the returned tree. That is the audit-5 F1 hazard, and the
|
|
12374
|
+
documented node.remove() hook pattern walks straight into it. So on
|
|
12375
|
+
the IN_PLACE path we neutralize the detached subtree inline here,
|
|
12376
|
+
stripping its non-allow-listed attributes before returning, exactly
|
|
12377
|
+
as the post-walk pass does for _forceRemove'd subtrees. */
|
|
12378
|
+
if (currentNode !== root && getParentNode(currentNode) === null) {
|
|
12379
|
+
if (IN_PLACE) {
|
|
12380
|
+
_neutralizeSubtree(currentNode);
|
|
12381
|
+
}
|
|
12382
|
+
return true;
|
|
12383
|
+
}
|
|
12199
12384
|
/* Remove mXSS vectors, processing instructions and risky comments */
|
|
12200
12385
|
if (_isUnsafeNode(currentNode, tagName)) {
|
|
12201
12386
|
_forceRemove(currentNode);
|
|
@@ -12203,7 +12388,22 @@ var purify = {exports: {}};
|
|
|
12203
12388
|
}
|
|
12204
12389
|
/* Remove element if anything forbids its presence */
|
|
12205
12390
|
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
|
|
12206
|
-
|
|
12391
|
+
const removed = _sanitizeDisallowedNode(currentNode, tagName, root);
|
|
12392
|
+
/* A false return means the node is a custom element kept via
|
|
12393
|
+
CUSTOM_ELEMENT_HANDLING - the only keep path through
|
|
12394
|
+
_sanitizeDisallowedNode. Run afterSanitizeElements on it so the
|
|
12395
|
+
element-hook lifecycle matches normal allowlisted elements: a
|
|
12396
|
+
security policy applied in this hook (e.g. stripping an attribute
|
|
12397
|
+
from every surviving element) must not silently skip kept custom
|
|
12398
|
+
elements (GHSA-c2j3-45gr-mqc4). This mirrors the normal-element
|
|
12399
|
+
tail below - the hook runs, then the walker's subsequent
|
|
12400
|
+
_sanitizeAttributes pass sanitizes the element's attributes. The
|
|
12401
|
+
deliberately skipped namespace and fallback-tag removal checks stay
|
|
12402
|
+
skipped; they are removal decisions, not the hook contract. */
|
|
12403
|
+
if (removed === false) {
|
|
12404
|
+
_executeHooks(hooks.afterSanitizeElements, currentNode, null);
|
|
12405
|
+
}
|
|
12406
|
+
return removed;
|
|
12207
12407
|
}
|
|
12208
12408
|
/* Check whether element has a valid namespace.
|
|
12209
12409
|
Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
|
|
@@ -12250,6 +12450,33 @@ var purify = {exports: {}};
|
|
|
12250
12450
|
if (FORBID_ATTR[lcName]) {
|
|
12251
12451
|
return false;
|
|
12252
12452
|
}
|
|
12453
|
+
/* Reject declarative-partial-updates patch-linkage attributes
|
|
12454
|
+
(https://github.com/WICG/declarative-partial-updates).
|
|
12455
|
+
Empirical note (Chrome 150, verified — see
|
|
12456
|
+
test/declarative-patch-probe-v3.html): expansion is NOT applied after
|
|
12457
|
+
sanitization. For the string path it fires during sanitize()'s own
|
|
12458
|
+
parse, so the walk sees and sanitizes the fully materialized expanded
|
|
12459
|
+
tree — teleports into MathML/SVG integration points included; a
|
|
12460
|
+
weaponized `<template for>`->`<img onerror>` comes back with the handler
|
|
12461
|
+
stripped. For the IN_PLACE path it fires on connection, before the walk.
|
|
12462
|
+
Either way DOMPurify is NOT blind to the patch.
|
|
12463
|
+
This removal is therefore defense-in-depth rather than the sole barrier:
|
|
12464
|
+
it prevents live linkage from surviving into the OUTPUT and re-expanding
|
|
12465
|
+
in the caller's context, and keeps behaviour deterministic if a future
|
|
12466
|
+
engine defers expansion. `for` is legitimate only on <label>/<output>;
|
|
12467
|
+
anywhere else (notably <template for>) it links the element to a patch
|
|
12468
|
+
target and teleports or removes an arbitrary DOM range by id/marker name.
|
|
12469
|
+
`patchsrc` fetches remote markup and is treated as a script-loading
|
|
12470
|
+
mechanism (CSP). Gated on SAFE_FOR_XML so the removal groups with the
|
|
12471
|
+
other structural-threat checks and stays overridable, consistent with
|
|
12472
|
+
the rest of the codebase. PI range markers are already removed by
|
|
12473
|
+
_isUnsafeNode. */
|
|
12474
|
+
if (SAFE_FOR_XML && lcName === 'patchsrc') {
|
|
12475
|
+
return false;
|
|
12476
|
+
}
|
|
12477
|
+
if (SAFE_FOR_XML && lcName === 'for' && lcTag !== 'label' && lcTag !== 'output') {
|
|
12478
|
+
return false;
|
|
12479
|
+
}
|
|
12253
12480
|
/* Make sure attribute cannot clobber */
|
|
12254
12481
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
12255
12482
|
return false;
|
|
@@ -12366,6 +12593,9 @@ var purify = {exports: {}};
|
|
|
12366
12593
|
if (!attributes || _isClobbered(currentNode)) {
|
|
12367
12594
|
return;
|
|
12368
12595
|
}
|
|
12596
|
+
/* Same lazy-install guard as uponSanitizeElement (see there): fork the
|
|
12597
|
+
attribute allowlist off its shared binding before a hook can see it. */
|
|
12598
|
+
ALLOWED_ATTR = _forkSharedAllowlist(hooks.uponSanitizeAttribute, ALLOWED_ATTR, DEFAULT_ALLOWED_ATTR, SET_CONFIG_ALLOWED_ATTR);
|
|
12369
12599
|
const hookEvent = {
|
|
12370
12600
|
attrName: '',
|
|
12371
12601
|
attrValue: '',
|
|
@@ -12459,7 +12689,7 @@ var purify = {exports: {}};
|
|
|
12459
12689
|
/* Execute a hook if present */
|
|
12460
12690
|
_executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
|
|
12461
12691
|
/* Sanitize tags and elements */
|
|
12462
|
-
_sanitizeElements(shadowNode);
|
|
12692
|
+
_sanitizeElements(shadowNode, fragment);
|
|
12463
12693
|
/* Check attributes next */
|
|
12464
12694
|
_sanitizeAttributes(shadowNode);
|
|
12465
12695
|
/* Deep shadow DOM detected.
|
|
@@ -12650,6 +12880,11 @@ var purify = {exports: {}};
|
|
|
12650
12880
|
keep using and whose return value they ignore — unsanitized. REPORT-2. */
|
|
12651
12881
|
const inPlace = IN_PLACE && typeof dirty !== 'string' && _isNode(dirty);
|
|
12652
12882
|
if (inPlace) {
|
|
12883
|
+
/* Declarative-partial-updates / streaming pre-pass: sever every patch
|
|
12884
|
+
linkage across the live tree BEFORE the walk, so no patch can fire
|
|
12885
|
+
mid-walk and inject into an already-processed region. Runs first, so
|
|
12886
|
+
it also covers the forbidden/clobbered roots that throw below. */
|
|
12887
|
+
_neutralizePatchLinkage(dirty);
|
|
12653
12888
|
/* Do some early pre-sanitization to avoid unsafe root nodes.
|
|
12654
12889
|
Read nodeName through the cached prototype getter — a clobbering
|
|
12655
12890
|
child named "nodeName" on the form root would otherwise shadow
|
|
@@ -12659,6 +12894,9 @@ var purify = {exports: {}};
|
|
|
12659
12894
|
if (typeof nn === 'string') {
|
|
12660
12895
|
const tagName = transformCaseFunc(nn);
|
|
12661
12896
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
12897
|
+
/* Fail closed on a live root: neutralize handlers/children before
|
|
12898
|
+
throwing, exactly as the mid-walk abort path does. */
|
|
12899
|
+
_neutralizeRoot(dirty);
|
|
12662
12900
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
12663
12901
|
}
|
|
12664
12902
|
}
|
|
@@ -12673,6 +12911,10 @@ var purify = {exports: {}};
|
|
|
12673
12911
|
the application unsanitized. Refuse to sanitize such a root
|
|
12674
12912
|
the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
|
|
12675
12913
|
if (_isClobbered(dirty)) {
|
|
12914
|
+
/* Fail closed on a live clobbered root before throwing.
|
|
12915
|
+
_neutralizeRoot's reads are clobber-safe (cached getters); the
|
|
12916
|
+
form's non-clobbered descendants, e.g. an armed <img>, are scrubbed. */
|
|
12917
|
+
_neutralizeRoot(dirty);
|
|
12676
12918
|
throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
|
|
12677
12919
|
}
|
|
12678
12920
|
/* Sanitize attached shadow roots before the main iterator runs.
|
|
@@ -12725,20 +12967,24 @@ var purify = {exports: {}};
|
|
|
12725
12967
|
_forceRemove(body.firstChild);
|
|
12726
12968
|
}
|
|
12727
12969
|
/* Get node iterator */
|
|
12728
|
-
const
|
|
12970
|
+
const walkRoot = inPlace ? dirty : body;
|
|
12729
12971
|
/* Now start iterating over the created document.
|
|
12730
12972
|
The walk runs inside an exception barrier (campaign-3 F2): a re-entrant
|
|
12731
12973
|
engine/custom-element mutation can detach a node mid-walk so
|
|
12732
12974
|
`_forceRemove`'s parentless guard throws, aborting the loop. Without the
|
|
12733
12975
|
barrier the caller's in-place tree would be left half-sanitized with the
|
|
12734
|
-
unvisited tail still armed.
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12976
|
+
unvisited tail still armed. _createNodeIterator itself is inside the
|
|
12977
|
+
barrier too: constructing the iterator dereferences the root's document,
|
|
12978
|
+
and any failure there (e.g. an exotic/clobbered root) must still fail
|
|
12979
|
+
closed rather than skip the neutralize. On any throw we fail closed -
|
|
12980
|
+
strip the in-place root bare - then rethrow so the existing throw
|
|
12981
|
+
contract is preserved. (String/DOM-copy paths never return the partial
|
|
12982
|
+
body, so the propagating throw is already fail-closed there.) */
|
|
12738
12983
|
try {
|
|
12984
|
+
const nodeIterator = _createNodeIterator(walkRoot);
|
|
12739
12985
|
while (currentNode = nodeIterator.nextNode()) {
|
|
12740
12986
|
/* Sanitize tags and elements */
|
|
12741
|
-
_sanitizeElements(currentNode);
|
|
12987
|
+
_sanitizeElements(currentNode, walkRoot);
|
|
12742
12988
|
/* Check attributes next */
|
|
12743
12989
|
_sanitizeAttributes(currentNode);
|
|
12744
12990
|
/* Shadow DOM detected, sanitize it.
|
|
@@ -12752,6 +12998,14 @@ var purify = {exports: {}};
|
|
|
12752
12998
|
} catch (error) {
|
|
12753
12999
|
if (inPlace) {
|
|
12754
13000
|
_neutralizeRoot(dirty);
|
|
13001
|
+
/* Nodes _forceRemove'd earlier in the aborted walk are already
|
|
13002
|
+
detached from the root, so _neutralizeRoot's subtree pass does not
|
|
13003
|
+
reach them. Defuse them too, mirroring the success-path loop below. */
|
|
13004
|
+
arrayForEach(DOMPurify.removed, entry => {
|
|
13005
|
+
if (entry.element) {
|
|
13006
|
+
_neutralizeSubtree(entry.element);
|
|
13007
|
+
}
|
|
13008
|
+
});
|
|
12755
13009
|
}
|
|
12756
13010
|
throw error;
|
|
12757
13011
|
}
|
|
@@ -14217,11 +14471,14 @@ class NavigationButton {
|
|
|
14217
14471
|
*/
|
|
14218
14472
|
this.hostMutationCallback = (mutationList) => {
|
|
14219
14473
|
mutationList.forEach(({ attributeName }) => {
|
|
14220
|
-
if (attributeName) {
|
|
14474
|
+
if (attributeName && MUTABLE_ATTRIBUTES$2.includes(attributeName)) {
|
|
14221
14475
|
const attribute = this.el.getAttribute(attributeName);
|
|
14222
|
-
if (attribute
|
|
14476
|
+
if (attribute) {
|
|
14223
14477
|
this.inheritedAttributes[attributeName] = attribute;
|
|
14224
14478
|
}
|
|
14479
|
+
else {
|
|
14480
|
+
delete this.inheritedAttributes[attributeName];
|
|
14481
|
+
}
|
|
14225
14482
|
}
|
|
14226
14483
|
});
|
|
14227
14484
|
};
|
|
@@ -14285,11 +14542,11 @@ class NavigationButton {
|
|
|
14285
14542
|
target,
|
|
14286
14543
|
theme,
|
|
14287
14544
|
};
|
|
14288
|
-
return (hAsync(Host, { key: '
|
|
14545
|
+
return (hAsync(Host, { key: 'dd30f288d728c8e8dffd4108d05e0a02b782d9ea', class: {
|
|
14289
14546
|
"in-side-menu": isMenuMode,
|
|
14290
14547
|
[`ic-theme-${theme}`]: theme !== "inherit",
|
|
14291
14548
|
dark: parentThemeDark,
|
|
14292
|
-
} }, hAsync("ic-button", Object.assign({ key: '
|
|
14549
|
+
} }, hAsync("ic-button", Object.assign({ key: 'b59e4538abe478aa21617a1ce3347668c3859e40', ref: (el) => (this.buttonEl = el), class: { "popout-menu-button": isMenuMode }, "aria-label": !isMenuMode ? label : null, disableTooltip: isMenuMode, fullWidth: isMenuMode, monochrome: !isMenuMode, size: isMenuMode ? "medium" : "large", variant: isMenuMode ? "tertiary" : "icon-tertiary" }, buttonProps, inheritedAttributes), isMenuMode ? label : "", hAsync("slot", { key: '4ca3226dd11288f30f595d8b0fa8484838b527c4', slot: "left-icon", name: "icon" }), isSlotUsed(this.el, "badge") && hAsync("slot", { key: '89408c559f0b62b9f92686bbc14a5312f8e6c2c6', name: "badge" }))));
|
|
14293
14550
|
}
|
|
14294
14551
|
static get delegatesFocus() { return true; }
|
|
14295
14552
|
get el() { return getElement(this); }
|