Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionMitigation bypass in the DOM: Security component
ComponentDOM
Bug ClassLogic Error
Tracker2046748
Fix commitbd89f42dd94d (firefox) +11/-2
CISA KEVNot listed
CreditedNithissh
Disclosed2026-07-21

Background

Sanitizer API
the DOM Sanitizer that strips unsafe markup from HTML before it is inserted into a document via APIs like setHTML and parseHTML.
Safe baseline configuration
the built-in FOR_EACH_BASELINE_REMOVE_ELEMENT list of elements the sanitizer always removes regardless of any caller-supplied allow/deny configuration.
`<base>` element
an HTML element whose href attribute sets the document base URL, retargeting how every relative URL in the document is resolved.
`javascript:` URL
a URL scheme whose body is executed as script when the URL is navigated or activated.

Root Cause Analysis

The vulnerable path was RemoveJavascriptNavigationURLAttribute in Sanitizer.cpp, which treated the <base> element like <a> and <area> by only stripping its href attribute when that attribute contained a javascript: URL, leaving the <base> element itself in the sanitized output. This violated the safe-baseline invariant that document-scope, navigation-affecting elements must not survive sanitization, because a <base> element is dangerous by its very presence: it can retarget all relative URL resolution in the document, and attribute-level javascript: filtering alone is an incomplete mitigation that can be bypassed.

The fix removes <base> from the per-attribute javascript:-stripping check so it is no longer merely patched at the attribute level, and instead adds ELEMENT(XHTML, xhtml, base) to FOR_EACH_BASELINE_REMOVE_ELEMENT so the entire <base> element is dropped by the baseline configuration. This works because eliminating the whole element removes the entire class of <base>-driven URL-retargeting abuse rather than trying to sanitize one attribute value.

Key insight
The single mistake was mitigating a dangerous element (<base>) at the attribute level — stripping only a javascript: href — instead of removing the element outright; the fix reclassifies <base> as a baseline-removed element so no <base> reaches the sanitized DOM.

Attack Path

  1. Craft malicious base The attacker embeds <base href="javascript:alert(1)"></base> (or another retargeting <base>) in HTML destined for the Sanitizer API.
  2. Submit to sanitizer The markup is passed through setHTML or parseHTML, which the victim relies on to neutralize unsafe content.
  3. Element survives RemoveJavascriptNavigationURLAttribute only attempts to remove the href attribute and leaves the <base> element in place in the output.
  4. Base retargets resolution The surviving <base> alters how relative URLs in the document resolve, defeating the sanitizer’s safety guarantee and bypassing the intended mitigation.

Impact Assessment

An attacker gains a Sanitizer-API mitigation bypass that lets a <base> element persist in supposedly sanitized HTML, undermining the safety promise applications depend on when handling untrusted markup. This occurs in the content process, in the web-content context of any page or component that routes untrusted HTML through the Sanitizer API. The precondition is that the target uses the Sanitizer (setHTML/parseHTML) as its defense and accepts attacker-influenced markup.

Files Changed

  • dom/security/sanitizer/Sanitizer.cpp
  • testing/web-platform/meta/sanitizer-api/sanitizer-javascript-url.html.ini
  • testing/web-platform/meta/sanitizer-api/sanitizer-removeUnsafe.html.ini

Audit Directions

  • Attribute-level mitigation of element-level threats
    flag sanitizer or filter logic that neutralizes a dangerous element by editing one attribute while leaving the element in the DOM.
  • Baseline removal completeness
    audit FOR_EACH_BASELINE_REMOVE_ELEMENT and equivalent allow/deny lists for document-scope elements (base, meta, link) that alter global resolution or policy and must be removed outright.
  • URL-retargeting sinks
    review any element (<base>, <form action>, <a href>) whose value redirects navigation or resource resolution, ensuring javascript: is not the only scheme guarded and that removal, not rewriting, is applied where required.
