[Pkg-javascript-devel] Bug#1084983: Bug#1084983: node-dompurify: CVE-2024-47875
Yadd
yadd at debian.org
Sat Oct 12 15:14:14 BST 2024
Hi,
here is a debdiff for bookworm
Best regards,
Xavier
On 10/12/24 11:36, Moritz Mühlenhoff wrote:
> Source: node-dompurify
> X-Debbugs-CC: team at security.debian.org
> Severity: grave
> Tags: security
>
> Hi,
>
> The following vulnerability was published for node-dompurify.
>
> CVE-2024-47875[0]:
> | DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for
> | HTML, MathML and SVG. DOMpurify was vulnerable to nesting-based
> | mXSS. This vulnerability is fixed in 2.5.0 and 3.1.3.
>
> https://github.com/cure53/DOMPurify/security/advisories/GHSA-gx9m-whjm-85jf
> https://github.com/cure53/DOMPurify/commit/0ef5e537a514f904b6aa1d7ad9e749e365d7185f
> https://github.com/cure53/DOMPurify/commit/6ea80cd8b47640c20f2f230c7920b1f4ce4fdf7a
>
>
> If you fix the vulnerability please also make sure to include the
> CVE (Common Vulnerabilities & Exposures) id in your changelog entry.
>
> For further information see:
>
> [0] https://security-tracker.debian.org/tracker/CVE-2024-47875
> https://www.cve.org/CVERecord?id=CVE-2024-47875
>
> Please adjust the affected versions in the BTS as needed.
>
-------------- next part --------------
diff --git a/debian/changelog b/debian/changelog
index e109eb4..02c7a01 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+node-dompurify (2.4.1+dfsg+~2.4.0-2) bookworm-security; urgency=medium
+
+ * Team upload
+ * Fix mXSS issue (Closes: #1084983, CVE-2024-47875)
+
+ -- Yadd <yadd at debian.org> Sat, 12 Oct 2024 16:12:19 +0200
+
node-dompurify (2.4.1+dfsg+~2.4.0-1) unstable; urgency=medium
* Team upload
diff --git a/debian/patches/CVE-2024-47875.patch b/debian/patches/CVE-2024-47875.patch
new file mode 100644
index 0000000..9ebc9e4
--- /dev/null
+++ b/debian/patches/CVE-2024-47875.patch
@@ -0,0 +1,209 @@
+Description: fix for CVE-2024-47875
+ Updated 2.x branch with relevant fixes for nesting-based mXSS
+Author: Mario Heiderich <mario at cure53.de>
+Origin: upstream, https://github.com/cure53/DOMPurify/commit/0ef5e537
+Bug: https://github.com/cure53/DOMPurify/security/advisories/GHSA-gx9m-whjm-85jf
+Bug-Debian: https://bugs.debian.org/1084983
+Forwarded: not-needed
+Reviewed-By: Yadd <yadd at debian.org>
+Last-Update: 2024-10-12
+
+--- a/src/purify.js
++++ b/src/purify.js
+@@ -383,6 +383,9 @@
+ /* Keep a reference to config to pass to hooks */
+ let CONFIG = null;
+
++ /* Specify the maximum element nesting depth to prevent mXSS */
++ const MAX_NESTING_DEPTH = 500;
++
+ /* Ideally, do not touch anything below this line */
+ /* ______________________________________________ */
+
+@@ -896,7 +899,13 @@
+ const _isClobbered = function (elm) {
+ return (
+ elm instanceof HTMLFormElement &&
+- (typeof elm.nodeName !== 'string' ||
++ // eslint-disable-next-line unicorn/no-typeof-undefined
++ ((typeof elm.__depth !== 'undefined' &&
++ typeof elm.__depth !== 'number') ||
++ // eslint-disable-next-line unicorn/no-typeof-undefined
++ (typeof elm.__removalCount !== 'undefined' &&
++ typeof elm.__removalCount !== 'number') ||
++ typeof elm.nodeName !== 'string' ||
+ typeof elm.textContent !== 'string' ||
+ typeof elm.removeChild !== 'function' ||
+ !(elm.attributes instanceof NamedNodeMap) ||
+@@ -1025,10 +1034,9 @@
+ const childCount = childNodes.length;
+
+ for (let i = childCount - 1; i >= 0; --i) {
+- parentNode.insertBefore(
+- cloneNode(childNodes[i], true),
+- getNextSibling(currentNode)
+- );
++ const childClone = cloneNode(childNodes[i], true);
++ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
++ parentNode.insertBefore(childClone, getNextSibling(currentNode));
+ }
+ }
+ }
+@@ -1328,8 +1336,30 @@
+ continue;
+ }
+
++ /* Set the nesting depth of an element */
++ if (shadowNode.nodeType === 1) {
++ if (shadowNode.parentNode && shadowNode.parentNode.__depth) {
++ /*
++ We want the depth of the node in the original tree, which can
++ change when it's removed from its parent.
++ */
++ shadowNode.__depth =
++ (shadowNode.__removalCount || 0) +
++ shadowNode.parentNode.__depth +
++ 1;
++ } else {
++ shadowNode.__depth = 1;
++ }
++ }
++
++ /* Remove an element if nested too deeply to avoid mXSS */
++ if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
++ _forceRemove(shadowNode);
++ }
++
+ /* Deep shadow DOM detected */
+ if (shadowNode.content instanceof DocumentFragment) {
++ shadowNode.content.__depth = shadowNode.__depth;
+ _sanitizeShadowDOM(shadowNode.content);
+ }
+
+@@ -1474,8 +1504,30 @@
+ continue;
+ }
+
++ /* Set the nesting depth of an element */
++ if (currentNode.nodeType === 1) {
++ if (currentNode.parentNode && currentNode.parentNode.__depth) {
++ /*
++ We want the depth of the node in the original tree, which can
++ change when it's removed from its parent.
++ */
++ currentNode.__depth =
++ (currentNode.__removalCount || 0) +
++ currentNode.parentNode.__depth +
++ 1;
++ } else {
++ currentNode.__depth = 1;
++ }
++ }
++
++ /* Remove an element if nested too deeply to avoid mXSS */
++ if (currentNode.__depth >= MAX_NESTING_DEPTH) {
++ _forceRemove(currentNode);
++ }
++
+ /* Shadow DOM detected, sanitize it */
+ if (currentNode.content instanceof DocumentFragment) {
++ currentNode.content.__depth = currentNode.__depth;
+ _sanitizeShadowDOM(currentNode.content);
+ }
+
+--- a/test/test-suite.js
++++ b/test/test-suite.js
+@@ -2070,6 +2070,93 @@
+ });
+ });
+
++ QUnit.test('Test proper handling of nesting-based mXSS 1/3', function (assert) {
++
++ let dirty = `${`<div>`.repeat(496)}${`</div>`.repeat(496)}<img>`;
++ let expected = `${`<div>`.repeat(496)}${`</div>`.repeat(496)}<img>`;
++ let clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`;
++ expected = `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`;
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `${`<div>`.repeat(502)}${`</div>`.repeat(502)}<img>`;
++ expected = `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`;
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `<template>${`<div>`.repeat(502)}${`</div>`.repeat(502)}<img>`;
++ expected = `<template>${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`;
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `<div><template>${`<r>`.repeat(497)}<img>${`</r>`.repeat(
++ 497
++ )}</template></div><img>`;
++ expected = `<div><template></template></div><img>`;
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ });
++
++ QUnit.test('Test proper handling of nesting-based mXSS 2/3', function (assert) {
++
++ let dirty = `<form><input name="__depth">${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`;
++ let expected = [
++ ``,
++ `<form><input name="__depth">${`<div>`.repeat(497)}${`</div>`.repeat(497)}<img></form>`,
++ ];
++ let clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `<form><input name="__depth"></form>${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`;
++ expected = [
++ `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`,
++ `<form><input name="__depth"></form>${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`
++ ];
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `<form><input name="__removalCount">${`<div>`.repeat(
++ 500
++ )}${`</div>`.repeat(500)}<img>`;
++ expected = [
++ ``,
++ `<form><input name="__removalCount">${`<div>`.repeat(
++ 497
++ )}${`</div>`.repeat(497)}<img></form>`,
++ ];
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `<form><input name="__removalCount"></form>${`<div>`.repeat(
++ 500
++ )}${`</div>`.repeat(500)}<img>`;
++ expected = [
++ `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`,
++ `<form><input name="__removalCount"></form>${`<div>`.repeat(
++ 498
++ )}${`</div>`.repeat(498)}<img>`,
++ ];
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++ });
++
++ QUnit.test('Test proper handling of nesting-based mXSS 3/3', function (assert) {
++
++ let dirty = `<form><input name="__depth">`;
++ let expected = [``, `<form><input name="__depth"></form>`];
++ let clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++
++ dirty = `<form><input name="__removalCount">`;
++ expected = [``, `<form><input name="__removalCount"></form>`];
++ clean = DOMPurify.sanitize(dirty);
++ assert.contains(clean, expected);
++ });
++
+ QUnit.test('removeHook returns hook function', function (assert) {
+ const entryPoint = 'afterSanitizeAttributes';
+ const dirty = '<div class="hello"></div>';
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..d5a92ec
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+CVE-2024-47875.patch
More information about the Pkg-javascript-devel
mailing list