UNPKG

amuchina

Version:

A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebWorker like neither of those.

818 lines (817 loc) 13.8 kB
/* IMPORT */ import { mergeMaps } from './utils.js'; /* ELEMENTS */ const HTML_ELEMENTS_ALLOW = [ 'a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'bgsound', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'layer', 'legend', 'li', 'link', 'listing', 'main', 'map', 'mark', 'marquee', 'menu', 'meta', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'popup', 'pre', 'progress', 'q', 'rb', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'section', 'select', 'selectmenu', 'small', 'source', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr' ]; const HTML_ELEMENTS_DISALLOW = [ 'basefont', 'command', 'data', 'iframe', 'image', 'plaintext', 'portal', 'slot', // 'template', //TODO: Not exactly correct to never allow this, too strict 'textarea', 'title', 'xmp' ]; const HTML_ELEMENTS = new Set([ ...HTML_ELEMENTS_ALLOW, ...HTML_ELEMENTS_DISALLOW ]); const SVG_ELEMENTS_ALLOW = [ 'svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern', /* FILTERS */ 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence' ]; const SVG_ELEMENTS_DISALLOW = [ 'animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use' ]; const SVG_ELEMENTS = new Set([ ...SVG_ELEMENTS_ALLOW, ...SVG_ELEMENTS_DISALLOW ]); const MATH_ELEMENTS_ALLOW = [ 'math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover' ]; const MATH_ELEMENTS_DISALLOW = [ 'maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none' ]; const MATH_ELEMENTS = new Set([ ...MATH_ELEMENTS_ALLOW, ...MATH_ELEMENTS_DISALLOW ]); /* ATTRIBUTES */ const HTML_ATTRIBUTES_ALLOW = [ 'abbr', 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'alink', 'allow', 'allowfullscreen', 'alt', 'anchor', 'archive', 'as', 'async', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'autopictureinpicture', 'autoplay', 'axis', 'background', 'behavior', 'bgcolor', 'border', 'bordercolor', 'capture', 'cellpadding', 'cellspacing', 'challenge', 'char', 'charoff', 'charset', 'checked', 'cite', 'class', 'classid', 'clear', 'code', 'codebase', 'codetype', 'color', 'cols', 'colspan', 'compact', 'content', 'contenteditable', 'controls', 'controlslist', 'conversiondestination', 'coords', 'crossorigin', 'csp', 'data', 'datetime', 'declare', 'decoding', 'default', 'defer', 'dir', 'direction', 'dirname', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'disallowdocumentaccess', 'download', 'draggable', 'elementtiming', 'enctype', 'end', 'enterkeyhint', 'event', 'exportparts', 'face', 'for', 'form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', 'frame', 'frameborder', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'hreftranslate', 'hspace', 'http-equiv', 'id', 'imagesizes', 'imagesrcset', 'importance', 'impressiondata', 'impressionexpiry', 'incremental', 'inert', 'inputmode', 'integrity', 'invisible', 'ismap', 'keytype', 'kind', 'label', 'lang', 'language', 'latencyhint', 'leftmargin', 'link', 'list', 'loading', 'longdesc', 'loop', 'low', 'lowsrc', 'manifest', 'marginheight', 'marginwidth', 'max', 'maxlength', 'mayscript', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nohref', 'nomodule', 'nonce', 'noresize', 'noshade', 'novalidate', 'nowrap', 'object', 'open', 'optimum', 'part', 'pattern', 'ping', 'placeholder', 'playsinline', 'policy', 'poster', 'preload', 'pseudo', 'readonly', 'referrerpolicy', 'rel', 'reportingorigin', 'required', 'resources', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'rules', 'sandbox', 'scheme', 'scope', 'scopes', 'scrollamount', 'scrolldelay', 'scrolling', 'select', 'selected', 'shadowroot', 'shadowrootdelegatesfocus', 'shape', 'size', 'sizes', 'slot', 'span', 'spellcheck', 'src', 'srclang', 'srcset', 'standby', 'start', 'step', 'style', 'summary', 'tabindex', 'target', 'text', 'title', 'topmargin', 'translate', 'truespeed', 'trusttoken', 'type', 'usemap', 'valign', 'value', 'valuetype', 'version', 'virtualkeyboardpolicy', 'vlink', 'vspace', 'webkitdirectory', 'width', 'wrap' ]; const HTML_ATTRIBUTES_DISALLOW = [ 'allowpaymentrequest', 'is' ]; const HTML_ATTRIBUTES = new Set([ ...HTML_ATTRIBUTES_ALLOW, ...HTML_ATTRIBUTES_DISALLOW ]); const SVG_ATTRIBUTES_ALLOW = [ 'accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dominant-baseline', 'dur', 'edgemode', 'elevation', 'end', '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', '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', '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', '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', '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' ]; const SVG_ATTRIBUTES_DISALLOW = []; const SVG_ATTRIBUTES = new Set([ ...SVG_ATTRIBUTES_ALLOW, ...SVG_ATTRIBUTES_DISALLOW ]); const MATH_ATTRIBUTES_ALLOW = [ 'accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', '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' ]; const MATH_ATTRIBUTES_DISALLOW = []; const MATH_ATTRIBUTES = new Set([ ...MATH_ATTRIBUTES_ALLOW, ...MATH_ATTRIBUTES_DISALLOW ]); /* NAMESPACES */ const NAMESPACES = { HTML: 'http://www.w3.org/1999/xhtml', SVG: 'http://www.w3.org/2000/svg', MATH: 'http://www.w3.org/1998/Math/MathML' }; const NAMESPACES_ELEMENTS = { [NAMESPACES.HTML]: HTML_ELEMENTS, [NAMESPACES.SVG]: SVG_ELEMENTS, [NAMESPACES.MATH]: MATH_ELEMENTS }; const NAMESPACES_ATTRIBUTES = { [NAMESPACES.HTML]: HTML_ATTRIBUTES, [NAMESPACES.SVG]: SVG_ATTRIBUTES, [NAMESPACES.MATH]: MATH_ATTRIBUTES }; const NAMESPACES_ROOTS = { [NAMESPACES.HTML]: 'html', [NAMESPACES.SVG]: 'svg', [NAMESPACES.MATH]: 'math' }; const NAMESPACES_PREFIXES = { [NAMESPACES.HTML]: '', [NAMESPACES.SVG]: 'svg:', [NAMESPACES.MATH]: 'math:' }; /* TAG NAMES */ const FUNKY_TAG_NAMES = new Set([ 'A', 'AREA', 'BUTTON', 'FORM', 'IFRAME', 'INPUT' ]); /* OTHERS */ const DEFAULTS = { allowComments: true, allowCustomElements: false, allowUnknownMarkup: false, allowElements: [ ...HTML_ELEMENTS_ALLOW, ...SVG_ELEMENTS_ALLOW.map(name => `svg:${name}`), ...MATH_ELEMENTS_ALLOW.map(name => `math:${name}`) ], allowAttributes: mergeMaps([ Object.fromEntries(HTML_ATTRIBUTES_ALLOW.map(name => [name, ['*']])), Object.fromEntries(SVG_ATTRIBUTES_ALLOW.map(name => [name, ['svg:*']])), Object.fromEntries(MATH_ATTRIBUTES_ALLOW.map(name => [name, ['math:*']])) ]) }; /* EXPORT */ export { HTML_ELEMENTS, SVG_ELEMENTS, MATH_ELEMENTS }; export { HTML_ATTRIBUTES, SVG_ATTRIBUTES, MATH_ATTRIBUTES }; export { NAMESPACES, NAMESPACES_ELEMENTS, NAMESPACES_ATTRIBUTES, NAMESPACES_ROOTS, NAMESPACES_PREFIXES }; export { FUNKY_TAG_NAMES }; export { DEFAULTS };