@locker/html-sanitizer
Advanced tools
Comparing version 0.14.4 to 0.14.5
@@ -6,415 +6,248 @@ /*! | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
Object.defineProperty(exports, '__esModule', { | ||
value: true | ||
}); | ||
var shared = require('@locker/shared'); | ||
var sharedDom = require('@locker/shared-dom'); | ||
var DOMPurify = require('dompurify'); | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
function _interopDefaultLegacy(e) { | ||
return e && typeof e === 'object' && 'default' in e ? e : { | ||
'default': e | ||
}; | ||
} | ||
var DOMPurify__default = /*#__PURE__*/_interopDefaultLegacy(DOMPurify); | ||
const ariaAttributes = [ | ||
'aria-activedescendant', | ||
'aria-atomic', | ||
'aria-autocomplete', | ||
'aria-busy', | ||
'aria-checked', | ||
'aria-controls', | ||
'aria-describedby', | ||
'aria-disabled', | ||
'aria-readonly', | ||
'aria-dropeffect', | ||
'aria-expanded', | ||
'aria-flowto', | ||
'aria-grabbed', | ||
'aria-haspopup', | ||
'aria-hidden', | ||
'aria-disabled', | ||
'aria-invalid', | ||
'aria-label', | ||
'aria-labelledby', | ||
'aria-level', | ||
'aria-live', | ||
'aria-multiline', | ||
'aria-multiselectable', | ||
'aria-orientation', | ||
'aria-owns', | ||
'aria-posinset', | ||
'aria-pressed', | ||
'aria-readonly', | ||
'aria-relevant', | ||
'aria-required', | ||
'aria-selected', | ||
'aria-setsize', | ||
'aria-sort', | ||
'aria-valuemax', | ||
'aria-valuemin', | ||
'aria-valuenow', | ||
'aria-valuetext', | ||
'role', | ||
'target', | ||
]; | ||
const ariaAttributes = ['aria-activedescendant', 'aria-atomic', 'aria-autocomplete', 'aria-busy', 'aria-checked', 'aria-controls', 'aria-describedby', 'aria-disabled', 'aria-readonly', 'aria-dropeffect', 'aria-expanded', 'aria-flowto', 'aria-grabbed', 'aria-haspopup', 'aria-hidden', 'aria-disabled', 'aria-invalid', 'aria-label', 'aria-labelledby', 'aria-level', 'aria-live', 'aria-multiline', 'aria-multiselectable', 'aria-orientation', 'aria-owns', 'aria-posinset', 'aria-pressed', 'aria-readonly', 'aria-relevant', 'aria-required', 'aria-selected', 'aria-setsize', 'aria-sort', 'aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext', 'role', 'target']; | ||
const htmlTags = ['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'caption', 'canvas', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del', 'details', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'figure', 'figcaption', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li', 'map', 'mark', 'menu', 'meter', 'nav', 'ol', 'optgroup', 'option', 'output', 'p', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'small', 'source', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']; | ||
const svgTags = ['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'video', 'view', 'vkern', 'use']; | ||
const allTags = shared.ArrayConcat(svgTags, htmlTags); // generic, sanitizer attempts in place sanitization and returns node | ||
const htmlTags = [ | ||
'a', | ||
'abbr', | ||
'acronym', | ||
'address', | ||
'area', | ||
'article', | ||
'aside', | ||
'audio', | ||
'b', | ||
'bdi', | ||
'bdo', | ||
'big', | ||
'blockquote', | ||
'body', | ||
'br', | ||
'button', | ||
'caption', | ||
'canvas', | ||
'center', | ||
'cite', | ||
'code', | ||
'col', | ||
'colgroup', | ||
'command', | ||
'datalist', | ||
'dd', | ||
'del', | ||
'details', | ||
'dfn', | ||
'dir', | ||
'div', | ||
'dl', | ||
'dt', | ||
'em', | ||
'fieldset', | ||
'figure', | ||
'figcaption', | ||
'footer', | ||
'form', | ||
'h1', | ||
'h2', | ||
'h3', | ||
'h4', | ||
'h5', | ||
'h6', | ||
'head', | ||
'header', | ||
'hgroup', | ||
'hr', | ||
'i', | ||
'iframe', | ||
'img', | ||
'input', | ||
'ins', | ||
'keygen', | ||
'kbd', | ||
'label', | ||
'legend', | ||
'li', | ||
'map', | ||
'mark', | ||
'menu', | ||
'meter', | ||
'nav', | ||
'ol', | ||
'optgroup', | ||
'option', | ||
'output', | ||
'p', | ||
'pre', | ||
'progress', | ||
'q', | ||
'rp', | ||
'rt', | ||
'ruby', | ||
's', | ||
'samp', | ||
'section', | ||
'select', | ||
'small', | ||
'source', | ||
'span', | ||
'strike', | ||
'strong', | ||
'style', | ||
'sub', | ||
'summary', | ||
'sup', | ||
'table', | ||
'tbody', | ||
'td', | ||
'textarea', | ||
'tfoot', | ||
'th', | ||
'thead', | ||
'time', | ||
'tr', | ||
'track', | ||
'tt', | ||
'u', | ||
'ul', | ||
'var', | ||
'video', | ||
'wbr', | ||
]; | ||
const svgTags = [ | ||
'svg', | ||
'a', | ||
'altglyph', | ||
'altglyphdef', | ||
'altglyphitem', | ||
'animatecolor', | ||
'animatemotion', | ||
'animatetransform', | ||
'audio', | ||
'canvas', | ||
'circle', | ||
'clippath', | ||
'defs', | ||
'desc', | ||
'ellipse', | ||
'filter', | ||
'font', | ||
'g', | ||
'glyph', | ||
'glyphref', | ||
'hkern', | ||
'image', | ||
'line', | ||
'lineargradient', | ||
'marker', | ||
'mask', | ||
'mpath', | ||
'path', | ||
'pattern', | ||
'polygon', | ||
'polyline', | ||
'radialgradient', | ||
'rect', | ||
'stop', | ||
'switch', | ||
'symbol', | ||
'text', | ||
'textpath', | ||
'title', | ||
'tref', | ||
'tspan', | ||
'video', | ||
'view', | ||
'vkern', | ||
'use', | ||
]; | ||
const NODE_ALL_IN_PLACE = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags, | ||
IN_PLACE: true | ||
}; // use only svg tags, sanitizer returns a document fragment | ||
const allTags = shared.ArrayConcat(svgTags, htmlTags); | ||
// generic, sanitizer attempts in place sanitization and returns node | ||
const NODE_ALL_IN_PLACE = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags, | ||
IN_PLACE: true, | ||
}; | ||
// use only svg tags, sanitizer returns a document fragment | ||
const NODE_SVG = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: svgTags, | ||
RETURN_DOM_FRAGMENT: true, | ||
SANITIZE_DOM: false, | ||
}; | ||
// generic, sanitizer returns string | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: svgTags, | ||
RETURN_DOM_FRAGMENT: true, | ||
SANITIZE_DOM: false | ||
}; // generic, sanitizer returns string | ||
const STRING_ALL = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags, | ||
}; | ||
// use only tags allowed for blob and file | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags | ||
}; // use only tags allowed for blob and file | ||
const STRING_BLOB_HTML = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: shared.ArrayFilter(allTags, (t) => !shared.ArrayIncludes(['iframe'], t)), | ||
SANITIZE_DOM: false, | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: shared.ArrayFilter(allTags, t => !shared.ArrayIncludes(['iframe'], t)), | ||
SANITIZE_DOM: false | ||
}; | ||
var config = /*#__PURE__*/Object.freeze({ | ||
__proto__: null, | ||
NODE_ALL_IN_PLACE: NODE_ALL_IN_PLACE, | ||
NODE_SVG: NODE_SVG, | ||
STRING_ALL: STRING_ALL, | ||
STRING_BLOB_HTML: STRING_BLOB_HTML | ||
__proto__: null, | ||
NODE_ALL_IN_PLACE: NODE_ALL_IN_PLACE, | ||
NODE_SVG: NODE_SVG, | ||
STRING_ALL: STRING_ALL, | ||
STRING_BLOB_HTML: STRING_BLOB_HTML | ||
}); | ||
const instances = new shared.WeakMapCtor(); | ||
const instances = new shared.WeakMapCtor(); | ||
function sanitizer(config, hooks) { | ||
let dompurify = shared.WeakMapGet(instances, config); | ||
if (dompurify) { | ||
return dompurify; | ||
} | ||
dompurify = DOMPurify__default['default'](); | ||
dompurify.setConfig(config); | ||
if (hooks) { | ||
shared.MapForEach(hooks, (cb, hookName) => { | ||
dompurify.addHook(hookName, cb); | ||
}); | ||
} | ||
shared.WeakMapSet(instances, config, dompurify); | ||
let dompurify = shared.WeakMapGet(instances, config); | ||
if (dompurify) { | ||
return dompurify; | ||
} | ||
dompurify = DOMPurify__default['default'](); | ||
dompurify.setConfig(config); | ||
if (hooks) { | ||
shared.MapForEach(hooks, (cb, hookName) => { | ||
dompurify.addHook(hookName, cb); | ||
}); | ||
} | ||
shared.WeakMapSet(instances, config, dompurify); | ||
return dompurify; | ||
} | ||
const ATTRIBUTES = ['href', 'xlink:href']; | ||
const SANITIZER_HOOKS = new shared.MapCtor([ | ||
['uponSanitizeAttribute', sanitizeHrefAttributeHook], | ||
]); | ||
const SANITIZER_HOOKS = new shared.MapCtor([['uponSanitizeAttribute', sanitizeHrefAttributeHook]]); | ||
const URL_SCHEMES = ['http:', 'https:']; | ||
const { document } = window; | ||
const { | ||
document | ||
} = window; | ||
const htmlTemplate = sharedDom.DocumentCreateElement(document, 'template'); | ||
const normalizerAnchor = sharedDom.DocumentCreateElement(document, 'a'); | ||
// Queue for managing pending xhr requests. | ||
const queue = new shared.SetCtor(); | ||
// Regex to find all non lowercase alphanumeric. | ||
const normalizerAnchor = sharedDom.DocumentCreateElement(document, 'a'); // Queue for managing pending xhr requests. | ||
const queue = new shared.SetCtor(); // Regex to find all non lowercase alphanumeric. | ||
const urlReplacer = /[^a-z0-9]+/gi; | ||
function checkExistingAndDequeue(container, normalizedHref) { | ||
if (shared.SetHas(queue, normalizedHref.normalizedUrl)) { | ||
const checkFn = () => { | ||
if (!shared.SetHas(queue, normalizedHref.normalizedUrl)) { | ||
updater(container, normalizedHref); | ||
sharedDom.WindowClearInterval(window, interval); | ||
} | ||
}; | ||
// Wait for request to finish, then update content. | ||
const interval = sharedDom.WindowSetInterval(window, checkFn, 50); | ||
} | ||
else { | ||
if (shared.SetHas(queue, normalizedHref.normalizedUrl)) { | ||
const checkFn = () => { | ||
if (!shared.SetHas(queue, normalizedHref.normalizedUrl)) { | ||
updater(container, normalizedHref); | ||
} | ||
sharedDom.WindowClearInterval(window, interval); | ||
} | ||
}; // Wait for request to finish, then update content. | ||
const interval = sharedDom.WindowSetInterval(window, checkFn, 50); | ||
} else { | ||
updater(container, normalizedHref); | ||
} | ||
} | ||
function createUrlContainer(url) { | ||
const container = sharedDom.DocumentCreateElement(document, 'div'); | ||
sharedDom.ElementSetAttribute(container, 'style', 'display:none'); | ||
sharedDom.ElementSetAttribute(container, 'id', url); | ||
const body = sharedDom.DocumentBodyGetter(document); | ||
sharedDom.NodeAppendChild(body, container); | ||
return container; | ||
const container = sharedDom.DocumentCreateElement(document, 'div'); | ||
sharedDom.ElementSetAttribute(container, 'style', 'display:none'); | ||
sharedDom.ElementSetAttribute(container, 'id', url); | ||
const body = sharedDom.DocumentBodyGetter(document); | ||
sharedDom.NodeAppendChild(body, container); | ||
return container; | ||
} | ||
function fetchAndSanitize(normalizedHref) { | ||
// This is the first time we see this href. | ||
const container = createUrlContainer(normalizedHref.normalizedUrl); | ||
// Put the URL we're fetching in a queue. | ||
shared.SetAdd(queue, normalizedHref.normalizedUrl); | ||
// Initiate an XHR to fetch the resource. | ||
const xhr = new sharedDom.XhrCtor(); | ||
sharedDom.EventTargetAddEventListener(xhr, 'load', () => { | ||
const status = sharedDom.XhrStatusGetter(xhr); | ||
if (status === 200) { | ||
// Retrieved content should be sanitized immediately. | ||
const fragment = sanitizeSvgTextReturnDOM(sharedDom.XhrResponseTextGetter(xhr)); | ||
// Look for the container again in case other requests have finished | ||
// earlier for the same URL. | ||
if (normalizedHref.requestedFragment) { | ||
const el = sharedDom.DocumentFragmentGetElementById(fragment, normalizedHref.requestedFragment); | ||
if (el) { | ||
sharedDom.ElementSetAttribute(el, 'id', normalizedHref.normalizedFragment); | ||
} | ||
} | ||
sharedDom.NodeAppendChild(container, fragment); | ||
shared.SetDelete(queue, normalizedHref.normalizedUrl); | ||
// This is the first time we see this href. | ||
const container = createUrlContainer(normalizedHref.normalizedUrl); // Put the URL we're fetching in a queue. | ||
shared.SetAdd(queue, normalizedHref.normalizedUrl); // Initiate an XHR to fetch the resource. | ||
const xhr = new sharedDom.XhrCtor(); | ||
sharedDom.EventTargetAddEventListener(xhr, 'load', () => { | ||
const status = sharedDom.XhrStatusGetter(xhr); | ||
if (status === 200) { | ||
// Retrieved content should be sanitized immediately. | ||
const fragment = sanitizeSvgTextReturnDOM(sharedDom.XhrResponseTextGetter(xhr)); // Look for the container again in case other requests have finished | ||
// earlier for the same URL. | ||
if (normalizedHref.requestedFragment) { | ||
const el = sharedDom.DocumentFragmentGetElementById(fragment, normalizedHref.requestedFragment); | ||
if (el) { | ||
sharedDom.ElementSetAttribute(el, 'id', normalizedHref.normalizedFragment); | ||
} | ||
}); | ||
sharedDom.XhrOpen(xhr, 'GET', normalizedHref.requestedUrl); | ||
sharedDom.XhrSend(xhr); | ||
} | ||
sharedDom.NodeAppendChild(container, fragment); | ||
shared.SetDelete(queue, normalizedHref.normalizedUrl); | ||
} | ||
}); | ||
sharedDom.XhrOpen(xhr, 'GET', normalizedHref.requestedUrl); | ||
sharedDom.XhrSend(xhr); | ||
} | ||
function parseHref(url) { | ||
sharedDom.HTMLAnchorElementHrefSetter(normalizerAnchor, url); | ||
const href = sharedDom.HTMLAnchorElementHrefGetter(normalizerAnchor); | ||
const protocol = sharedDom.HTMLAnchorElementProtocolGetter(normalizerAnchor); | ||
const [requestedUrl, requestedFragment] = shared.StringSplit(href, '#'); | ||
const normalizedUrl = shared.StringReplace(shared.StringToLowerCase(requestedUrl), urlReplacer, ''); | ||
const normalizedFragment = requestedFragment | ||
? `${normalizedUrl}_${shared.StringReplace(requestedFragment, urlReplacer, '')}` | ||
: ''; | ||
return { | ||
normalizedFragment, | ||
normalizedUrl, | ||
protocol, | ||
requestedFragment, | ||
requestedUrl, | ||
}; | ||
sharedDom.HTMLAnchorElementHrefSetter(normalizerAnchor, url); | ||
const href = sharedDom.HTMLAnchorElementHrefGetter(normalizerAnchor); | ||
const protocol = sharedDom.HTMLAnchorElementProtocolGetter(normalizerAnchor); | ||
const [requestedUrl, requestedFragment] = shared.StringSplit(href, '#'); | ||
const normalizedUrl = shared.StringReplace(shared.StringToLowerCase(requestedUrl), urlReplacer, ''); | ||
const normalizedFragment = requestedFragment ? "".concat(normalizedUrl, "_").concat(shared.StringReplace(requestedFragment, urlReplacer, '')) : ''; | ||
return { | ||
normalizedFragment, | ||
normalizedUrl, | ||
protocol, | ||
requestedFragment, | ||
requestedUrl | ||
}; | ||
} | ||
function updater(container, normalizedHref) { | ||
const { normalizedFragment, requestedFragment } = normalizedHref; | ||
let el = sharedDom.ElementQuerySelector(container, `#${normalizedFragment}`); | ||
if (!el) { | ||
try { | ||
el = sharedDom.ElementQuerySelector(container, `#${requestedFragment}`); | ||
sharedDom.ElementSetAttribute(el, 'id', normalizedFragment); | ||
} | ||
catch { | ||
// Catch all malformed CSS3 selectors. | ||
// getElementById not available on Node. | ||
// Cannot use document.getElementById because multiple containers may | ||
// have the same ids for svg elements. | ||
} | ||
const { | ||
normalizedFragment, | ||
requestedFragment | ||
} = normalizedHref; | ||
let el = sharedDom.ElementQuerySelector(container, "#".concat(normalizedFragment)); | ||
if (!el) { | ||
try { | ||
el = sharedDom.ElementQuerySelector(container, "#".concat(requestedFragment)); | ||
sharedDom.ElementSetAttribute(el, 'id', normalizedFragment); | ||
} catch (_unused) {// Catch all malformed CSS3 selectors. | ||
// getElementById not available on Node. | ||
// Cannot use document.getElementById because multiple containers may | ||
// have the same ids for svg elements. | ||
} | ||
} | ||
} | ||
function blobSanitizer() { | ||
return sanitizer(STRING_BLOB_HTML, SANITIZER_HOOKS); | ||
return sanitizer(STRING_BLOB_HTML, SANITIZER_HOOKS); | ||
} | ||
function svgSanitizer() { | ||
return sanitizer(NODE_SVG, SANITIZER_HOOKS); | ||
return sanitizer(NODE_SVG, SANITIZER_HOOKS); | ||
} | ||
function sanitize(dirty) { | ||
sharedDom.ElementInnerHTMLSetter(htmlTemplate, dirty); | ||
const content = sharedDom.HTMLTemplateElementContentGetter(htmlTemplate); | ||
const sanitizer$1 = sanitizer(NODE_ALL_IN_PLACE, SANITIZER_HOOKS); | ||
sanitizer$1.sanitize(content); | ||
return sharedDom.ElementInnerHTMLGetter(htmlTemplate); | ||
} | ||
// Sanitize a URL representing a SVG href attribute value. | ||
sharedDom.ElementInnerHTMLSetter(htmlTemplate, dirty); | ||
const content = sharedDom.HTMLTemplateElementContentGetter(htmlTemplate); | ||
const sanitizer$1 = sanitizer(NODE_ALL_IN_PLACE, SANITIZER_HOOKS); | ||
sanitizer$1.sanitize(content); | ||
return sharedDom.ElementInnerHTMLGetter(htmlTemplate); | ||
} // Sanitize a URL representing a SVG href attribute value. | ||
function sanitizeHrefAttributeHook(node, data) { | ||
const nodeName = sharedDom.NodeNameGetter(node); | ||
if (data.attrValue && nodeName === 'USE' && shared.ArrayIncludes(ATTRIBUTES, data.attrName)) { | ||
data.attrValue = sanitizeSvgHrefValue(data.attrValue); | ||
} | ||
return data; | ||
const nodeName = sharedDom.NodeNameGetter(node); | ||
if (data.attrValue && nodeName === 'USE' && shared.ArrayIncludes(ATTRIBUTES, data.attrName)) { | ||
data.attrValue = sanitizeSvgHrefValue(data.attrValue); | ||
} | ||
return data; | ||
} | ||
function sanitizeSvgHrefValue(url) { | ||
if (shared.StringStartsWith(url, '#')) { | ||
return url; | ||
} | ||
const normalizedHref = parseHref(url); | ||
// Sanitize only for supported URL_SCHEMES. | ||
if (shared.ArrayIncludes(URL_SCHEMES, normalizedHref.protocol)) { | ||
const container = sharedDom.DocumentGetElementById(document, normalizedHref.normalizedUrl); | ||
// Have we sanitized this URL already? | ||
if (container && normalizedHref.normalizedFragment) { | ||
checkExistingAndDequeue(container, normalizedHref); | ||
} | ||
else if (!container) { | ||
fetchAndSanitize(normalizedHref); | ||
} | ||
// If this has been in the form of http://my-url/file.svg#fragment we | ||
// return the normalized fragment otherwise we return the normalized URL. | ||
return normalizedHref.requestedFragment | ||
? `#${normalizedHref.normalizedFragment}` | ||
: `#${normalizedHref.normalizedUrl}`; | ||
} | ||
if (shared.StringStartsWith(url, '#')) { | ||
return url; | ||
} | ||
const normalizedHref = parseHref(url); // Sanitize only for supported URL_SCHEMES. | ||
if (shared.ArrayIncludes(URL_SCHEMES, normalizedHref.protocol)) { | ||
const container = sharedDom.DocumentGetElementById(document, normalizedHref.normalizedUrl); // Have we sanitized this URL already? | ||
if (container && normalizedHref.normalizedFragment) { | ||
checkExistingAndDequeue(container, normalizedHref); | ||
} else if (!container) { | ||
fetchAndSanitize(normalizedHref); | ||
} // If this has been in the form of http://my-url/file.svg#fragment we | ||
// return the normalized fragment otherwise we return the normalized URL. | ||
return normalizedHref.requestedFragment ? "#".concat(normalizedHref.normalizedFragment) : "#".concat(normalizedHref.normalizedUrl); | ||
} | ||
return url; | ||
} | ||
function sanitizeSvgInnerHtml(el, dirty) { | ||
const ownerDoc = sharedDom.NodeOwnerDocumentGetter(el); | ||
const comment = sharedDom.DocumentCreateComment(ownerDoc, ''); | ||
const closestSvg = sharedDom.ElementClosest(el, 'svg'); | ||
const container = closestSvg | ||
? sharedDom.NodeClone(closestSvg, false) | ||
: sharedDom.DocumentCreateElementNS(ownerDoc, 'http://www.w3.org/2000/svg', 'svg'); | ||
sharedDom.NodeAppendChild(container, comment); | ||
const outerHTML = sharedDom.ElementOuterHTMLGetter(container); | ||
const replacedOuterHTML = shared.StringReplace(outerHTML, '<!---->', dirty); | ||
const fragment = sanitizeSvgTextReturnDOM(replacedOuterHTML); | ||
const firstChild = sharedDom.NodeFirstChildGetter(fragment); | ||
return sharedDom.ElementInnerHTMLGetter(firstChild); | ||
const ownerDoc = sharedDom.NodeOwnerDocumentGetter(el); | ||
const comment = sharedDom.DocumentCreateComment(ownerDoc, ''); | ||
const closestSvg = sharedDom.ElementClosest(el, 'svg'); | ||
const container = closestSvg ? sharedDom.NodeClone(closestSvg, false) : sharedDom.DocumentCreateElementNS(ownerDoc, 'http://www.w3.org/2000/svg', 'svg'); | ||
sharedDom.NodeAppendChild(container, comment); | ||
const outerHTML = sharedDom.ElementOuterHTMLGetter(container); | ||
const replacedOuterHTML = shared.StringReplace(outerHTML, '<!---->', dirty); | ||
const fragment = sanitizeSvgTextReturnDOM(replacedOuterHTML); | ||
const firstChild = sharedDom.NodeFirstChildGetter(fragment); | ||
return sharedDom.ElementInnerHTMLGetter(firstChild); | ||
} | ||
function sanitizeSvgTextReturnDOM(dirty) { | ||
const sanitizer = svgSanitizer(); | ||
return sanitizer.sanitize(dirty); | ||
const sanitizer = svgSanitizer(); | ||
return sanitizer.sanitize(dirty); | ||
} | ||
@@ -431,2 +264,2 @@ | ||
exports.svgSanitizer = svgSanitizer; | ||
/*! version: 0.14.4 */ | ||
/*! version: 0.14.5 */ |
@@ -7,409 +7,233 @@ /*! | ||
import DOMPurify from 'dompurify'; | ||
const ariaAttributes = ['aria-activedescendant', 'aria-atomic', 'aria-autocomplete', 'aria-busy', 'aria-checked', 'aria-controls', 'aria-describedby', 'aria-disabled', 'aria-readonly', 'aria-dropeffect', 'aria-expanded', 'aria-flowto', 'aria-grabbed', 'aria-haspopup', 'aria-hidden', 'aria-disabled', 'aria-invalid', 'aria-label', 'aria-labelledby', 'aria-level', 'aria-live', 'aria-multiline', 'aria-multiselectable', 'aria-orientation', 'aria-owns', 'aria-posinset', 'aria-pressed', 'aria-readonly', 'aria-relevant', 'aria-required', 'aria-selected', 'aria-setsize', 'aria-sort', 'aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext', 'role', 'target']; | ||
const htmlTags = ['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'caption', 'canvas', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del', 'details', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'figure', 'figcaption', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li', 'map', 'mark', 'menu', 'meter', 'nav', 'ol', 'optgroup', 'option', 'output', 'p', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'small', 'source', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']; | ||
const svgTags = ['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'video', 'view', 'vkern', 'use']; | ||
const allTags = ArrayConcat(svgTags, htmlTags); // generic, sanitizer attempts in place sanitization and returns node | ||
const ariaAttributes = [ | ||
'aria-activedescendant', | ||
'aria-atomic', | ||
'aria-autocomplete', | ||
'aria-busy', | ||
'aria-checked', | ||
'aria-controls', | ||
'aria-describedby', | ||
'aria-disabled', | ||
'aria-readonly', | ||
'aria-dropeffect', | ||
'aria-expanded', | ||
'aria-flowto', | ||
'aria-grabbed', | ||
'aria-haspopup', | ||
'aria-hidden', | ||
'aria-disabled', | ||
'aria-invalid', | ||
'aria-label', | ||
'aria-labelledby', | ||
'aria-level', | ||
'aria-live', | ||
'aria-multiline', | ||
'aria-multiselectable', | ||
'aria-orientation', | ||
'aria-owns', | ||
'aria-posinset', | ||
'aria-pressed', | ||
'aria-readonly', | ||
'aria-relevant', | ||
'aria-required', | ||
'aria-selected', | ||
'aria-setsize', | ||
'aria-sort', | ||
'aria-valuemax', | ||
'aria-valuemin', | ||
'aria-valuenow', | ||
'aria-valuetext', | ||
'role', | ||
'target', | ||
]; | ||
const NODE_ALL_IN_PLACE = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags, | ||
IN_PLACE: true | ||
}; // use only svg tags, sanitizer returns a document fragment | ||
const htmlTags = [ | ||
'a', | ||
'abbr', | ||
'acronym', | ||
'address', | ||
'area', | ||
'article', | ||
'aside', | ||
'audio', | ||
'b', | ||
'bdi', | ||
'bdo', | ||
'big', | ||
'blockquote', | ||
'body', | ||
'br', | ||
'button', | ||
'caption', | ||
'canvas', | ||
'center', | ||
'cite', | ||
'code', | ||
'col', | ||
'colgroup', | ||
'command', | ||
'datalist', | ||
'dd', | ||
'del', | ||
'details', | ||
'dfn', | ||
'dir', | ||
'div', | ||
'dl', | ||
'dt', | ||
'em', | ||
'fieldset', | ||
'figure', | ||
'figcaption', | ||
'footer', | ||
'form', | ||
'h1', | ||
'h2', | ||
'h3', | ||
'h4', | ||
'h5', | ||
'h6', | ||
'head', | ||
'header', | ||
'hgroup', | ||
'hr', | ||
'i', | ||
'iframe', | ||
'img', | ||
'input', | ||
'ins', | ||
'keygen', | ||
'kbd', | ||
'label', | ||
'legend', | ||
'li', | ||
'map', | ||
'mark', | ||
'menu', | ||
'meter', | ||
'nav', | ||
'ol', | ||
'optgroup', | ||
'option', | ||
'output', | ||
'p', | ||
'pre', | ||
'progress', | ||
'q', | ||
'rp', | ||
'rt', | ||
'ruby', | ||
's', | ||
'samp', | ||
'section', | ||
'select', | ||
'small', | ||
'source', | ||
'span', | ||
'strike', | ||
'strong', | ||
'style', | ||
'sub', | ||
'summary', | ||
'sup', | ||
'table', | ||
'tbody', | ||
'td', | ||
'textarea', | ||
'tfoot', | ||
'th', | ||
'thead', | ||
'time', | ||
'tr', | ||
'track', | ||
'tt', | ||
'u', | ||
'ul', | ||
'var', | ||
'video', | ||
'wbr', | ||
]; | ||
const svgTags = [ | ||
'svg', | ||
'a', | ||
'altglyph', | ||
'altglyphdef', | ||
'altglyphitem', | ||
'animatecolor', | ||
'animatemotion', | ||
'animatetransform', | ||
'audio', | ||
'canvas', | ||
'circle', | ||
'clippath', | ||
'defs', | ||
'desc', | ||
'ellipse', | ||
'filter', | ||
'font', | ||
'g', | ||
'glyph', | ||
'glyphref', | ||
'hkern', | ||
'image', | ||
'line', | ||
'lineargradient', | ||
'marker', | ||
'mask', | ||
'mpath', | ||
'path', | ||
'pattern', | ||
'polygon', | ||
'polyline', | ||
'radialgradient', | ||
'rect', | ||
'stop', | ||
'switch', | ||
'symbol', | ||
'text', | ||
'textpath', | ||
'title', | ||
'tref', | ||
'tspan', | ||
'video', | ||
'view', | ||
'vkern', | ||
'use', | ||
]; | ||
const NODE_SVG = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: svgTags, | ||
RETURN_DOM_FRAGMENT: true, | ||
SANITIZE_DOM: false | ||
}; // generic, sanitizer returns string | ||
const allTags = ArrayConcat(svgTags, htmlTags); | ||
// generic, sanitizer attempts in place sanitization and returns node | ||
const NODE_ALL_IN_PLACE = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags, | ||
IN_PLACE: true, | ||
}; | ||
// use only svg tags, sanitizer returns a document fragment | ||
const NODE_SVG = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: svgTags, | ||
RETURN_DOM_FRAGMENT: true, | ||
SANITIZE_DOM: false, | ||
}; | ||
// generic, sanitizer returns string | ||
const STRING_ALL = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags, | ||
}; | ||
// use only tags allowed for blob and file | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: allTags | ||
}; // use only tags allowed for blob and file | ||
const STRING_BLOB_HTML = { | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: ArrayFilter(allTags, (t) => !ArrayIncludes(['iframe'], t)), | ||
SANITIZE_DOM: false, | ||
ADD_ATTR: ariaAttributes, | ||
ALLOWED_TAGS: ArrayFilter(allTags, t => !ArrayIncludes(['iframe'], t)), | ||
SANITIZE_DOM: false | ||
}; | ||
var config = /*#__PURE__*/Object.freeze({ | ||
__proto__: null, | ||
NODE_ALL_IN_PLACE: NODE_ALL_IN_PLACE, | ||
NODE_SVG: NODE_SVG, | ||
STRING_ALL: STRING_ALL, | ||
STRING_BLOB_HTML: STRING_BLOB_HTML | ||
__proto__: null, | ||
NODE_ALL_IN_PLACE: NODE_ALL_IN_PLACE, | ||
NODE_SVG: NODE_SVG, | ||
STRING_ALL: STRING_ALL, | ||
STRING_BLOB_HTML: STRING_BLOB_HTML | ||
}); | ||
const instances = new WeakMapCtor(); | ||
const instances = new WeakMapCtor(); | ||
function sanitizer(config, hooks) { | ||
let dompurify = WeakMapGet(instances, config); | ||
if (dompurify) { | ||
return dompurify; | ||
} | ||
dompurify = DOMPurify(); | ||
dompurify.setConfig(config); | ||
if (hooks) { | ||
MapForEach(hooks, (cb, hookName) => { | ||
dompurify.addHook(hookName, cb); | ||
}); | ||
} | ||
WeakMapSet(instances, config, dompurify); | ||
let dompurify = WeakMapGet(instances, config); | ||
if (dompurify) { | ||
return dompurify; | ||
} | ||
dompurify = DOMPurify(); | ||
dompurify.setConfig(config); | ||
if (hooks) { | ||
MapForEach(hooks, (cb, hookName) => { | ||
dompurify.addHook(hookName, cb); | ||
}); | ||
} | ||
WeakMapSet(instances, config, dompurify); | ||
return dompurify; | ||
} | ||
const ATTRIBUTES = ['href', 'xlink:href']; | ||
const SANITIZER_HOOKS = new MapCtor([ | ||
['uponSanitizeAttribute', sanitizeHrefAttributeHook], | ||
]); | ||
const SANITIZER_HOOKS = new MapCtor([['uponSanitizeAttribute', sanitizeHrefAttributeHook]]); | ||
const URL_SCHEMES = ['http:', 'https:']; | ||
const { document } = window; | ||
const { | ||
document | ||
} = window; | ||
const htmlTemplate = DocumentCreateElement(document, 'template'); | ||
const normalizerAnchor = DocumentCreateElement(document, 'a'); | ||
// Queue for managing pending xhr requests. | ||
const queue = new SetCtor(); | ||
// Regex to find all non lowercase alphanumeric. | ||
const normalizerAnchor = DocumentCreateElement(document, 'a'); // Queue for managing pending xhr requests. | ||
const queue = new SetCtor(); // Regex to find all non lowercase alphanumeric. | ||
const urlReplacer = /[^a-z0-9]+/gi; | ||
function checkExistingAndDequeue(container, normalizedHref) { | ||
if (SetHas(queue, normalizedHref.normalizedUrl)) { | ||
const checkFn = () => { | ||
if (!SetHas(queue, normalizedHref.normalizedUrl)) { | ||
updater(container, normalizedHref); | ||
WindowClearInterval(window, interval); | ||
} | ||
}; | ||
// Wait for request to finish, then update content. | ||
const interval = WindowSetInterval(window, checkFn, 50); | ||
} | ||
else { | ||
if (SetHas(queue, normalizedHref.normalizedUrl)) { | ||
const checkFn = () => { | ||
if (!SetHas(queue, normalizedHref.normalizedUrl)) { | ||
updater(container, normalizedHref); | ||
} | ||
WindowClearInterval(window, interval); | ||
} | ||
}; // Wait for request to finish, then update content. | ||
const interval = WindowSetInterval(window, checkFn, 50); | ||
} else { | ||
updater(container, normalizedHref); | ||
} | ||
} | ||
function createUrlContainer(url) { | ||
const container = DocumentCreateElement(document, 'div'); | ||
ElementSetAttribute(container, 'style', 'display:none'); | ||
ElementSetAttribute(container, 'id', url); | ||
const body = DocumentBodyGetter(document); | ||
NodeAppendChild(body, container); | ||
return container; | ||
const container = DocumentCreateElement(document, 'div'); | ||
ElementSetAttribute(container, 'style', 'display:none'); | ||
ElementSetAttribute(container, 'id', url); | ||
const body = DocumentBodyGetter(document); | ||
NodeAppendChild(body, container); | ||
return container; | ||
} | ||
function fetchAndSanitize(normalizedHref) { | ||
// This is the first time we see this href. | ||
const container = createUrlContainer(normalizedHref.normalizedUrl); | ||
// Put the URL we're fetching in a queue. | ||
SetAdd(queue, normalizedHref.normalizedUrl); | ||
// Initiate an XHR to fetch the resource. | ||
const xhr = new XhrCtor(); | ||
EventTargetAddEventListener(xhr, 'load', () => { | ||
const status = XhrStatusGetter(xhr); | ||
if (status === 200) { | ||
// Retrieved content should be sanitized immediately. | ||
const fragment = sanitizeSvgTextReturnDOM(XhrResponseTextGetter(xhr)); | ||
// Look for the container again in case other requests have finished | ||
// earlier for the same URL. | ||
if (normalizedHref.requestedFragment) { | ||
const el = DocumentFragmentGetElementById(fragment, normalizedHref.requestedFragment); | ||
if (el) { | ||
ElementSetAttribute(el, 'id', normalizedHref.normalizedFragment); | ||
} | ||
} | ||
NodeAppendChild(container, fragment); | ||
SetDelete(queue, normalizedHref.normalizedUrl); | ||
// This is the first time we see this href. | ||
const container = createUrlContainer(normalizedHref.normalizedUrl); // Put the URL we're fetching in a queue. | ||
SetAdd(queue, normalizedHref.normalizedUrl); // Initiate an XHR to fetch the resource. | ||
const xhr = new XhrCtor(); | ||
EventTargetAddEventListener(xhr, 'load', () => { | ||
const status = XhrStatusGetter(xhr); | ||
if (status === 200) { | ||
// Retrieved content should be sanitized immediately. | ||
const fragment = sanitizeSvgTextReturnDOM(XhrResponseTextGetter(xhr)); // Look for the container again in case other requests have finished | ||
// earlier for the same URL. | ||
if (normalizedHref.requestedFragment) { | ||
const el = DocumentFragmentGetElementById(fragment, normalizedHref.requestedFragment); | ||
if (el) { | ||
ElementSetAttribute(el, 'id', normalizedHref.normalizedFragment); | ||
} | ||
}); | ||
XhrOpen(xhr, 'GET', normalizedHref.requestedUrl); | ||
XhrSend(xhr); | ||
} | ||
NodeAppendChild(container, fragment); | ||
SetDelete(queue, normalizedHref.normalizedUrl); | ||
} | ||
}); | ||
XhrOpen(xhr, 'GET', normalizedHref.requestedUrl); | ||
XhrSend(xhr); | ||
} | ||
function parseHref(url) { | ||
HTMLAnchorElementHrefSetter(normalizerAnchor, url); | ||
const href = HTMLAnchorElementHrefGetter(normalizerAnchor); | ||
const protocol = HTMLAnchorElementProtocolGetter(normalizerAnchor); | ||
const [requestedUrl, requestedFragment] = StringSplit(href, '#'); | ||
const normalizedUrl = StringReplace(StringToLowerCase(requestedUrl), urlReplacer, ''); | ||
const normalizedFragment = requestedFragment | ||
? `${normalizedUrl}_${StringReplace(requestedFragment, urlReplacer, '')}` | ||
: ''; | ||
return { | ||
normalizedFragment, | ||
normalizedUrl, | ||
protocol, | ||
requestedFragment, | ||
requestedUrl, | ||
}; | ||
HTMLAnchorElementHrefSetter(normalizerAnchor, url); | ||
const href = HTMLAnchorElementHrefGetter(normalizerAnchor); | ||
const protocol = HTMLAnchorElementProtocolGetter(normalizerAnchor); | ||
const [requestedUrl, requestedFragment] = StringSplit(href, '#'); | ||
const normalizedUrl = StringReplace(StringToLowerCase(requestedUrl), urlReplacer, ''); | ||
const normalizedFragment = requestedFragment ? "".concat(normalizedUrl, "_").concat(StringReplace(requestedFragment, urlReplacer, '')) : ''; | ||
return { | ||
normalizedFragment, | ||
normalizedUrl, | ||
protocol, | ||
requestedFragment, | ||
requestedUrl | ||
}; | ||
} | ||
function updater(container, normalizedHref) { | ||
const { normalizedFragment, requestedFragment } = normalizedHref; | ||
let el = ElementQuerySelector(container, `#${normalizedFragment}`); | ||
if (!el) { | ||
try { | ||
el = ElementQuerySelector(container, `#${requestedFragment}`); | ||
ElementSetAttribute(el, 'id', normalizedFragment); | ||
} | ||
catch { | ||
// Catch all malformed CSS3 selectors. | ||
// getElementById not available on Node. | ||
// Cannot use document.getElementById because multiple containers may | ||
// have the same ids for svg elements. | ||
} | ||
const { | ||
normalizedFragment, | ||
requestedFragment | ||
} = normalizedHref; | ||
let el = ElementQuerySelector(container, "#".concat(normalizedFragment)); | ||
if (!el) { | ||
try { | ||
el = ElementQuerySelector(container, "#".concat(requestedFragment)); | ||
ElementSetAttribute(el, 'id', normalizedFragment); | ||
} catch (_unused) {// Catch all malformed CSS3 selectors. | ||
// getElementById not available on Node. | ||
// Cannot use document.getElementById because multiple containers may | ||
// have the same ids for svg elements. | ||
} | ||
} | ||
} | ||
function blobSanitizer() { | ||
return sanitizer(STRING_BLOB_HTML, SANITIZER_HOOKS); | ||
return sanitizer(STRING_BLOB_HTML, SANITIZER_HOOKS); | ||
} | ||
function svgSanitizer() { | ||
return sanitizer(NODE_SVG, SANITIZER_HOOKS); | ||
return sanitizer(NODE_SVG, SANITIZER_HOOKS); | ||
} | ||
function sanitize(dirty) { | ||
ElementInnerHTMLSetter(htmlTemplate, dirty); | ||
const content = HTMLTemplateElementContentGetter(htmlTemplate); | ||
const sanitizer$1 = sanitizer(NODE_ALL_IN_PLACE, SANITIZER_HOOKS); | ||
sanitizer$1.sanitize(content); | ||
return ElementInnerHTMLGetter(htmlTemplate); | ||
} | ||
// Sanitize a URL representing a SVG href attribute value. | ||
ElementInnerHTMLSetter(htmlTemplate, dirty); | ||
const content = HTMLTemplateElementContentGetter(htmlTemplate); | ||
const sanitizer$1 = sanitizer(NODE_ALL_IN_PLACE, SANITIZER_HOOKS); | ||
sanitizer$1.sanitize(content); | ||
return ElementInnerHTMLGetter(htmlTemplate); | ||
} // Sanitize a URL representing a SVG href attribute value. | ||
function sanitizeHrefAttributeHook(node, data) { | ||
const nodeName = NodeNameGetter(node); | ||
if (data.attrValue && nodeName === 'USE' && ArrayIncludes(ATTRIBUTES, data.attrName)) { | ||
data.attrValue = sanitizeSvgHrefValue(data.attrValue); | ||
} | ||
return data; | ||
const nodeName = NodeNameGetter(node); | ||
if (data.attrValue && nodeName === 'USE' && ArrayIncludes(ATTRIBUTES, data.attrName)) { | ||
data.attrValue = sanitizeSvgHrefValue(data.attrValue); | ||
} | ||
return data; | ||
} | ||
function sanitizeSvgHrefValue(url) { | ||
if (StringStartsWith(url, '#')) { | ||
return url; | ||
} | ||
const normalizedHref = parseHref(url); | ||
// Sanitize only for supported URL_SCHEMES. | ||
if (ArrayIncludes(URL_SCHEMES, normalizedHref.protocol)) { | ||
const container = DocumentGetElementById(document, normalizedHref.normalizedUrl); | ||
// Have we sanitized this URL already? | ||
if (container && normalizedHref.normalizedFragment) { | ||
checkExistingAndDequeue(container, normalizedHref); | ||
} | ||
else if (!container) { | ||
fetchAndSanitize(normalizedHref); | ||
} | ||
// If this has been in the form of http://my-url/file.svg#fragment we | ||
// return the normalized fragment otherwise we return the normalized URL. | ||
return normalizedHref.requestedFragment | ||
? `#${normalizedHref.normalizedFragment}` | ||
: `#${normalizedHref.normalizedUrl}`; | ||
} | ||
if (StringStartsWith(url, '#')) { | ||
return url; | ||
} | ||
const normalizedHref = parseHref(url); // Sanitize only for supported URL_SCHEMES. | ||
if (ArrayIncludes(URL_SCHEMES, normalizedHref.protocol)) { | ||
const container = DocumentGetElementById(document, normalizedHref.normalizedUrl); // Have we sanitized this URL already? | ||
if (container && normalizedHref.normalizedFragment) { | ||
checkExistingAndDequeue(container, normalizedHref); | ||
} else if (!container) { | ||
fetchAndSanitize(normalizedHref); | ||
} // If this has been in the form of http://my-url/file.svg#fragment we | ||
// return the normalized fragment otherwise we return the normalized URL. | ||
return normalizedHref.requestedFragment ? "#".concat(normalizedHref.normalizedFragment) : "#".concat(normalizedHref.normalizedUrl); | ||
} | ||
return url; | ||
} | ||
function sanitizeSvgInnerHtml(el, dirty) { | ||
const ownerDoc = NodeOwnerDocumentGetter(el); | ||
const comment = DocumentCreateComment(ownerDoc, ''); | ||
const closestSvg = ElementClosest(el, 'svg'); | ||
const container = closestSvg | ||
? NodeClone(closestSvg, false) | ||
: DocumentCreateElementNS(ownerDoc, 'http://www.w3.org/2000/svg', 'svg'); | ||
NodeAppendChild(container, comment); | ||
const outerHTML = ElementOuterHTMLGetter(container); | ||
const replacedOuterHTML = StringReplace(outerHTML, '<!---->', dirty); | ||
const fragment = sanitizeSvgTextReturnDOM(replacedOuterHTML); | ||
const firstChild = NodeFirstChildGetter(fragment); | ||
return ElementInnerHTMLGetter(firstChild); | ||
const ownerDoc = NodeOwnerDocumentGetter(el); | ||
const comment = DocumentCreateComment(ownerDoc, ''); | ||
const closestSvg = ElementClosest(el, 'svg'); | ||
const container = closestSvg ? NodeClone(closestSvg, false) : DocumentCreateElementNS(ownerDoc, 'http://www.w3.org/2000/svg', 'svg'); | ||
NodeAppendChild(container, comment); | ||
const outerHTML = ElementOuterHTMLGetter(container); | ||
const replacedOuterHTML = StringReplace(outerHTML, '<!---->', dirty); | ||
const fragment = sanitizeSvgTextReturnDOM(replacedOuterHTML); | ||
const firstChild = NodeFirstChildGetter(fragment); | ||
return ElementInnerHTMLGetter(firstChild); | ||
} | ||
function sanitizeSvgTextReturnDOM(dirty) { | ||
const sanitizer = svgSanitizer(); | ||
return sanitizer.sanitize(dirty); | ||
const sanitizer = svgSanitizer(); | ||
return sanitizer.sanitize(dirty); | ||
} | ||
export { config as CONFIG, blobSanitizer, sanitize, sanitizeHrefAttributeHook, sanitizeSvgHrefValue, sanitizeSvgInnerHtml, sanitizeSvgTextReturnDOM, sanitizer, svgSanitizer }; | ||
/*! version: 0.14.4 */ | ||
/*! version: 0.14.5 */ |
{ | ||
"name": "@locker/html-sanitizer", | ||
"version": "0.14.4", | ||
"version": "0.14.5", | ||
"license": "Salesforce Developer Agreement", | ||
@@ -18,4 +18,4 @@ "author": "Salesforce UI Security Team", | ||
"dependencies": { | ||
"@locker/shared": "0.14.4", | ||
"@locker/shared-dom": "0.14.4", | ||
"@locker/shared": "0.14.5", | ||
"@locker/shared-dom": "0.14.5", | ||
"@types/dompurify": "2.2.2", | ||
@@ -28,3 +28,3 @@ "dompurify": "2.2.9" | ||
], | ||
"gitHead": "775de69b0fe1bb6cc739abc304e56b552c1f85d2" | ||
"gitHead": "257009273b0c98354e0fafd9d4e81d16664bad2e" | ||
} |
29146
451
+ Added@locker/shared@0.14.5(transitive)
+ Added@locker/shared-dom@0.14.5(transitive)
- Removed@locker/shared@0.14.4(transitive)
- Removed@locker/shared-dom@0.14.4(transitive)
Updated@locker/shared@0.14.5
Updated@locker/shared-dom@0.14.5