dompurify 3.3.3 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/purify.cjs.d.ts +4 -3
- package/dist/purify.cjs.js +26 -25
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.d.mts +4 -3
- package/dist/purify.es.mjs +26 -25
- package/dist/purify.es.mjs.map +1 -1
- package/dist/purify.js +26 -25
- package/dist/purify.js.map +1 -1
- package/dist/purify.min.js +2 -2
- package/dist/purify.min.js.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
# DOMPurify
|
|
2
2
|
|
|
3
|
-
[](http://badge.fury.io/js/dompurify) ](http://badge.fury.io/js/dompurify)  [](https://www.npmjs.com/package/dompurify) [](https://github.com/cure53/DOMPurify/network/dependents) [](https://github.com/cure53/DOMPurify/blob/main/LICENSE)
|
|
4
|
+
|
|
5
|
+
 [](https://cloudback.it) [](https://www.bestpractices.dev/projects/12162) [](https://scorecard.dev/viewer/?uri=github.com/cure53/DOMPurify)
|
|
4
6
|
|
|
5
7
|
DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG.
|
|
6
8
|
|
|
7
|
-
It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.
|
|
9
|
+
It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.4.0**.
|
|
8
10
|
|
|
9
11
|
DOMPurify runs as JavaScript and works in all modern browsers (Safari (10+), Opera (15+), Edge, Firefox and Chrome - as well as almost anything else using Blink, Gecko or WebKit). It doesn't break on MSIE or other legacy browsers. It simply does nothing.
|
|
10
12
|
|
|
11
13
|
**Note that [DOMPurify v2.5.9](https://github.com/cure53/DOMPurify/releases/tag/2.5.9) is the latest version supporting MSIE. For important security updates compatible with MSIE, please use the [2.x branch](https://github.com/cure53/DOMPurify/tree/2.x).**
|
|
12
14
|
|
|
13
|
-
Our automated tests cover [
|
|
15
|
+
Our automated tests cover [12 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L3). We also cover Node.js v20.x, v22.x, 24.x and v25.x, running DOMPurify on [jsdom](https://github.com/jsdom/jsdom). Older Node versions are known to work as well, but hey... no guarantees.
|
|
14
16
|
|
|
15
17
|
DOMPurify is written by security people who have vast background in web attacks and XSS. Fear not. For more details please also read about our [Security Goals & Threat Model](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model). Please, read it. Like, really.
|
|
16
18
|
|
|
17
19
|
## What does it do?
|
|
18
20
|
|
|
19
|
-
DOMPurify sanitizes HTML and prevents XSS attacks. You can feed DOMPurify with string full of dirty HTML and it will return a string (unless configured otherwise) with clean HTML. DOMPurify will strip out everything that contains dangerous HTML and thereby prevent XSS attacks and other nastiness. It's also damn bloody fast. We use the technologies the browser provides and turn them into an XSS filter. The faster your browser, the faster DOMPurify will be.
|
|
21
|
+
DOMPurify sanitizes HTML and prevents XSS attacks. You can feed DOMPurify with e.g. a string full of dirty HTML and it will return a string (unless configured otherwise) with clean HTML. DOMPurify will strip out everything that contains dangerous HTML and thereby prevent XSS attacks and other nastiness. It's also damn bloody fast. We use the technologies the browser provides and turn them into an XSS filter. The faster your browser, the faster DOMPurify will be.
|
|
20
22
|
|
|
21
23
|
## How do I use it?
|
|
22
24
|
|
package/dist/purify.cjs.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.
|
|
1
|
+
/*! @license DOMPurify 3.4.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.0/LICENSE */
|
|
2
2
|
|
|
3
|
-
import { TrustedTypePolicy,
|
|
3
|
+
import { TrustedTypePolicy, TrustedTypesWindow, TrustedHTML } from 'trusted-types/lib/index.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Configuration to control DOMPurify behavior.
|
|
@@ -444,7 +444,8 @@ type WindowLike = Pick<typeof globalThis, 'DocumentFragment' | 'HTMLTemplateElem
|
|
|
444
444
|
MozNamedAttrMap?: typeof window.NamedNodeMap;
|
|
445
445
|
} & Pick<TrustedTypesWindow, 'trustedTypes'>;
|
|
446
446
|
|
|
447
|
-
export {
|
|
447
|
+
export { _default as default };
|
|
448
|
+
export type { Config, DOMPurify, DocumentFragmentHook, ElementHook, HookName, NodeHook, RemovedAttribute, RemovedElement, UponSanitizeAttributeHook, UponSanitizeAttributeHookEvent, UponSanitizeElementHook, UponSanitizeElementHookEvent, WindowLike };
|
|
448
449
|
|
|
449
450
|
// @ts-ignore
|
|
450
451
|
export = _default;
|
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.
|
|
1
|
+
/*! @license DOMPurify 3.4.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.0/LICENSE */
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -201,7 +201,7 @@ const text = freeze(['#text']);
|
|
|
201
201
|
|
|
202
202
|
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', 'slot']);
|
|
203
203
|
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']);
|
|
204
|
-
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', '
|
|
204
|
+
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']);
|
|
205
205
|
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
206
206
|
|
|
207
207
|
// eslint-disable-next-line unicorn/better-regex
|
|
@@ -236,20 +236,11 @@ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
|
236
236
|
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
|
237
237
|
const NODE_TYPE = {
|
|
238
238
|
element: 1,
|
|
239
|
-
attribute: 2,
|
|
240
239
|
text: 3,
|
|
241
|
-
cdataSection: 4,
|
|
242
|
-
entityReference: 5,
|
|
243
|
-
// Deprecated
|
|
244
|
-
entityNode: 6,
|
|
245
240
|
// Deprecated
|
|
246
241
|
progressingInstruction: 7,
|
|
247
242
|
comment: 8,
|
|
248
|
-
document: 9
|
|
249
|
-
documentType: 10,
|
|
250
|
-
documentFragment: 11,
|
|
251
|
-
notation: 12 // Deprecated
|
|
252
|
-
};
|
|
243
|
+
document: 9};
|
|
253
244
|
const getGlobal = function getGlobal() {
|
|
254
245
|
return typeof window === 'undefined' ? null : window;
|
|
255
246
|
};
|
|
@@ -307,7 +298,7 @@ const _createHooksMap = function _createHooksMap() {
|
|
|
307
298
|
function createDOMPurify() {
|
|
308
299
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
309
300
|
const DOMPurify = root => createDOMPurify(root);
|
|
310
|
-
DOMPurify.version = '3.
|
|
301
|
+
DOMPurify.version = '3.4.0';
|
|
311
302
|
DOMPurify.removed = [];
|
|
312
303
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
313
304
|
// Not running in a browser, provide a factory function
|
|
@@ -583,7 +574,7 @@ function createDOMPurify() {
|
|
|
583
574
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
584
575
|
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
|
|
585
576
|
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
|
|
586
|
-
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING ||
|
|
577
|
+
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || create(null);
|
|
587
578
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
588
579
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
589
580
|
}
|
|
@@ -623,13 +614,10 @@ function createDOMPurify() {
|
|
|
623
614
|
addToSet(ALLOWED_ATTR, xml);
|
|
624
615
|
}
|
|
625
616
|
}
|
|
626
|
-
/*
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
|
|
631
|
-
EXTRA_ELEMENT_HANDLING.attributeCheck = null;
|
|
632
|
-
}
|
|
617
|
+
/* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
|
|
618
|
+
* leaking across calls when switching from function to array config */
|
|
619
|
+
EXTRA_ELEMENT_HANDLING.tagCheck = null;
|
|
620
|
+
EXTRA_ELEMENT_HANDLING.attributeCheck = null;
|
|
633
621
|
/* Merge configuration parameters */
|
|
634
622
|
if (cfg.ADD_TAGS) {
|
|
635
623
|
if (typeof cfg.ADD_TAGS === 'function') {
|
|
@@ -952,6 +940,11 @@ function createDOMPurify() {
|
|
|
952
940
|
_forceRemove(currentNode);
|
|
953
941
|
return true;
|
|
954
942
|
}
|
|
943
|
+
/* Remove risky CSS construction leading to mXSS */
|
|
944
|
+
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
|
|
945
|
+
_forceRemove(currentNode);
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
955
948
|
/* Remove any occurrence of processing instructions */
|
|
956
949
|
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
|
|
957
950
|
_forceRemove(currentNode);
|
|
@@ -963,7 +956,7 @@ function createDOMPurify() {
|
|
|
963
956
|
return true;
|
|
964
957
|
}
|
|
965
958
|
/* Remove element if anything forbids its presence */
|
|
966
|
-
if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) &&
|
|
959
|
+
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
|
|
967
960
|
/* Check if we have a custom element to handle */
|
|
968
961
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
969
962
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
@@ -1202,7 +1195,7 @@ function createDOMPurify() {
|
|
|
1202
1195
|
*
|
|
1203
1196
|
* @param fragment to iterate over recursively
|
|
1204
1197
|
*/
|
|
1205
|
-
const
|
|
1198
|
+
const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
|
|
1206
1199
|
let shadowNode = null;
|
|
1207
1200
|
const shadowIterator = _createNodeIterator(fragment);
|
|
1208
1201
|
/* Execute a hook if present */
|
|
@@ -1216,7 +1209,7 @@ function createDOMPurify() {
|
|
|
1216
1209
|
_sanitizeAttributes(shadowNode);
|
|
1217
1210
|
/* Deep shadow DOM detected */
|
|
1218
1211
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1219
|
-
|
|
1212
|
+
_sanitizeShadowDOM2(shadowNode.content);
|
|
1220
1213
|
}
|
|
1221
1214
|
}
|
|
1222
1215
|
/* Execute a hook if present */
|
|
@@ -1311,7 +1304,7 @@ function createDOMPurify() {
|
|
|
1311
1304
|
_sanitizeAttributes(currentNode);
|
|
1312
1305
|
/* Shadow DOM detected, sanitize it */
|
|
1313
1306
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1314
|
-
|
|
1307
|
+
_sanitizeShadowDOM2(currentNode.content);
|
|
1315
1308
|
}
|
|
1316
1309
|
}
|
|
1317
1310
|
/* If we sanitized `dirty` in-place, return it. */
|
|
@@ -1320,6 +1313,14 @@ function createDOMPurify() {
|
|
|
1320
1313
|
}
|
|
1321
1314
|
/* Return sanitized string or DOM */
|
|
1322
1315
|
if (RETURN_DOM) {
|
|
1316
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
1317
|
+
body.normalize();
|
|
1318
|
+
let html = body.innerHTML;
|
|
1319
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1320
|
+
html = stringReplace(html, expr, ' ');
|
|
1321
|
+
});
|
|
1322
|
+
body.innerHTML = html;
|
|
1323
|
+
}
|
|
1323
1324
|
if (RETURN_DOM_FRAGMENT) {
|
|
1324
1325
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1325
1326
|
while (body.firstChild) {
|