New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@locker/html-sanitizer

Package Overview
Dependencies
Maintainers
7
Versions
236
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@locker/html-sanitizer - npm Package Compare versions

Comparing version 0.14.4 to 0.14.5

559

dist/index.cjs.js

@@ -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"
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc