+376
@@ -0,0 +1,376 @@
|
||||
import { freeze } from './utils.js';
|
||||
|
||||
export 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',
|
||||
] as const);
|
||||
|
||||
export 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',
|
||||
] as const);
|
||||
|
||||
export 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',
|
||||
]);
|
||||
|
||||
export const xml = freeze([
|
||||
'xlink:href',
|
||||
'xml:id',
|
||||
'xlink:title',
|
||||
'xml:space',
|
||||
'xmlns:xlink',
|
||||
] as const);
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
|
||||
import type { TrustedTypePolicy } from 'trusted-types/lib/index.js';
|
||||
|
||||
/**
|
||||
* Configuration to control DOMPurify behavior.
|
||||
*/
|
||||
export interface Config {
|
||||
/**
|
||||
* Extend the existing array of allowed attributes.
|
||||
* Can be an array of attribute names, or a function that receives
|
||||
* the attribute name and tag name to determine if the attribute is allowed.
|
||||
*/
|
||||
ADD_ATTR?:
|
||||
| string[]
|
||||
| ((attributeName: string, tagName: string) => boolean)
|
||||
| undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing array of elements that can use Data URIs.
|
||||
*/
|
||||
ADD_DATA_URI_TAGS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing array of allowed tags.
|
||||
* Can be an array of tag names, or a function that receives
|
||||
* the tag name to determine if the tag is allowed.
|
||||
*/
|
||||
ADD_TAGS?: string[] | ((tagName: string) => boolean) | undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing array of elements that are safe for URI-like values (be careful, XSS risk).
|
||||
*/
|
||||
ADD_URI_SAFE_ATTR?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow ARIA attributes, leave other safe HTML as is (default is true).
|
||||
*/
|
||||
ALLOW_ARIA_ATTR?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow HTML5 data attributes, leave other safe HTML as is (default is true).
|
||||
*/
|
||||
ALLOW_DATA_ATTR?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow external protocol handlers in URL attributes (default is false, be careful, XSS risk).
|
||||
* By default only `http`, `https`, `ftp`, `ftps`, `tel`, `mailto`, `callto`, `sms`, `cid` and `xmpp` are allowed.
|
||||
*/
|
||||
ALLOW_UNKNOWN_PROTOCOLS?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Decide if self-closing tags in attributes are allowed.
|
||||
* Usually removed due to a mXSS issue in jQuery 3.0.
|
||||
*/
|
||||
ALLOW_SELF_CLOSE_IN_ATTR?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow only specific attributes.
|
||||
*/
|
||||
ALLOWED_ATTR?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow only specific elements.
|
||||
*/
|
||||
ALLOWED_TAGS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow only specific namespaces. Defaults to:
|
||||
* - `http://www.w3.org/1999/xhtml`
|
||||
* - `http://www.w3.org/2000/svg`
|
||||
* - `http://www.w3.org/1998/Math/MathML`
|
||||
*/
|
||||
ALLOWED_NAMESPACES?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow specific protocols handlers in URL attributes via regex (be careful, XSS risk).
|
||||
* Default RegExp:
|
||||
* ```
|
||||
* /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
|
||||
* ```
|
||||
*/
|
||||
ALLOWED_URI_REGEXP?: RegExp | undefined;
|
||||
|
||||
/**
|
||||
* Define how custom elements are handled.
|
||||
*/
|
||||
CUSTOM_ELEMENT_HANDLING?: {
|
||||
/**
|
||||
* Regular expression or function to match to allowed elements.
|
||||
* Default is null (disallow any custom elements).
|
||||
*/
|
||||
tagNameCheck?: RegExp | ((tagName: string) => boolean) | null | undefined;
|
||||
|
||||
/**
|
||||
* Regular expression or function to match to allowed attributes.
|
||||
* Default is null (disallow any attributes not on the allow list).
|
||||
*/
|
||||
attributeNameCheck?:
|
||||
| RegExp
|
||||
| ((attributeName: string, tagName?: string) => boolean)
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
/**
|
||||
* Allow custom elements derived from built-ins if they pass `tagNameCheck`. Default is false.
|
||||
*/
|
||||
allowCustomizedBuiltInElements?: boolean | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add attributes to block-list.
|
||||
*/
|
||||
FORBID_ATTR?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Add child elements to be removed when their parent is removed.
|
||||
*/
|
||||
FORBID_CONTENTS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing or default array of forbidden content elements.
|
||||
*/
|
||||
ADD_FORBID_CONTENTS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Add elements to block-list.
|
||||
*/
|
||||
FORBID_TAGS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Glue elements like style, script or others to `document.body` and prevent unintuitive browser behavior in several edge-cases (default is false).
|
||||
*/
|
||||
FORCE_BODY?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Map of non-standard HTML element names to support. Map to true to enable support. For example:
|
||||
*
|
||||
* ```
|
||||
* HTML_INTEGRATION_POINTS: { foreignobject: true }
|
||||
* ```
|
||||
*/
|
||||
HTML_INTEGRATION_POINTS?: Record<string, boolean> | undefined;
|
||||
|
||||
/**
|
||||
* Sanitize a node "in place", which is much faster depending on how you use DOMPurify.
|
||||
*/
|
||||
IN_PLACE?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Keep an element's content when the element is removed (default is true).
|
||||
*/
|
||||
KEEP_CONTENT?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Map of MathML element names to support. Map to true to enable support. For example:
|
||||
*
|
||||
* ```
|
||||
* MATHML_TEXT_INTEGRATION_POINTS: { mtext: true }
|
||||
* ```
|
||||
*/
|
||||
MATHML_TEXT_INTEGRATION_POINTS?: Record<string, boolean> | undefined;
|
||||
|
||||
/**
|
||||
* Change the default namespace from HTML to something different.
|
||||
*/
|
||||
NAMESPACE?: string | undefined;
|
||||
|
||||
/**
|
||||
* Change the parser type so sanitized data is treated as XML and not as HTML, which is the default.
|
||||
*/
|
||||
PARSER_MEDIA_TYPE?: DOMParserSupportedType | undefined;
|
||||
|
||||
/**
|
||||
* Return a DOM `DocumentFragment` instead of an HTML string (default is false).
|
||||
*/
|
||||
RETURN_DOM_FRAGMENT?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Return a DOM `HTMLBodyElement` instead of an HTML string (default is false).
|
||||
*/
|
||||
RETURN_DOM?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Return a TrustedHTML object instead of a string if possible.
|
||||
*/
|
||||
RETURN_TRUSTED_TYPE?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Strip `{{ ... }}`, `${ ... }` and `<% ... %>` to make output safe for template systems.
|
||||
* Be careful please, this mode is not recommended for production usage.
|
||||
* Allowing template parsing in user-controlled HTML is not advised at all.
|
||||
* Only use this mode if there is really no alternative.
|
||||
*/
|
||||
SAFE_FOR_TEMPLATES?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Change how e.g. comments containing risky HTML characters are treated.
|
||||
* Be very careful, this setting should only be set to `false` if you really only handle
|
||||
* HTML and nothing else, no SVG, MathML or the like.
|
||||
* Otherwise, changing from `true` to `false` will lead to XSS in this or some other way.
|
||||
*/
|
||||
SAFE_FOR_XML?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Use DOM Clobbering protection on output (default is true, handle with care, minor XSS risks here).
|
||||
*/
|
||||
SANITIZE_DOM?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Enforce strict DOM Clobbering protection via namespace isolation (default is false).
|
||||
* When enabled, isolates the namespace of named properties (i.e., `id` and `name` attributes)
|
||||
* from JS variables by prefixing them with the string `user-content-`
|
||||
*/
|
||||
SANITIZE_NAMED_PROPS?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Supplied policy must define `createHTML` and `createScriptURL`.
|
||||
*/
|
||||
TRUSTED_TYPES_POLICY?: TrustedTypePolicy | undefined;
|
||||
|
||||
/**
|
||||
* Controls categories of allowed elements.
|
||||
*
|
||||
* Note that the `USE_PROFILES` setting will override the `ALLOWED_TAGS` setting
|
||||
* so don't use them together.
|
||||
*/
|
||||
USE_PROFILES?: false | UseProfilesConfig | undefined;
|
||||
|
||||
/**
|
||||
* Return entire document including <html> tags (default is false).
|
||||
*/
|
||||
WHOLE_DOCUMENT?: boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines categories of allowed elements.
|
||||
*/
|
||||
export interface UseProfilesConfig {
|
||||
/**
|
||||
* Allow all safe MathML elements.
|
||||
*/
|
||||
mathMl?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow all safe SVG elements.
|
||||
*/
|
||||
svg?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow all safe SVG Filters.
|
||||
*/
|
||||
svgFilters?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow all safe HTML elements.
|
||||
*/
|
||||
html?: boolean | undefined;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
/*! @license DOMPurify VERSION | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/VERSION/LICENSE */
|
||||
+2184
File diff suppressed because it is too large
Load Diff
+17
@@ -0,0 +1,17 @@
|
||||
import { seal } from './utils.js';
|
||||
|
||||
// eslint-disable-next-line unicorn/better-regex
|
||||
export const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
||||
export const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
||||
export const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
|
||||
export const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
|
||||
export const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
||||
export 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
|
||||
);
|
||||
export const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
||||
export const ATTR_WHITESPACE = seal(
|
||||
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
||||
);
|
||||
export const DOCTYPE_NAME = seal(/^html$/i);
|
||||
export const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
import { freeze } from './utils.js';
|
||||
|
||||
export const html = 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',
|
||||
] as const);
|
||||
|
||||
export const svg = 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',
|
||||
] as const);
|
||||
|
||||
export const svgFilters = freeze([
|
||||
'feBlend',
|
||||
'feColorMatrix',
|
||||
'feComponentTransfer',
|
||||
'feComposite',
|
||||
'feConvolveMatrix',
|
||||
'feDiffuseLighting',
|
||||
'feDisplacementMap',
|
||||
'feDistantLight',
|
||||
'feDropShadow',
|
||||
'feFlood',
|
||||
'feFuncA',
|
||||
'feFuncB',
|
||||
'feFuncG',
|
||||
'feFuncR',
|
||||
'feGaussianBlur',
|
||||
'feImage',
|
||||
'feMerge',
|
||||
'feMergeNode',
|
||||
'feMorphology',
|
||||
'feOffset',
|
||||
'fePointLight',
|
||||
'feSpecularLighting',
|
||||
'feSpotLight',
|
||||
'feTile',
|
||||
'feTurbulence',
|
||||
] as const);
|
||||
|
||||
// List of SVG elements that are disallowed by default.
|
||||
// We still need to know them so that we can do namespace
|
||||
// checks properly in case one wants to add them to
|
||||
// allow-list.
|
||||
export const svgDisallowed = freeze([
|
||||
'animate',
|
||||
'color-profile',
|
||||
'cursor',
|
||||
'discard',
|
||||
'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',
|
||||
] as const);
|
||||
|
||||
export const mathMl = freeze([
|
||||
'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',
|
||||
'mprescripts',
|
||||
] as const);
|
||||
|
||||
// Similarly to SVG, we want to know all MathML elements,
|
||||
// even those that we disallow by default.
|
||||
export const mathMlDisallowed = freeze([
|
||||
'maction',
|
||||
'maligngroup',
|
||||
'malignmark',
|
||||
'mlongdiv',
|
||||
'mscarries',
|
||||
'mscarry',
|
||||
'msgroup',
|
||||
'mstack',
|
||||
'msline',
|
||||
'msrow',
|
||||
'semantics',
|
||||
'annotation',
|
||||
'annotation-xml',
|
||||
'mprescripts',
|
||||
'none',
|
||||
] as const);
|
||||
|
||||
export const text = freeze(['#text'] as const);
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
const {
|
||||
entries,
|
||||
setPrototypeOf,
|
||||
isFrozen,
|
||||
getPrototypeOf,
|
||||
getOwnPropertyDescriptor,
|
||||
} = Object;
|
||||
|
||||
let { freeze, seal, create } = Object; // eslint-disable-line import/no-mutable-exports
|
||||
let { apply, construct } = typeof Reflect !== 'undefined' && Reflect;
|
||||
|
||||
if (!freeze) {
|
||||
freeze = function <T>(x: T): T {
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
if (!seal) {
|
||||
seal = function <T>(x: T): T {
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
if (!apply) {
|
||||
apply = function <T>(
|
||||
func: (thisArg: any, ...args: any[]) => T,
|
||||
thisArg: any,
|
||||
...args: any[]
|
||||
): T {
|
||||
return func.apply(thisArg, args);
|
||||
};
|
||||
}
|
||||
|
||||
if (!construct) {
|
||||
construct = function <T>(Func: new (...args: any[]) => T, ...args: any[]): T {
|
||||
return new Func(...args);
|
||||
};
|
||||
}
|
||||
|
||||
const arrayForEach = unapply(Array.prototype.forEach);
|
||||
const arrayIndexOf = unapply(Array.prototype.indexOf);
|
||||
const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
|
||||
const arrayPop = unapply(Array.prototype.pop);
|
||||
const arrayPush = unapply(Array.prototype.push);
|
||||
const arraySlice = unapply(Array.prototype.slice);
|
||||
const arraySplice = unapply(Array.prototype.splice);
|
||||
const arrayIsArray = Array.isArray;
|
||||
|
||||
const stringToLowerCase = unapply(String.prototype.toLowerCase);
|
||||
const stringToString = unapply(String.prototype.toString);
|
||||
const stringMatch = unapply(String.prototype.match);
|
||||
const stringReplace = unapply(String.prototype.replace);
|
||||
const stringIndexOf = unapply(String.prototype.indexOf);
|
||||
const stringTrim = unapply(String.prototype.trim);
|
||||
|
||||
const numberToString = unapply(Number.prototype.toString);
|
||||
const booleanToString = unapply(Boolean.prototype.toString);
|
||||
const bigintToString =
|
||||
typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
|
||||
const symbolToString =
|
||||
typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
|
||||
|
||||
const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
|
||||
const objectToString = unapply(Object.prototype.toString);
|
||||
|
||||
const regExpTest = unapply(RegExp.prototype.test);
|
||||
|
||||
const typeErrorCreate = unconstruct(TypeError);
|
||||
|
||||
/**
|
||||
* Creates a new function that calls the given function with a specified thisArg and arguments.
|
||||
*
|
||||
* @param func - The function to be wrapped and called.
|
||||
* @returns A new function that calls the given function with a specified thisArg and arguments.
|
||||
*/
|
||||
function unapply<T>(
|
||||
func: (thisArg: any, ...args: any[]) => T
|
||||
): (thisArg: any, ...args: any[]) => T {
|
||||
return (thisArg: any, ...args: any[]): T => {
|
||||
if (thisArg instanceof RegExp) {
|
||||
thisArg.lastIndex = 0;
|
||||
}
|
||||
|
||||
return apply(func, thisArg, args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new function that constructs an instance of the given constructor function with the provided arguments.
|
||||
*
|
||||
* @param func - The constructor function to be wrapped and called.
|
||||
* @returns A new function that constructs an instance of the given constructor function with the provided arguments.
|
||||
*/
|
||||
function unconstruct<T>(
|
||||
Func: new (...args: any[]) => T
|
||||
): (...args: any[]) => T {
|
||||
return (...args: any[]): T => construct(Func, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add properties to a lookup table
|
||||
*
|
||||
* @param set - The set to which elements will be added.
|
||||
* @param array - The array containing elements to be added to the set.
|
||||
* @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
|
||||
* @returns The modified set with added elements.
|
||||
*/
|
||||
function addToSet(
|
||||
set: Record<string, boolean>,
|
||||
array: readonly unknown[],
|
||||
transformCaseFunc: ReturnType<typeof unapply<string>> = stringToLowerCase
|
||||
): Record<string, boolean> {
|
||||
if (setPrototypeOf) {
|
||||
// Make 'in' and truthy checks like Boolean(set.constructor)
|
||||
// independent of any properties defined on Object.prototype.
|
||||
// Prevent prototype setters from intercepting set as a this value.
|
||||
setPrototypeOf(set, null);
|
||||
}
|
||||
|
||||
if (!arrayIsArray(array)) {
|
||||
return set;
|
||||
}
|
||||
|
||||
let l = array.length;
|
||||
while (l--) {
|
||||
let element = array[l];
|
||||
|
||||
if (typeof element === 'string') {
|
||||
const lcElement = transformCaseFunc(element);
|
||||
|
||||
if (lcElement !== element) {
|
||||
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
||||
if (!isFrozen(array)) {
|
||||
(array as unknown[])[l] = lcElement;
|
||||
}
|
||||
|
||||
element = lcElement;
|
||||
}
|
||||
}
|
||||
|
||||
set[element as string] = true;
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up an array to harden against CSPP
|
||||
*
|
||||
* @param array - The array to be cleaned.
|
||||
* @returns The cleaned version of the array
|
||||
*/
|
||||
function cleanArray<T>(array: T[]): Array<T | null> {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
const isPropertyExist = objectHasOwnProperty(array, index);
|
||||
|
||||
if (!isPropertyExist) {
|
||||
array[index] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shallow clone an object
|
||||
*
|
||||
* @param object - The object to be cloned.
|
||||
* @returns A new object that copies the original.
|
||||
*/
|
||||
function clone<T extends Record<string, any>>(object: T): T {
|
||||
const newObject = create(null);
|
||||
|
||||
for (const [property, value] of entries(object)) {
|
||||
const isPropertyExist = objectHasOwnProperty(object, property);
|
||||
|
||||
if (isPropertyExist) {
|
||||
if (arrayIsArray(value)) {
|
||||
newObject[property] = cleanArray(value);
|
||||
} else if (
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
value.constructor === Object
|
||||
) {
|
||||
newObject[property] = clone(value);
|
||||
} else {
|
||||
newObject[property] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert non-node values into strings without depending on direct property access.
|
||||
*
|
||||
* @param value - The value to stringify.
|
||||
* @returns A string representation of the provided value.
|
||||
*/
|
||||
function stringifyValue(value: unknown): string {
|
||||
switch (typeof value) {
|
||||
case 'string': {
|
||||
return value;
|
||||
}
|
||||
|
||||
case 'number': {
|
||||
return numberToString(value);
|
||||
}
|
||||
|
||||
case 'boolean': {
|
||||
return booleanToString(value);
|
||||
}
|
||||
|
||||
case 'bigint': {
|
||||
return bigintToString ? bigintToString(value) : '0';
|
||||
}
|
||||
|
||||
case 'symbol': {
|
||||
return symbolToString ? symbolToString(value) : 'Symbol()';
|
||||
}
|
||||
|
||||
case 'undefined': {
|
||||
return objectToString(value);
|
||||
}
|
||||
|
||||
case 'function':
|
||||
case 'object': {
|
||||
if (value === null) {
|
||||
return objectToString(value);
|
||||
}
|
||||
|
||||
const valueAsRecord = value as Record<string, any>;
|
||||
const valueToString = lookupGetter(valueAsRecord, 'toString');
|
||||
|
||||
if (typeof valueToString === 'function') {
|
||||
const stringified = valueToString(valueAsRecord);
|
||||
|
||||
return typeof stringified === 'string'
|
||||
? stringified
|
||||
: objectToString(stringified);
|
||||
}
|
||||
|
||||
return objectToString(value);
|
||||
}
|
||||
|
||||
default: {
|
||||
return objectToString(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method automatically checks if the prop is function or getter and behaves accordingly.
|
||||
*
|
||||
* @param object - The object to look up the getter function in its prototype chain.
|
||||
* @param prop - The property name for which to find the getter function.
|
||||
* @returns The getter function found in the prototype chain or a fallback function.
|
||||
*/
|
||||
function lookupGetter<T extends Record<string, any>>(
|
||||
object: T,
|
||||
prop: string
|
||||
): ReturnType<typeof unapply<any>> | (() => null) {
|
||||
while (object !== null) {
|
||||
const desc = getOwnPropertyDescriptor(object, prop);
|
||||
|
||||
if (desc) {
|
||||
if (desc.get) {
|
||||
return unapply(desc.get);
|
||||
}
|
||||
|
||||
if (typeof desc.value === 'function') {
|
||||
return unapply(desc.value);
|
||||
}
|
||||
}
|
||||
|
||||
object = getPrototypeOf(object);
|
||||
}
|
||||
|
||||
function fallbackValue(): null {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
function isRegex(value: unknown): value is RegExp {
|
||||
try {
|
||||
regExpTest(value as RegExp, '');
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
// Array
|
||||
arrayForEach,
|
||||
arrayIndexOf,
|
||||
arrayIsArray,
|
||||
arrayLastIndexOf,
|
||||
arrayPop,
|
||||
arrayPush,
|
||||
arraySlice,
|
||||
arraySplice,
|
||||
// Object
|
||||
entries,
|
||||
freeze,
|
||||
getPrototypeOf,
|
||||
getOwnPropertyDescriptor,
|
||||
isFrozen,
|
||||
setPrototypeOf,
|
||||
seal,
|
||||
clone,
|
||||
create,
|
||||
objectHasOwnProperty,
|
||||
objectToString,
|
||||
// RegExp
|
||||
regExpTest,
|
||||
isRegex,
|
||||
// String
|
||||
stringIndexOf,
|
||||
stringMatch,
|
||||
stringReplace,
|
||||
stringToLowerCase,
|
||||
stringToString,
|
||||
stringTrim,
|
||||
// Other conversion
|
||||
stringifyValue,
|
||||
// Errors
|
||||
typeErrorCreate,
|
||||
// Other
|
||||
lookupGetter,
|
||||
addToSet,
|
||||
// Reflect
|
||||
unapply,
|
||||
unconstruct,
|
||||
};
|
||||
Reference in New Issue
Block a user