diff --git a/dom/security/sanitizer/Sanitizer.cpp b/dom/security/sanitizer/Sanitizer.cpp
index 403bf6fbecc..eed9ed94130 100644
--- a/dom/security/sanitizer/Sanitizer.cpp
+++ b/dom/security/sanitizer/Sanitizer.cpp
@@ -1630,6 +1630,7 @@ bool Sanitizer::SetDataAttributes(bool aAllow) {
 // https://wicg.github.io/sanitizer-api/#built-in-safe-baseline-configuration
 // The built-in safe baseline configuration
 #define FOR_EACH_BASELINE_REMOVE_ELEMENT(ELEMENT) \
+  ELEMENT(XHTML, xhtml, base)                     \
   ELEMENT(XHTML, xhtml, embed)                    \
   ELEMENT(XHTML, xhtml, frame)                    \
   ELEMENT(XHTML, xhtml, iframe)                   \
@@ -1762,8 +1763,7 @@ static bool RemoveJavascriptNavigationURLAttribute(Element* aElement,
   // Step 1. If «[elementName, attrName]» matches an entry in the built-in
   // navigating URL attributes list, and if attribute contains a javascript:
   // URL, then remove attribute from child.
-  if ((aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
-                                     nsGkAtoms::base) &&
+  if ((aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
        aLocalName == nsGkAtoms::href && aNamespaceID == kNameSpaceID_None) ||
       (aElement->IsAnyOfHTMLElements(nsGkAtoms::button, nsGkAtoms::input) &&
        aLocalName == nsGkAtoms::formaction &&
diff --git a/testing/web-platform/meta/sanitizer-api/sanitizer-javascript-url.html.ini b/testing/web-platform/meta/sanitizer-api/sanitizer-javascript-url.html.ini
new file mode 100644
index 00000000000..2a43dc3811d
--- /dev/null
+++ b/testing/web-platform/meta/sanitizer-api/sanitizer-javascript-url.html.ini
@@ -0,0 +1,6 @@
+[sanitizer-javascript-url.html]
+  [setHTML testcase built-in-navigating-url-attributes-list/2, "<base href="javascript:alert(1)"></base>"]
+    expected: FAIL
+
+  [parseHTML testcase built-in-navigating-url-attributes-list/2, "<base href="javascript:alert(1)"></base>"]
+    expected: FAIL
diff --git a/testing/web-platform/meta/sanitizer-api/sanitizer-removeUnsafe.html.ini b/testing/web-platform/meta/sanitizer-api/sanitizer-removeUnsafe.html.ini
new file mode 100644
index 00000000000..79b34a636f4
--- /dev/null
+++ b/testing/web-platform/meta/sanitizer-api/sanitizer-removeUnsafe.html.ini
@@ -0,0 +1,3 @@
+[sanitizer-removeUnsafe.html]
+  [removeUnsafe removes the right elements and attributes]
+    expected: FAIL
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/testing/web-platform/meta/sanitizer-api/sanitizer-javascript-url.html.ini b/testing/web-platform/meta/sanitizer-api/sanitizer-javascript-url.html.ini
new file mode 100644
index 00000000000..2a43dc3811d
--- /dev/null
+++ b/testing/web-platform/meta/sanitizer-api/sanitizer-javascript-url.html.ini
@@ -0,0 +1,6 @@
+[sanitizer-javascript-url.html]
+  [setHTML testcase built-in-navigating-url-attributes-list/2, "<base href="javascript:alert(1)"></base>"]
+    expected: FAIL
+
+  [parseHTML testcase built-in-navigating-url-attributes-list/2, "<base href="javascript:alert(1)"></base>"]
+    expected: FAIL
diff --git a/testing/web-platform/meta/sanitizer-api/sanitizer-removeUnsafe.html.ini b/testing/web-platform/meta/sanitizer-api/sanitizer-removeUnsafe.html.ini
new file mode 100644
index 00000000000..79b34a636f4
--- /dev/null
+++ b/testing/web-platform/meta/sanitizer-api/sanitizer-removeUnsafe.html.ini
@@ -0,0 +1,3 @@
+[sanitizer-removeUnsafe.html]
+  [removeUnsafe removes the right elements and attributes]
+    expected: FAIL
Loading diff…