Update DOMPurify to version 3.1.4

This commit is contained in:
Théophile Diot 2024-05-25 17:11:45 +01:00
parent 51194f0fed
commit 58025b5ff4
No known key found for this signature in database
GPG key ID: 248FEA4BAE400D06
4 changed files with 74 additions and 15 deletions

File diff suppressed because one or more lines are too long

View file

@ -74,6 +74,9 @@ export const html = freeze([
'pattern',
'placeholder',
'playsinline',
'popover',
'popovertarget',
'popovertargetaction',
'poster',
'preload',
'pubdate',

View file

@ -15,6 +15,7 @@ import {
stringToString,
stringIndexOf,
stringTrim,
numberIsNaN,
regExpTest,
typeErrorCreate,
lookupGetter,
@ -22,6 +23,22 @@ import {
objectHasOwnProperty,
} from './utils.js';
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
const NODE_TYPE = {
element: 1,
attribute: 2,
text: 3,
cdataSection: 4,
entityReference: 5, // Deprecated
entityNode: 6, // Deprecated
progressingInstruction: 7,
comment: 8,
document: 9,
documentType: 10,
documentFragment: 11,
notation: 12, // Deprecated
};
const getGlobal = function () {
return typeof window === 'undefined' ? null : window;
};
@ -88,7 +105,11 @@ function createDOMPurify(window = getGlobal()) {
*/
DOMPurify.removed = [];
if (!window || !window.document || window.document.nodeType !== 9) {
if (
!window ||
!window.document ||
window.document.nodeType !== NODE_TYPE.document
) {
// Not running in a browser, provide a factory function
// so that you can pass your own Window
DOMPurify.isSupported = false;
@ -1024,7 +1045,7 @@ function createDOMPurify(window = getGlobal()) {
}
/* Remove any ocurrence of processing instructions */
if (currentNode.nodeType === 7) {
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
_forceRemove(currentNode);
return true;
}
@ -1032,7 +1053,7 @@ function createDOMPurify(window = getGlobal()) {
/* Remove any kind of possibly harmful comments */
if (
SAFE_FOR_XML &&
currentNode.nodeType === 8 &&
currentNode.nodeType === NODE_TYPE.comment &&
regExpTest(/<[/\w]/g, currentNode.data)
) {
_forceRemove(currentNode);
@ -1096,7 +1117,7 @@ function createDOMPurify(window = getGlobal()) {
}
/* Sanitize element content to be template-safe */
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
/* Get the element's text content */
content = currentNode.textContent;
@ -1130,7 +1151,10 @@ function createDOMPurify(window = getGlobal()) {
if (
SANITIZE_DOM &&
(lcName === 'id' || lcName === 'name') &&
(value in document || value in formElement)
(value in document ||
value in formElement ||
value === '__depth' ||
value === '__removalCount')
) {
return false;
}
@ -1288,6 +1312,12 @@ function createDOMPurify(window = getGlobal()) {
continue;
}
/* Work around a security issue with comments inside attributes */
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
_removeAttribute(name, currentNode);
continue;
}
/* Sanitize attribute content to be template-safe */
if (SAFE_FOR_TEMPLATES) {
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], (expr) => {
@ -1348,7 +1378,11 @@ function createDOMPurify(window = getGlobal()) {
currentNode.setAttribute(name, value);
}
arrayPop(DOMPurify.removed);
if (_isClobbered(currentNode)) {
_forceRemove(currentNode);
} else {
arrayPop(DOMPurify.removed);
}
} catch (_) {}
}
@ -1380,7 +1414,7 @@ function createDOMPurify(window = getGlobal()) {
const parentNode = getParentNode(shadowNode);
/* Set the nesting depth of an element */
if (shadowNode.nodeType === 1) {
if (shadowNode.nodeType === NODE_TYPE.element) {
if (parentNode && parentNode.__depth) {
/*
We want the depth of the node in the original tree, which can
@ -1393,8 +1427,15 @@ function createDOMPurify(window = getGlobal()) {
}
}
/* Remove an element if nested too deeply to avoid mXSS */
if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
/*
* Remove an element if nested too deeply to avoid mXSS
* or if the __depth might have been tampered with
*/
if (
shadowNode.__depth >= MAX_NESTING_DEPTH ||
shadowNode.__depth < 0 ||
numberIsNaN(shadowNode.__depth)
) {
_forceRemove(shadowNode);
}
@ -1478,7 +1519,10 @@ function createDOMPurify(window = getGlobal()) {
elements being stripped by the parser */
body = _initDocument('<!---->');
importedNode = body.ownerDocument.importNode(dirty, true);
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
if (
importedNode.nodeType === NODE_TYPE.element &&
importedNode.nodeName === 'BODY'
) {
/* Node is already a body, use as is */
body = importedNode;
} else if (importedNode.nodeName === 'HTML') {
@ -1528,7 +1572,7 @@ function createDOMPurify(window = getGlobal()) {
const parentNode = getParentNode(currentNode);
/* Set the nesting depth of an element */
if (currentNode.nodeType === 1) {
if (currentNode.nodeType === NODE_TYPE.element) {
if (parentNode && parentNode.__depth) {
/*
We want the depth of the node in the original tree, which can
@ -1541,8 +1585,15 @@ function createDOMPurify(window = getGlobal()) {
}
}
/* Remove an element if nested too deeply to avoid mXSS */
if (currentNode.__depth >= MAX_NESTING_DEPTH) {
/*
* Remove an element if nested too deeply to avoid mXSS
* or if the __depth might have been tampered with
*/
if (
currentNode.__depth >= MAX_NESTING_DEPTH ||
currentNode.__depth < 0 ||
numberIsNaN(currentNode.__depth)
) {
_forceRemove(currentNode);
}

View file

@ -52,6 +52,11 @@ const regExpTest = unapply(RegExp.prototype.test);
const typeErrorCreate = unconstruct(TypeError);
export function numberIsNaN(x) {
// eslint-disable-next-line unicorn/prefer-number-properties
return typeof x === 'number' && isNaN(x);
}
/**
* Creates a new function that calls the given function with a specified thisArg and arguments.
*