igv 3.8.0 → 3.8.1
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/README.md +18 -10
- package/dist/igv.esm.js +518 -165
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +518 -165
- package/dist/igv.min.js +9 -9
- package/dist/igv.min.js.map +1 -1
- package/package.json +1 -1
package/dist/igv.js
CHANGED
|
@@ -10612,24 +10612,65 @@
|
|
|
10612
10612
|
return 1 === unique.length
|
|
10613
10613
|
}
|
|
10614
10614
|
|
|
10615
|
-
/*! @license DOMPurify 3.
|
|
10615
|
+
/*! @license DOMPurify 3.4.7 | (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.7/LICENSE */
|
|
10616
10616
|
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10617
|
+
function _arrayLikeToArray(r, a) {
|
|
10618
|
+
(null == a || a > r.length) && (a = r.length);
|
|
10619
|
+
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
|
|
10620
|
+
return n;
|
|
10621
|
+
}
|
|
10622
|
+
function _arrayWithHoles(r) {
|
|
10623
|
+
if (Array.isArray(r)) return r;
|
|
10624
|
+
}
|
|
10625
|
+
function _iterableToArrayLimit(r, l) {
|
|
10626
|
+
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
10627
|
+
if (null != t) {
|
|
10628
|
+
var e,
|
|
10629
|
+
n,
|
|
10630
|
+
i,
|
|
10631
|
+
u,
|
|
10632
|
+
a = [],
|
|
10633
|
+
f = true,
|
|
10634
|
+
o = false;
|
|
10635
|
+
try {
|
|
10636
|
+
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
|
|
10637
|
+
} catch (r) {
|
|
10638
|
+
o = true, n = r;
|
|
10639
|
+
} finally {
|
|
10640
|
+
try {
|
|
10641
|
+
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
|
|
10642
|
+
} finally {
|
|
10643
|
+
if (o) throw n;
|
|
10644
|
+
}
|
|
10645
|
+
}
|
|
10646
|
+
return a;
|
|
10647
|
+
}
|
|
10648
|
+
}
|
|
10649
|
+
function _nonIterableRest() {
|
|
10650
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
10651
|
+
}
|
|
10652
|
+
function _slicedToArray(r, e) {
|
|
10653
|
+
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
|
|
10654
|
+
}
|
|
10655
|
+
function _unsupportedIterableToArray(r, a) {
|
|
10656
|
+
if (r) {
|
|
10657
|
+
if ("string" == typeof r) return _arrayLikeToArray(r, a);
|
|
10658
|
+
var t = {}.toString.call(r).slice(8, -1);
|
|
10659
|
+
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;
|
|
10660
|
+
}
|
|
10661
|
+
}
|
|
10662
|
+
|
|
10663
|
+
const entries = Object.entries,
|
|
10664
|
+
setPrototypeOf = Object.setPrototypeOf,
|
|
10665
|
+
isFrozen = Object.isFrozen,
|
|
10666
|
+
getPrototypeOf = Object.getPrototypeOf,
|
|
10667
|
+
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
10668
|
+
let freeze = Object.freeze,
|
|
10669
|
+
seal = Object.seal,
|
|
10670
|
+
create = Object.create; // eslint-disable-line import/no-mutable-exports
|
|
10671
|
+
let _ref = typeof Reflect !== 'undefined' && Reflect,
|
|
10672
|
+
apply = _ref.apply,
|
|
10673
|
+
construct = _ref.construct;
|
|
10633
10674
|
if (!freeze) {
|
|
10634
10675
|
freeze = function freeze(x) {
|
|
10635
10676
|
return x;
|
|
@@ -10661,13 +10702,19 @@
|
|
|
10661
10702
|
const arrayPop = unapply(Array.prototype.pop);
|
|
10662
10703
|
const arrayPush = unapply(Array.prototype.push);
|
|
10663
10704
|
const arraySplice = unapply(Array.prototype.splice);
|
|
10705
|
+
const arrayIsArray = Array.isArray;
|
|
10664
10706
|
const stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
10665
10707
|
const stringToString = unapply(String.prototype.toString);
|
|
10666
10708
|
const stringMatch = unapply(String.prototype.match);
|
|
10667
10709
|
const stringReplace = unapply(String.prototype.replace);
|
|
10668
10710
|
const stringIndexOf = unapply(String.prototype.indexOf);
|
|
10669
10711
|
const stringTrim = unapply(String.prototype.trim);
|
|
10712
|
+
const numberToString = unapply(Number.prototype.toString);
|
|
10713
|
+
const booleanToString = unapply(Boolean.prototype.toString);
|
|
10714
|
+
const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
|
|
10715
|
+
const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
|
|
10670
10716
|
const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
|
|
10717
|
+
const objectToString = unapply(Object.prototype.toString);
|
|
10671
10718
|
const regExpTest = unapply(RegExp.prototype.test);
|
|
10672
10719
|
const typeErrorCreate = unconstruct(TypeError);
|
|
10673
10720
|
/**
|
|
@@ -10717,6 +10764,9 @@
|
|
|
10717
10764
|
// Prevent prototype setters from intercepting set as a this value.
|
|
10718
10765
|
setPrototypeOf(set, null);
|
|
10719
10766
|
}
|
|
10767
|
+
if (!arrayIsArray(array)) {
|
|
10768
|
+
return set;
|
|
10769
|
+
}
|
|
10720
10770
|
let l = array.length;
|
|
10721
10771
|
while (l--) {
|
|
10722
10772
|
let element = array[l];
|
|
@@ -10757,10 +10807,13 @@
|
|
|
10757
10807
|
*/
|
|
10758
10808
|
function clone(object) {
|
|
10759
10809
|
const newObject = create(null);
|
|
10760
|
-
for (const
|
|
10810
|
+
for (const _ref2 of entries(object)) {
|
|
10811
|
+
var _ref3 = _slicedToArray(_ref2, 2);
|
|
10812
|
+
const property = _ref3[0];
|
|
10813
|
+
const value = _ref3[1];
|
|
10761
10814
|
const isPropertyExist = objectHasOwnProperty(object, property);
|
|
10762
10815
|
if (isPropertyExist) {
|
|
10763
|
-
if (
|
|
10816
|
+
if (arrayIsArray(value)) {
|
|
10764
10817
|
newObject[property] = cleanArray(value);
|
|
10765
10818
|
} else if (value && typeof value === 'object' && value.constructor === Object) {
|
|
10766
10819
|
newObject[property] = clone(value);
|
|
@@ -10771,6 +10824,58 @@
|
|
|
10771
10824
|
}
|
|
10772
10825
|
return newObject;
|
|
10773
10826
|
}
|
|
10827
|
+
/**
|
|
10828
|
+
* Convert non-node values into strings without depending on direct property access.
|
|
10829
|
+
*
|
|
10830
|
+
* @param value - The value to stringify.
|
|
10831
|
+
* @returns A string representation of the provided value.
|
|
10832
|
+
*/
|
|
10833
|
+
function stringifyValue(value) {
|
|
10834
|
+
switch (typeof value) {
|
|
10835
|
+
case 'string':
|
|
10836
|
+
{
|
|
10837
|
+
return value;
|
|
10838
|
+
}
|
|
10839
|
+
case 'number':
|
|
10840
|
+
{
|
|
10841
|
+
return numberToString(value);
|
|
10842
|
+
}
|
|
10843
|
+
case 'boolean':
|
|
10844
|
+
{
|
|
10845
|
+
return booleanToString(value);
|
|
10846
|
+
}
|
|
10847
|
+
case 'bigint':
|
|
10848
|
+
{
|
|
10849
|
+
return bigintToString ? bigintToString(value) : '0';
|
|
10850
|
+
}
|
|
10851
|
+
case 'symbol':
|
|
10852
|
+
{
|
|
10853
|
+
return symbolToString ? symbolToString(value) : 'Symbol()';
|
|
10854
|
+
}
|
|
10855
|
+
case 'undefined':
|
|
10856
|
+
{
|
|
10857
|
+
return objectToString(value);
|
|
10858
|
+
}
|
|
10859
|
+
case 'function':
|
|
10860
|
+
case 'object':
|
|
10861
|
+
{
|
|
10862
|
+
if (value === null) {
|
|
10863
|
+
return objectToString(value);
|
|
10864
|
+
}
|
|
10865
|
+
const valueAsRecord = value;
|
|
10866
|
+
const valueToString = lookupGetter(valueAsRecord, 'toString');
|
|
10867
|
+
if (typeof valueToString === 'function') {
|
|
10868
|
+
const stringified = valueToString(valueAsRecord);
|
|
10869
|
+
return typeof stringified === 'string' ? stringified : objectToString(stringified);
|
|
10870
|
+
}
|
|
10871
|
+
return objectToString(value);
|
|
10872
|
+
}
|
|
10873
|
+
default:
|
|
10874
|
+
{
|
|
10875
|
+
return objectToString(value);
|
|
10876
|
+
}
|
|
10877
|
+
}
|
|
10878
|
+
}
|
|
10774
10879
|
/**
|
|
10775
10880
|
* This method automatically checks if the prop is function or getter and behaves accordingly.
|
|
10776
10881
|
*
|
|
@@ -10796,6 +10901,14 @@
|
|
|
10796
10901
|
}
|
|
10797
10902
|
return fallbackValue;
|
|
10798
10903
|
}
|
|
10904
|
+
function isRegex(value) {
|
|
10905
|
+
try {
|
|
10906
|
+
regExpTest(value, '');
|
|
10907
|
+
return true;
|
|
10908
|
+
} catch (_unused) {
|
|
10909
|
+
return false;
|
|
10910
|
+
}
|
|
10911
|
+
}
|
|
10799
10912
|
|
|
10800
10913
|
const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
|
|
10801
10914
|
const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
|
|
@@ -10811,15 +10924,14 @@
|
|
|
10811
10924
|
const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
10812
10925
|
const text = freeze(['#text']);
|
|
10813
10926
|
|
|
10814
|
-
const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', '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'
|
|
10927
|
+
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']);
|
|
10815
10928
|
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']);
|
|
10816
|
-
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', '
|
|
10929
|
+
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']);
|
|
10817
10930
|
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
10818
10931
|
|
|
10819
|
-
|
|
10820
|
-
const
|
|
10821
|
-
const
|
|
10822
|
-
const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
|
|
10932
|
+
const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
|
|
10933
|
+
const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
|
|
10934
|
+
const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
|
|
10823
10935
|
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
|
|
10824
10936
|
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
10825
10937
|
const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
@@ -10830,20 +10942,6 @@
|
|
|
10830
10942
|
const DOCTYPE_NAME = seal(/^html$/i);
|
|
10831
10943
|
const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
10832
10944
|
|
|
10833
|
-
var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
10834
|
-
__proto__: null,
|
|
10835
|
-
ARIA_ATTR: ARIA_ATTR,
|
|
10836
|
-
ATTR_WHITESPACE: ATTR_WHITESPACE,
|
|
10837
|
-
CUSTOM_ELEMENT: CUSTOM_ELEMENT,
|
|
10838
|
-
DATA_ATTR: DATA_ATTR,
|
|
10839
|
-
DOCTYPE_NAME: DOCTYPE_NAME,
|
|
10840
|
-
ERB_EXPR: ERB_EXPR,
|
|
10841
|
-
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
10842
|
-
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
10843
|
-
MUSTACHE_EXPR: MUSTACHE_EXPR,
|
|
10844
|
-
TMPLIT_EXPR: TMPLIT_EXPR
|
|
10845
|
-
});
|
|
10846
|
-
|
|
10847
10945
|
/* eslint-disable @typescript-eslint/indent */
|
|
10848
10946
|
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
|
10849
10947
|
const NODE_TYPE = {
|
|
@@ -10919,7 +11017,7 @@
|
|
|
10919
11017
|
function createDOMPurify() {
|
|
10920
11018
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
10921
11019
|
const DOMPurify = root => createDOMPurify(root);
|
|
10922
|
-
DOMPurify.version = '3.
|
|
11020
|
+
DOMPurify.version = '3.4.7';
|
|
10923
11021
|
DOMPurify.removed = [];
|
|
10924
11022
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
10925
11023
|
// Not running in a browser, provide a factory function
|
|
@@ -10927,28 +11025,29 @@
|
|
|
10927
11025
|
DOMPurify.isSupported = false;
|
|
10928
11026
|
return DOMPurify;
|
|
10929
11027
|
}
|
|
10930
|
-
let
|
|
10931
|
-
document
|
|
10932
|
-
} = window;
|
|
11028
|
+
let document = window.document;
|
|
10933
11029
|
const originalDocument = document;
|
|
10934
11030
|
const currentScript = originalDocument.currentScript;
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
NamedNodeMap
|
|
10942
|
-
HTMLFormElement
|
|
10943
|
-
DOMParser,
|
|
10944
|
-
trustedTypes
|
|
10945
|
-
} = window;
|
|
11031
|
+
window.DocumentFragment;
|
|
11032
|
+
const HTMLTemplateElement = window.HTMLTemplateElement,
|
|
11033
|
+
Node = window.Node,
|
|
11034
|
+
Element = window.Element,
|
|
11035
|
+
NodeFilter = window.NodeFilter,
|
|
11036
|
+
_window$NamedNodeMap = window.NamedNodeMap;
|
|
11037
|
+
_window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;
|
|
11038
|
+
window.HTMLFormElement;
|
|
11039
|
+
const DOMParser = window.DOMParser,
|
|
11040
|
+
trustedTypes = window.trustedTypes;
|
|
10946
11041
|
const ElementPrototype = Element.prototype;
|
|
10947
11042
|
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
10948
11043
|
const remove = lookupGetter(ElementPrototype, 'remove');
|
|
10949
11044
|
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
10950
11045
|
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
10951
11046
|
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
11047
|
+
const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');
|
|
11048
|
+
const getAttributes = lookupGetter(ElementPrototype, 'attributes');
|
|
11049
|
+
const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
|
|
11050
|
+
const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
|
|
10952
11051
|
// As per issue #47, the web-components registry is inherited by a
|
|
10953
11052
|
// new document created via createHTMLDocument. As per the spec
|
|
10954
11053
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
@@ -10963,33 +11062,26 @@
|
|
|
10963
11062
|
}
|
|
10964
11063
|
let trustedTypesPolicy;
|
|
10965
11064
|
let emptyHTML = '';
|
|
10966
|
-
const
|
|
10967
|
-
implementation,
|
|
10968
|
-
createNodeIterator,
|
|
10969
|
-
createDocumentFragment,
|
|
10970
|
-
getElementsByTagName
|
|
10971
|
-
|
|
10972
|
-
const {
|
|
10973
|
-
importNode
|
|
10974
|
-
} = originalDocument;
|
|
11065
|
+
const _document = document,
|
|
11066
|
+
implementation = _document.implementation,
|
|
11067
|
+
createNodeIterator = _document.createNodeIterator,
|
|
11068
|
+
createDocumentFragment = _document.createDocumentFragment,
|
|
11069
|
+
getElementsByTagName = _document.getElementsByTagName;
|
|
11070
|
+
const importNode = originalDocument.importNode;
|
|
10975
11071
|
let hooks = _createHooksMap();
|
|
10976
11072
|
/**
|
|
10977
11073
|
* Expose whether this browser supports running the full DOMPurify.
|
|
10978
11074
|
*/
|
|
10979
11075
|
DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
|
|
10980
|
-
const
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
} = EXPRESSIONS;
|
|
10990
|
-
let {
|
|
10991
|
-
IS_ALLOWED_URI: IS_ALLOWED_URI$1
|
|
10992
|
-
} = EXPRESSIONS;
|
|
11076
|
+
const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
|
|
11077
|
+
ERB_EXPR$1 = ERB_EXPR,
|
|
11078
|
+
TMPLIT_EXPR$1 = TMPLIT_EXPR,
|
|
11079
|
+
DATA_ATTR$1 = DATA_ATTR,
|
|
11080
|
+
ARIA_ATTR$1 = ARIA_ATTR,
|
|
11081
|
+
IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
|
|
11082
|
+
ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
|
|
11083
|
+
CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
|
|
11084
|
+
let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
|
|
10993
11085
|
/**
|
|
10994
11086
|
* We consider the elements and attributes below to be safe. Ideally
|
|
10995
11087
|
* don't add any new ones but feel free to remove unwanted ones.
|
|
@@ -11167,15 +11259,15 @@
|
|
|
11167
11259
|
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
11168
11260
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
11169
11261
|
/* Set configuration parameters */
|
|
11170
|
-
ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
11171
|
-
ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
11172
|
-
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
11173
|
-
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
11174
|
-
DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
|
|
11175
|
-
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
11176
|
-
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
|
|
11177
|
-
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
|
|
11178
|
-
USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
|
|
11262
|
+
ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
11263
|
+
ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
11264
|
+
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
11265
|
+
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
11266
|
+
DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
|
|
11267
|
+
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
11268
|
+
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
|
|
11269
|
+
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
|
|
11270
|
+
USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
|
|
11179
11271
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
11180
11272
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
11181
11273
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
@@ -11191,19 +11283,20 @@
|
|
|
11191
11283
|
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
11192
11284
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
11193
11285
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
11194
|
-
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP
|
|
11195
|
-
NAMESPACE = cfg.NAMESPACE
|
|
11196
|
-
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS
|
|
11197
|
-
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11286
|
+
IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
|
|
11287
|
+
NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
|
|
11288
|
+
MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map
|
|
11289
|
+
HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map
|
|
11290
|
+
const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
|
|
11291
|
+
CUSTOM_ELEMENT_HANDLING = create(null);
|
|
11292
|
+
if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
|
|
11293
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
|
|
11201
11294
|
}
|
|
11202
|
-
if (
|
|
11203
|
-
CUSTOM_ELEMENT_HANDLING.attributeNameCheck =
|
|
11295
|
+
if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
|
|
11296
|
+
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
|
|
11204
11297
|
}
|
|
11205
|
-
if (
|
|
11206
|
-
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements =
|
|
11298
|
+
if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
|
|
11299
|
+
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
|
|
11207
11300
|
}
|
|
11208
11301
|
if (SAFE_FOR_TEMPLATES) {
|
|
11209
11302
|
ALLOW_DATA_ATTR = false;
|
|
@@ -11235,44 +11328,41 @@
|
|
|
11235
11328
|
addToSet(ALLOWED_ATTR, xml);
|
|
11236
11329
|
}
|
|
11237
11330
|
}
|
|
11238
|
-
/*
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
|
|
11243
|
-
EXTRA_ELEMENT_HANDLING.attributeCheck = null;
|
|
11244
|
-
}
|
|
11331
|
+
/* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
|
|
11332
|
+
* leaking across calls when switching from function to array config */
|
|
11333
|
+
EXTRA_ELEMENT_HANDLING.tagCheck = null;
|
|
11334
|
+
EXTRA_ELEMENT_HANDLING.attributeCheck = null;
|
|
11245
11335
|
/* Merge configuration parameters */
|
|
11246
|
-
if (cfg
|
|
11336
|
+
if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
|
|
11247
11337
|
if (typeof cfg.ADD_TAGS === 'function') {
|
|
11248
11338
|
EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
|
|
11249
|
-
} else {
|
|
11339
|
+
} else if (arrayIsArray(cfg.ADD_TAGS)) {
|
|
11250
11340
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
11251
11341
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
11252
11342
|
}
|
|
11253
11343
|
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
11254
11344
|
}
|
|
11255
11345
|
}
|
|
11256
|
-
if (cfg
|
|
11346
|
+
if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
|
|
11257
11347
|
if (typeof cfg.ADD_ATTR === 'function') {
|
|
11258
11348
|
EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
|
|
11259
|
-
} else {
|
|
11349
|
+
} else if (arrayIsArray(cfg.ADD_ATTR)) {
|
|
11260
11350
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
11261
11351
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
11262
11352
|
}
|
|
11263
11353
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
11264
11354
|
}
|
|
11265
11355
|
}
|
|
11266
|
-
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
11356
|
+
if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
|
|
11267
11357
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
11268
11358
|
}
|
|
11269
|
-
if (cfg.FORBID_CONTENTS) {
|
|
11359
|
+
if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
|
|
11270
11360
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
11271
11361
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
11272
11362
|
}
|
|
11273
11363
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
11274
11364
|
}
|
|
11275
|
-
if (cfg.ADD_FORBID_CONTENTS) {
|
|
11365
|
+
if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
|
|
11276
11366
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
11277
11367
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
11278
11368
|
}
|
|
@@ -11312,6 +11402,21 @@
|
|
|
11312
11402
|
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
11313
11403
|
}
|
|
11314
11404
|
}
|
|
11405
|
+
/*
|
|
11406
|
+
* Mirror the clone-before-mutate pattern already applied above for
|
|
11407
|
+
* cfg.ADD_TAGS / cfg.ADD_ATTR: if any uponSanitize* hook is
|
|
11408
|
+
* registered AND the set still points at the default constant,
|
|
11409
|
+
* clone it. The hook then mutates the clone (in-call widening
|
|
11410
|
+
* still works exactly as documented) and the next default-cfg
|
|
11411
|
+
* call rebinds to the untouched original via the reassignment at
|
|
11412
|
+
* the top of this function.
|
|
11413
|
+
*/
|
|
11414
|
+
if ((hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) && ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
11415
|
+
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
11416
|
+
}
|
|
11417
|
+
if (hooks.uponSanitizeAttribute.length > 0 && ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
11418
|
+
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
11419
|
+
}
|
|
11315
11420
|
// Prevent further manipulation of configuration.
|
|
11316
11421
|
// Not available in IE8, Safari 5, etc.
|
|
11317
11422
|
if (freeze) {
|
|
@@ -11511,23 +11616,132 @@
|
|
|
11511
11616
|
// eslint-disable-next-line no-bitwise
|
|
11512
11617
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
|
|
11513
11618
|
};
|
|
11619
|
+
/**
|
|
11620
|
+
* Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
|
|
11621
|
+
* character data of an element subtree. Used as the final safety net for
|
|
11622
|
+
* SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
|
|
11623
|
+
* which only form after text-node normalization (e.g. fragments split across
|
|
11624
|
+
* stripped elements) cannot survive into a template-evaluating framework.
|
|
11625
|
+
*
|
|
11626
|
+
* Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
|
|
11627
|
+
* in place rather than round-tripping through innerHTML. This preserves
|
|
11628
|
+
* descendant node references (important for IN_PLACE callers), avoids a
|
|
11629
|
+
* serialize/reparse cycle, and reads literal character data — which means
|
|
11630
|
+
* `<%...%>` in text content matches the ERB regex against its real bytes
|
|
11631
|
+
* instead of the HTML-entity-escaped form innerHTML would produce.
|
|
11632
|
+
*
|
|
11633
|
+
* Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
|
|
11634
|
+
* attributes is performed during the per-node `_sanitizeAttributes` pass.
|
|
11635
|
+
*
|
|
11636
|
+
* @param node The root element whose character data should be scrubbed.
|
|
11637
|
+
*/
|
|
11638
|
+
const _scrubTemplateExpressions = function _scrubTemplateExpressions(node) {
|
|
11639
|
+
node.normalize();
|
|
11640
|
+
const walker = createNodeIterator.call(node.ownerDocument || node, node,
|
|
11641
|
+
// eslint-disable-next-line no-bitwise
|
|
11642
|
+
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
|
|
11643
|
+
let currentNode = walker.nextNode();
|
|
11644
|
+
while (currentNode) {
|
|
11645
|
+
let data = currentNode.data;
|
|
11646
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
11647
|
+
data = stringReplace(data, expr, ' ');
|
|
11648
|
+
});
|
|
11649
|
+
currentNode.data = data;
|
|
11650
|
+
currentNode = walker.nextNode();
|
|
11651
|
+
}
|
|
11652
|
+
};
|
|
11514
11653
|
/**
|
|
11515
11654
|
* _isClobbered
|
|
11516
11655
|
*
|
|
11656
|
+
* Detect DOM-clobbering on HTMLFormElement nodes. Form is the only HTML
|
|
11657
|
+
* interface with [LegacyOverrideBuiltIns]; a descendant element with a
|
|
11658
|
+
* `name` attribute matching a prototype property shadows that property
|
|
11659
|
+
* on direct reads. We use this check at the IN_PLACE entry-point and
|
|
11660
|
+
* during attribute sanitization to refuse clobbered forms.
|
|
11661
|
+
*
|
|
11517
11662
|
* @param element element to check for clobbering attacks
|
|
11518
11663
|
* @return true if clobbered, false if safe
|
|
11519
11664
|
*/
|
|
11520
11665
|
const _isClobbered = function _isClobbered(element) {
|
|
11521
|
-
|
|
11666
|
+
// Realm-independent tag-name probe. If we can't determine the tag
|
|
11667
|
+
// name at all, we can't reason about clobbering — return false
|
|
11668
|
+
// (the caller's other defences still apply).
|
|
11669
|
+
const realTagName = getNodeName ? getNodeName(element) : null;
|
|
11670
|
+
if (typeof realTagName !== 'string') {
|
|
11671
|
+
return false;
|
|
11672
|
+
}
|
|
11673
|
+
if (transformCaseFunc(realTagName) !== 'form') {
|
|
11674
|
+
return false;
|
|
11675
|
+
}
|
|
11676
|
+
return typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' ||
|
|
11677
|
+
// Realm-safe NamedNodeMap detection: equality against the cached
|
|
11678
|
+
// prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
|
|
11679
|
+
// makes the direct read diverge from the cached read; a clean form
|
|
11680
|
+
// (same-realm OR foreign-realm) has both reads pointing at the same
|
|
11681
|
+
// canonical NamedNodeMap.
|
|
11682
|
+
element.attributes !== getAttributes(element) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function' ||
|
|
11683
|
+
// NodeType clobbering probe. Cached Node.prototype.nodeType getter
|
|
11684
|
+
// returns the integer 1 for any Element regardless of realm; direct
|
|
11685
|
+
// read on a clobbered form (e.g. <input name="nodeType">) returns
|
|
11686
|
+
// the named child element. Cheap addition — nodeType is read from
|
|
11687
|
+
// an internal slot, no serialization cost — and removes a residual
|
|
11688
|
+
// clobbering surface used by several mXSS / PI / comment branches
|
|
11689
|
+
// in _sanitizeElements that compare currentNode.nodeType directly.
|
|
11690
|
+
element.nodeType !== getNodeType(element) ||
|
|
11691
|
+
// HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
|
|
11692
|
+
// "childNodes" shadows the prototype getter. Direct reads of
|
|
11693
|
+
// form.childNodes from a clobbered form return the named child
|
|
11694
|
+
// instead of the real NodeList, so any walk that reads it directly
|
|
11695
|
+
// skips the form's real children. Compare the direct read to the
|
|
11696
|
+
// cached Node.prototype getter — when the form's named-property
|
|
11697
|
+
// getter intercepts the read, the two values differ and we flag
|
|
11698
|
+
// the form. This catches every clobbering child type (input,
|
|
11699
|
+
// select, etc.) regardless of whether the named child happens to
|
|
11700
|
+
// carry a numeric .length, which a typeof-based probe would miss
|
|
11701
|
+
// (e.g. HTMLSelectElement.length is a defined unsigned-long).
|
|
11702
|
+
element.childNodes !== getChildNodes(element);
|
|
11703
|
+
};
|
|
11704
|
+
/**
|
|
11705
|
+
* Checks whether the given value is a DocumentFragment from any realm.
|
|
11706
|
+
*
|
|
11707
|
+
* The realm-independent replacement reads `nodeType` through the cached
|
|
11708
|
+
* Node.prototype getter and compares to the DOCUMENT_FRAGMENT_NODE
|
|
11709
|
+
* constant (11). nodeType is a numeric value resolved from the node's
|
|
11710
|
+
* internal slot, identical across realms for the same kind of node.
|
|
11711
|
+
*
|
|
11712
|
+
* @param value object to check
|
|
11713
|
+
* @return true if value is a DocumentFragment-shaped node from any realm
|
|
11714
|
+
*/
|
|
11715
|
+
const _isDocumentFragment = function _isDocumentFragment(value) {
|
|
11716
|
+
if (!getNodeType || typeof value !== 'object' || value === null) {
|
|
11717
|
+
return false;
|
|
11718
|
+
}
|
|
11719
|
+
try {
|
|
11720
|
+
return getNodeType(value) === NODE_TYPE.documentFragment;
|
|
11721
|
+
} catch (_) {
|
|
11722
|
+
return false;
|
|
11723
|
+
}
|
|
11522
11724
|
};
|
|
11523
11725
|
/**
|
|
11524
|
-
* Checks whether the given object is a DOM node
|
|
11726
|
+
* Checks whether the given object is a DOM node, including nodes that
|
|
11727
|
+
* originate from a different window/realm (e.g. an iframe's
|
|
11728
|
+
* contentDocument). The previous `value instanceof Node` check was
|
|
11729
|
+
* realm-bound: nodes from a different window failed it, causing
|
|
11730
|
+
* sanitize() to silently stringify them and reset IN_PLACE to false,
|
|
11731
|
+
* returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
|
|
11525
11732
|
*
|
|
11526
11733
|
* @param value object to check whether it's a DOM node
|
|
11527
|
-
* @return true
|
|
11734
|
+
* @return true if value is a DOM node from any realm
|
|
11528
11735
|
*/
|
|
11529
11736
|
const _isNode = function _isNode(value) {
|
|
11530
|
-
|
|
11737
|
+
if (!getNodeType || typeof value !== 'object' || value === null) {
|
|
11738
|
+
return false;
|
|
11739
|
+
}
|
|
11740
|
+
try {
|
|
11741
|
+
return typeof getNodeType(value) === 'number';
|
|
11742
|
+
} catch (_) {
|
|
11743
|
+
return false;
|
|
11744
|
+
}
|
|
11531
11745
|
};
|
|
11532
11746
|
function _executeHooks(hooks, currentNode, data) {
|
|
11533
11747
|
arrayForEach(hooks, hook => {
|
|
@@ -11564,6 +11778,11 @@
|
|
|
11564
11778
|
_forceRemove(currentNode);
|
|
11565
11779
|
return true;
|
|
11566
11780
|
}
|
|
11781
|
+
/* Remove risky CSS construction leading to mXSS */
|
|
11782
|
+
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
|
|
11783
|
+
_forceRemove(currentNode);
|
|
11784
|
+
return true;
|
|
11785
|
+
}
|
|
11567
11786
|
/* Remove any occurrence of processing instructions */
|
|
11568
11787
|
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
|
|
11569
11788
|
_forceRemove(currentNode);
|
|
@@ -11575,7 +11794,7 @@
|
|
|
11575
11794
|
return true;
|
|
11576
11795
|
}
|
|
11577
11796
|
/* Remove element if anything forbids its presence */
|
|
11578
|
-
if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) &&
|
|
11797
|
+
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
|
|
11579
11798
|
/* Check if we have a custom element to handle */
|
|
11580
11799
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
11581
11800
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
@@ -11585,15 +11804,21 @@
|
|
|
11585
11804
|
return false;
|
|
11586
11805
|
}
|
|
11587
11806
|
}
|
|
11588
|
-
/* Keep content except for bad-listed elements
|
|
11807
|
+
/* Keep content except for bad-listed elements.
|
|
11808
|
+
Use the cached prototype getters exclusively — the previous code
|
|
11809
|
+
had `|| currentNode.parentNode` / `|| currentNode.childNodes`
|
|
11810
|
+
fallbacks, but the cached getters always return the canonical
|
|
11811
|
+
value (or null for a real parent-less node), so the fallback
|
|
11812
|
+
path was dead in safe cases and a clobbering surface in unsafe
|
|
11813
|
+
ones. Falsy cached results stay falsy; the `if (childNodes &&
|
|
11814
|
+
parentNode)` check already gates correctly. */
|
|
11589
11815
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
11590
|
-
const parentNode = getParentNode(currentNode)
|
|
11591
|
-
const childNodes = getChildNodes(currentNode)
|
|
11816
|
+
const parentNode = getParentNode(currentNode);
|
|
11817
|
+
const childNodes = getChildNodes(currentNode);
|
|
11592
11818
|
if (childNodes && parentNode) {
|
|
11593
11819
|
const childCount = childNodes.length;
|
|
11594
11820
|
for (let i = childCount - 1; i >= 0; --i) {
|
|
11595
11821
|
const childClone = cloneNode(childNodes[i], true);
|
|
11596
|
-
childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
|
|
11597
11822
|
parentNode.insertBefore(childClone, getNextSibling(currentNode));
|
|
11598
11823
|
}
|
|
11599
11824
|
}
|
|
@@ -11601,8 +11826,14 @@
|
|
|
11601
11826
|
_forceRemove(currentNode);
|
|
11602
11827
|
return true;
|
|
11603
11828
|
}
|
|
11604
|
-
/* Check whether element has a valid namespace
|
|
11605
|
-
|
|
11829
|
+
/* Check whether element has a valid namespace.
|
|
11830
|
+
Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
|
|
11831
|
+
nodeType getter rather than `instanceof Element`, which is realm-
|
|
11832
|
+
bound and short-circuits to false for any node minted in a different
|
|
11833
|
+
realm — letting a foreign-realm element with a forbidden namespace
|
|
11834
|
+
slip past the namespace check entirely. */
|
|
11835
|
+
const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
|
|
11836
|
+
if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
|
|
11606
11837
|
_forceRemove(currentNode);
|
|
11607
11838
|
return true;
|
|
11608
11839
|
}
|
|
@@ -11615,7 +11846,7 @@
|
|
|
11615
11846
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
|
|
11616
11847
|
/* Get the element's text content */
|
|
11617
11848
|
content = currentNode.textContent;
|
|
11618
|
-
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
11849
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
11619
11850
|
content = stringReplace(content, expr, ' ');
|
|
11620
11851
|
});
|
|
11621
11852
|
if (currentNode.textContent !== content) {
|
|
@@ -11647,11 +11878,12 @@
|
|
|
11647
11878
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
11648
11879
|
return false;
|
|
11649
11880
|
}
|
|
11881
|
+
const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
|
|
11650
11882
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
11651
11883
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
11652
11884
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
11653
11885
|
We don't need to check the value; it's always URI safe. */
|
|
11654
|
-
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (
|
|
11886
|
+
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted || FORBID_ATTR[lcName]) {
|
|
11655
11887
|
if (
|
|
11656
11888
|
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
11657
11889
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
@@ -11663,11 +11895,15 @@
|
|
|
11663
11895
|
return false;
|
|
11664
11896
|
}
|
|
11665
11897
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
11666
|
-
} else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
|
|
11898
|
+
} else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
|
|
11667
11899
|
return false;
|
|
11668
11900
|
} else ;
|
|
11669
11901
|
return true;
|
|
11670
11902
|
};
|
|
11903
|
+
/* Names the HTML spec reserves from valid-custom-element-name; these must
|
|
11904
|
+
* never be treated as basic custom elements even when a permissive
|
|
11905
|
+
* CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
|
|
11906
|
+
const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
|
|
11671
11907
|
/**
|
|
11672
11908
|
* _isBasicCustomElement
|
|
11673
11909
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
@@ -11677,7 +11913,7 @@
|
|
|
11677
11913
|
* @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
|
|
11678
11914
|
*/
|
|
11679
11915
|
const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
|
|
11680
|
-
return tagName
|
|
11916
|
+
return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
|
|
11681
11917
|
};
|
|
11682
11918
|
/**
|
|
11683
11919
|
* _sanitizeAttributes
|
|
@@ -11692,9 +11928,7 @@
|
|
|
11692
11928
|
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
11693
11929
|
/* Execute a hook if present */
|
|
11694
11930
|
_executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
|
|
11695
|
-
const
|
|
11696
|
-
attributes
|
|
11697
|
-
} = currentNode;
|
|
11931
|
+
const attributes = currentNode.attributes;
|
|
11698
11932
|
/* Check if we have attributes; if not we might have a text node */
|
|
11699
11933
|
if (!attributes || _isClobbered(currentNode)) {
|
|
11700
11934
|
return;
|
|
@@ -11710,11 +11944,9 @@
|
|
|
11710
11944
|
/* Go backwards over all attributes; safely remove bad ones */
|
|
11711
11945
|
while (l--) {
|
|
11712
11946
|
const attr = attributes[l];
|
|
11713
|
-
const
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
value: attrValue
|
|
11717
|
-
} = attr;
|
|
11947
|
+
const name = attr.name,
|
|
11948
|
+
namespaceURI = attr.namespaceURI,
|
|
11949
|
+
attrValue = attr.value;
|
|
11718
11950
|
const lcName = transformCaseFunc(name);
|
|
11719
11951
|
const initValue = attrValue;
|
|
11720
11952
|
let value = name === 'value' ? initValue : stringTrim(initValue);
|
|
@@ -11728,12 +11960,14 @@
|
|
|
11728
11960
|
/* Full DOM Clobbering protection via namespace isolation,
|
|
11729
11961
|
* Prefix id and name attributes with `user-content-`
|
|
11730
11962
|
*/
|
|
11731
|
-
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
11963
|
+
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
|
|
11732
11964
|
// Remove the attribute with this value
|
|
11733
11965
|
_removeAttribute(name, currentNode);
|
|
11734
11966
|
// Prefix the value and later re-create the attribute with the sanitized value
|
|
11735
11967
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
11736
11968
|
}
|
|
11969
|
+
// Else: already prefixed, leave the attribute alone — the prefix is
|
|
11970
|
+
// itself the clobbering protection, and re-applying it is incorrect.
|
|
11737
11971
|
/* Work around a security issue with comments inside attributes */
|
|
11738
11972
|
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
|
|
11739
11973
|
_removeAttribute(name, currentNode);
|
|
@@ -11760,7 +11994,7 @@
|
|
|
11760
11994
|
}
|
|
11761
11995
|
/* Sanitize attribute content to be template-safe */
|
|
11762
11996
|
if (SAFE_FOR_TEMPLATES) {
|
|
11763
|
-
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
11997
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
11764
11998
|
value = stringReplace(value, expr, ' ');
|
|
11765
11999
|
});
|
|
11766
12000
|
}
|
|
@@ -11814,7 +12048,7 @@
|
|
|
11814
12048
|
*
|
|
11815
12049
|
* @param fragment to iterate over recursively
|
|
11816
12050
|
*/
|
|
11817
|
-
const
|
|
12051
|
+
const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
|
|
11818
12052
|
let shadowNode = null;
|
|
11819
12053
|
const shadowIterator = _createNodeIterator(fragment);
|
|
11820
12054
|
/* Execute a hook if present */
|
|
@@ -11826,14 +12060,98 @@
|
|
|
11826
12060
|
_sanitizeElements(shadowNode);
|
|
11827
12061
|
/* Check attributes next */
|
|
11828
12062
|
_sanitizeAttributes(shadowNode);
|
|
11829
|
-
/* Deep shadow DOM detected
|
|
11830
|
-
|
|
11831
|
-
|
|
12063
|
+
/* Deep shadow DOM detected.
|
|
12064
|
+
Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType against the
|
|
12065
|
+
DOCUMENT_FRAGMENT_NODE constant rather than instanceof, so we
|
|
12066
|
+
recurse into <template>.content from foreign realms too. */
|
|
12067
|
+
if (_isDocumentFragment(shadowNode.content)) {
|
|
12068
|
+
_sanitizeShadowDOM2(shadowNode.content);
|
|
12069
|
+
}
|
|
12070
|
+
/* An element iterated here may itself host an attached
|
|
12071
|
+
shadow root. The default NodeIterator does not enter shadow
|
|
12072
|
+
trees, so a shadow root nested inside template.content was
|
|
12073
|
+
previously reached by no walk at all (the pre-pass at
|
|
12074
|
+
_sanitizeAttachedShadowRoots descends via childNodes, which
|
|
12075
|
+
doesn't enter template.content; the template-content recursion
|
|
12076
|
+
above iterates the content but never inspected shadowRoot).
|
|
12077
|
+
Walk it explicitly. The nodeType guard avoids reading
|
|
12078
|
+
shadowRoot off text / comment / CDATA / PI nodes that the
|
|
12079
|
+
iterator also surfaces. */
|
|
12080
|
+
const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
|
|
12081
|
+
if (shadowNodeType === NODE_TYPE.element) {
|
|
12082
|
+
const innerSr = getShadowRoot ? getShadowRoot(shadowNode) : shadowNode.shadowRoot;
|
|
12083
|
+
if (_isDocumentFragment(innerSr)) {
|
|
12084
|
+
_sanitizeAttachedShadowRoots2(innerSr);
|
|
12085
|
+
_sanitizeShadowDOM2(innerSr);
|
|
12086
|
+
}
|
|
11832
12087
|
}
|
|
11833
12088
|
}
|
|
11834
12089
|
/* Execute a hook if present */
|
|
11835
12090
|
_executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
|
|
11836
12091
|
};
|
|
12092
|
+
/**
|
|
12093
|
+
* _sanitizeAttachedShadowRoots
|
|
12094
|
+
*
|
|
12095
|
+
* Walks `root` and feeds every attached shadow root we encounter into
|
|
12096
|
+
* the existing _sanitizeShadowDOM pipeline. The default node iterator
|
|
12097
|
+
* does not descend into shadow trees, so nodes inside an attached
|
|
12098
|
+
* shadow root would otherwise be skipped entirely.
|
|
12099
|
+
*
|
|
12100
|
+
* Two real input paths put attached shadow roots in front of us:
|
|
12101
|
+
* 1. IN_PLACE on a DOM node that already has shadow roots attached.
|
|
12102
|
+
* 2. DOM-node input where importNode(dirty, true) deep-clones the
|
|
12103
|
+
* shadow root because it was created with `clonable: true`.
|
|
12104
|
+
*
|
|
12105
|
+
* This pass runs once, up front, so the main iteration loop (and the
|
|
12106
|
+
* existing _sanitizeShadowDOM template-content recursion) stay
|
|
12107
|
+
* untouched — string-input paths are not affected.
|
|
12108
|
+
*
|
|
12109
|
+
* @param root the subtree root to walk for attached shadow roots
|
|
12110
|
+
*/
|
|
12111
|
+
const _sanitizeAttachedShadowRoots2 = function _sanitizeAttachedShadowRoots(root) {
|
|
12112
|
+
const nodeType = getNodeType ? getNodeType(root) : root.nodeType;
|
|
12113
|
+
if (nodeType === NODE_TYPE.element) {
|
|
12114
|
+
const sr = getShadowRoot ? getShadowRoot(root) : root.shadowRoot;
|
|
12115
|
+
// Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType-based
|
|
12116
|
+
// detection rather than `instanceof DocumentFragment`, which is
|
|
12117
|
+
// realm-bound and silently skipped shadow roots whose host element
|
|
12118
|
+
// belonged to a foreign realm (e.g. iframe.contentDocument
|
|
12119
|
+
// attachShadow). A foreign-realm ShadowRoot extends the foreign
|
|
12120
|
+
// realm's DocumentFragment, not ours, so the old instanceof check
|
|
12121
|
+
// returned false and the shadow subtree was never walked.
|
|
12122
|
+
if (_isDocumentFragment(sr)) {
|
|
12123
|
+
// Recurse first so that nested shadow roots are reached even if
|
|
12124
|
+
// _sanitizeShadowDOM removes hosts at this level.
|
|
12125
|
+
_sanitizeAttachedShadowRoots2(sr);
|
|
12126
|
+
_sanitizeShadowDOM2(sr);
|
|
12127
|
+
}
|
|
12128
|
+
}
|
|
12129
|
+
// Snapshot children before recursing. Sanitization of one subtree
|
|
12130
|
+
// (e.g. via an uponSanitizeShadowNode hook) may detach siblings,
|
|
12131
|
+
// and naive nextSibling traversal would silently skip the rest of
|
|
12132
|
+
// the list once a node is detached.
|
|
12133
|
+
const childNodes = getChildNodes ? getChildNodes(root) : root.childNodes;
|
|
12134
|
+
if (!childNodes) {
|
|
12135
|
+
return;
|
|
12136
|
+
}
|
|
12137
|
+
const snapshot = [];
|
|
12138
|
+
arrayForEach(childNodes, child => {
|
|
12139
|
+
arrayPush(snapshot, child);
|
|
12140
|
+
});
|
|
12141
|
+
for (const child of snapshot) {
|
|
12142
|
+
_sanitizeAttachedShadowRoots2(child);
|
|
12143
|
+
}
|
|
12144
|
+
/* When the root is a <template>, also descend into root.content */
|
|
12145
|
+
if (nodeType === NODE_TYPE.element) {
|
|
12146
|
+
const rootName = getNodeName ? getNodeName(root) : null;
|
|
12147
|
+
if (typeof rootName === 'string' && transformCaseFunc(rootName) === 'template') {
|
|
12148
|
+
const content = root.content;
|
|
12149
|
+
if (_isDocumentFragment(content)) {
|
|
12150
|
+
_sanitizeAttachedShadowRoots2(content);
|
|
12151
|
+
}
|
|
12152
|
+
}
|
|
12153
|
+
}
|
|
12154
|
+
};
|
|
11837
12155
|
// eslint-disable-next-line complexity
|
|
11838
12156
|
DOMPurify.sanitize = function (dirty) {
|
|
11839
12157
|
let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
@@ -11850,13 +12168,9 @@
|
|
|
11850
12168
|
}
|
|
11851
12169
|
/* Stringify, in case dirty is an object */
|
|
11852
12170
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
throw typeErrorCreate('dirty is not a string, aborting');
|
|
11857
|
-
}
|
|
11858
|
-
} else {
|
|
11859
|
-
throw typeErrorCreate('toString is not a function');
|
|
12171
|
+
dirty = stringifyValue(dirty);
|
|
12172
|
+
if (typeof dirty !== 'string') {
|
|
12173
|
+
throw typeErrorCreate('dirty is not a string, aborting');
|
|
11860
12174
|
}
|
|
11861
12175
|
}
|
|
11862
12176
|
/* Return dirty HTML if DOMPurify cannot run */
|
|
@@ -11874,14 +12188,35 @@
|
|
|
11874
12188
|
IN_PLACE = false;
|
|
11875
12189
|
}
|
|
11876
12190
|
if (IN_PLACE) {
|
|
11877
|
-
/* Do some early pre-sanitization to avoid unsafe root nodes
|
|
11878
|
-
|
|
11879
|
-
|
|
12191
|
+
/* Do some early pre-sanitization to avoid unsafe root nodes.
|
|
12192
|
+
Read nodeName through the cached prototype getter — a clobbering
|
|
12193
|
+
child named "nodeName" on the form root would otherwise shadow
|
|
12194
|
+
the property and let this check skip the root-allowlist
|
|
12195
|
+
validation entirely. */
|
|
12196
|
+
const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
|
|
12197
|
+
if (typeof nn === 'string') {
|
|
12198
|
+
const tagName = transformCaseFunc(nn);
|
|
11880
12199
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
11881
12200
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
11882
12201
|
}
|
|
11883
12202
|
}
|
|
11884
|
-
|
|
12203
|
+
/* Pre-flight the root through _isClobbered. The iterator-driven
|
|
12204
|
+
removal path can not detach a parent-less root: _forceRemove
|
|
12205
|
+
falls through to Element.prototype.remove(), which per spec
|
|
12206
|
+
is a no-op on a node with no parent. A clobbered root would
|
|
12207
|
+
then survive the main loop with its attributes uninspected,
|
|
12208
|
+
because _sanitizeAttributes early-returns on _isClobbered. The
|
|
12209
|
+
result would be an attacker-controlled form, complete with any
|
|
12210
|
+
event-handler attributes the caller passed in, handed back to
|
|
12211
|
+
the application unsanitized. Refuse to sanitize such a root
|
|
12212
|
+
the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
|
|
12213
|
+
if (_isClobbered(dirty)) {
|
|
12214
|
+
throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
|
|
12215
|
+
}
|
|
12216
|
+
/* Sanitize attached shadow roots before the main iterator runs.
|
|
12217
|
+
The iterator does not descend into shadow trees. */
|
|
12218
|
+
_sanitizeAttachedShadowRoots2(dirty);
|
|
12219
|
+
} else if (_isNode(dirty)) {
|
|
11885
12220
|
/* If dirty is a DOM element, append to an empty document to avoid
|
|
11886
12221
|
elements being stripped by the parser */
|
|
11887
12222
|
body = _initDocument('<!---->');
|
|
@@ -11895,6 +12230,12 @@
|
|
|
11895
12230
|
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
11896
12231
|
body.appendChild(importedNode);
|
|
11897
12232
|
}
|
|
12233
|
+
/* Clonable shadow roots are deep-cloned by importNode(); sanitize
|
|
12234
|
+
them before the main iterator runs, since the iterator does not
|
|
12235
|
+
descend into shadow trees. The walk routes every read through a
|
|
12236
|
+
cached prototype getter so clobbering descendants on a form root
|
|
12237
|
+
cannot hide a shadow host from this pass. */
|
|
12238
|
+
_sanitizeAttachedShadowRoots2(importedNode);
|
|
11898
12239
|
} else {
|
|
11899
12240
|
/* Exit directly if we have nothing to do */
|
|
11900
12241
|
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
@@ -11921,17 +12262,26 @@
|
|
|
11921
12262
|
_sanitizeElements(currentNode);
|
|
11922
12263
|
/* Check attributes next */
|
|
11923
12264
|
_sanitizeAttributes(currentNode);
|
|
11924
|
-
/* Shadow DOM detected, sanitize it
|
|
11925
|
-
|
|
11926
|
-
|
|
12265
|
+
/* Shadow DOM detected, sanitize it.
|
|
12266
|
+
Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
|
|
12267
|
+
instead of instanceof, so foreign-realm <template>.content is
|
|
12268
|
+
walked correctly. */
|
|
12269
|
+
if (_isDocumentFragment(currentNode.content)) {
|
|
12270
|
+
_sanitizeShadowDOM2(currentNode.content);
|
|
11927
12271
|
}
|
|
11928
12272
|
}
|
|
11929
12273
|
/* If we sanitized `dirty` in-place, return it. */
|
|
11930
12274
|
if (IN_PLACE) {
|
|
12275
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
12276
|
+
_scrubTemplateExpressions(dirty);
|
|
12277
|
+
}
|
|
11931
12278
|
return dirty;
|
|
11932
12279
|
}
|
|
11933
12280
|
/* Return sanitized string or DOM */
|
|
11934
12281
|
if (RETURN_DOM) {
|
|
12282
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
12283
|
+
_scrubTemplateExpressions(body);
|
|
12284
|
+
}
|
|
11935
12285
|
if (RETURN_DOM_FRAGMENT) {
|
|
11936
12286
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
11937
12287
|
while (body.firstChild) {
|
|
@@ -11960,7 +12310,7 @@
|
|
|
11960
12310
|
}
|
|
11961
12311
|
/* Sanitize final string template-safe */
|
|
11962
12312
|
if (SAFE_FOR_TEMPLATES) {
|
|
11963
|
-
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
12313
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
11964
12314
|
serializedHTML = stringReplace(serializedHTML, expr, ' ');
|
|
11965
12315
|
});
|
|
11966
12316
|
}
|
|
@@ -46232,6 +46582,7 @@
|
|
|
46232
46582
|
logScale: false,
|
|
46233
46583
|
windowFunction: 'mean',
|
|
46234
46584
|
graphType: 'bar',
|
|
46585
|
+
lineWidth: 2,
|
|
46235
46586
|
normalize: undefined,
|
|
46236
46587
|
scaleFactor: undefined,
|
|
46237
46588
|
overflowColor: `rgb(255, 32, 255)`,
|
|
@@ -46283,6 +46634,8 @@
|
|
|
46283
46634
|
if ("heatmap" === config.graphType && !config.height) {
|
|
46284
46635
|
this.height = 20;
|
|
46285
46636
|
}
|
|
46637
|
+
|
|
46638
|
+
this.lineWidth = config.lineWidth || WigTrack.defaults.lineWidth; // Set lineWidth from config
|
|
46286
46639
|
}
|
|
46287
46640
|
|
|
46288
46641
|
async postInit() {
|
|
@@ -46504,11 +46857,11 @@
|
|
|
46504
46857
|
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
46505
46858
|
|
|
46506
46859
|
if (this.graphType === "line") {
|
|
46860
|
+
const lineWidth = this.lineWidth;
|
|
46861
|
+
const properties = {"fillStyle": color, "strokeStyle": color, "lineWidth": lineWidth};
|
|
46862
|
+
|
|
46507
46863
|
if (lastY !== undefined) {
|
|
46508
|
-
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y,
|
|
46509
|
-
"fillStyle": color,
|
|
46510
|
-
"strokeStyle": color
|
|
46511
|
-
});
|
|
46864
|
+
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, properties);
|
|
46512
46865
|
}
|
|
46513
46866
|
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
46514
46867
|
} else if (this.graphType === "points") {
|
|
@@ -76707,7 +77060,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
76707
77060
|
})
|
|
76708
77061
|
}
|
|
76709
77062
|
|
|
76710
|
-
const _version = "3.8.
|
|
77063
|
+
const _version = "3.8.1";
|
|
76711
77064
|
function version() {
|
|
76712
77065
|
return _version
|
|
76713
77066
|
}
|
|
@@ -78334,11 +78687,11 @@ ${indent}columns: ${matrix.columns}
|
|
|
78334
78687
|
|
|
78335
78688
|
}
|
|
78336
78689
|
|
|
78337
|
-
function createRegionKey
|
|
78690
|
+
function createRegionKey(chr, start, end) {
|
|
78338
78691
|
return `${chr}-${start}-${end}`
|
|
78339
78692
|
}
|
|
78340
78693
|
|
|
78341
|
-
function parseRegionKey
|
|
78694
|
+
function parseRegionKey(regionKey) {
|
|
78342
78695
|
let regionParts = regionKey.split('-');
|
|
78343
78696
|
let ee = parseInt(regionParts.pop());
|
|
78344
78697
|
let ss = parseInt(regionParts.pop());
|
|
@@ -78368,7 +78721,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
78368
78721
|
const dom = div({ class: 'igv-roi-table-row' });
|
|
78369
78722
|
|
|
78370
78723
|
const { setName, feature } = record;
|
|
78371
|
-
dom.dataset.region = createRegionKey
|
|
78724
|
+
dom.dataset.region = createRegionKey(feature.chr, feature.start, feature.end);
|
|
78372
78725
|
|
|
78373
78726
|
let strings =
|
|
78374
78727
|
[
|
|
@@ -78440,7 +78793,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
78440
78793
|
|
|
78441
78794
|
const loci = [];
|
|
78442
78795
|
for (let el of selected) {
|
|
78443
|
-
const { locus } = parseRegionKey
|
|
78796
|
+
const { locus } = parseRegionKey(el.dataset.region);
|
|
78444
78797
|
loci.push(locus);
|
|
78445
78798
|
}
|
|
78446
78799
|
|
|
@@ -78494,7 +78847,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
78494
78847
|
const selected = this.tableDOM.querySelectorAll('.igv-roi-table-row-selected');
|
|
78495
78848
|
|
|
78496
78849
|
if (selected.length > 0 && selected.length < 2) {
|
|
78497
|
-
const { locus } = parseRegionKey
|
|
78850
|
+
const { locus } = parseRegionKey(selected[ 0 ].dataset.region);
|
|
78498
78851
|
const { chr, start, end } = parseLocusString(locus, this.browser.isSoftclipped());
|
|
78499
78852
|
enableButton(this.copySequenceButton, (end - start) < 1e6);
|
|
78500
78853
|
} else {
|
|
@@ -78543,7 +78896,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
78543
78896
|
const selected = this.tableDOM.querySelectorAll('.igv-roi-table-row-selected');
|
|
78544
78897
|
const loci = [];
|
|
78545
78898
|
for (let el of selected) {
|
|
78546
|
-
const { locus } = parseRegionKey
|
|
78899
|
+
const { locus } = parseRegionKey(el.dataset.region);
|
|
78547
78900
|
loci.push(locus);
|
|
78548
78901
|
}
|
|
78549
78902
|
|