Medium CVSS 4.3 firefox Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionError handling for script execution was incorrectly isolated from web content, which could have allowed cross-origin leak attacks.
ComponentCore
Bug ClassCross Origin
Tracker1960745
Fix commit466e2a52971f (firefox) +7/-4
CWECWE-346 (Origin validation error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
Creditedterjanq
Disclosed2025-05-27

Background

`DevToolsUtils.getProperty(obj, prop, invokeUnsafeGetters)`
Reads a property from a debuggee object. The third argument controls whether unsafe getters (accessor properties backed by debuggee-defined functions) are actually invoked.
Muted errors (`isMutedError`)
When a script is loaded cross-origin without CORS, the engine “mutes” its errors: error details are hidden from the embedding page to prevent cross-origin information leaks. A Debugger.Object exposes isMutedError to indicate this.
CWE-346 (origin validation error)
Here, cross-origin error information that should be withheld from web content can be exposed if the error’s getters are invoked and the result is observable.

Root Cause Analysis

The Error previewer unconditionally invoked potentially unsafe getters for name and message: it called DevToolsUtils.getProperty(obj, "name", true) and getProperty(obj, "message", true), with the true flag forcing invocation of debuggee getters even when they are “unsafe” (a deliberate choice from Bug 1710694 so users could still see error text). But this ignored whether the error was a muted cross-origin error. For a muted error, name/message are exactly the cross-origin details the platform withholds; invoking the getters and feeding the result into the preview re-exposed information that same-origin policy had suppressed, enabling cross-origin leak attacks. Because the error-handling preview path was not isolated from web content’s muting state, an attacker could learn details of a cross-origin script error that should have been opaque.

The patch gates getter invocation on the mute flag. It computes const invokeUnsafeGetters = !obj.isMutedError; and passes that instead of a hard-coded true to both getProperty(obj, "name", invokeUnsafeGetters) and getProperty(obj, "message", invokeUnsafeGetters). For a muted error, invokeUnsafeGetters is false, so the unsafe getters are not called and no cross-origin error text is extracted; for ordinary (non-muted) errors behavior is unchanged. The advisory’s framing — “error handling for script execution was incorrectly isolated from web content, allowing cross-origin leak attacks” (CWE-346) — is substantiated precisely by the diff: the fix is an origin/mute-state check before surfacing error getters.

Key insight
The DevTools error previewer always invoked name/message getters, even on cross-origin muted errors whose details the platform deliberately hides, leaking them; the fix skips unsafe-getter invocation whenever isMutedError is set.

Attack Path

  1. Attacker Hosts a cross-origin script (loaded without CORS) that throws an error whose name/message are produced by getters encoding cross-origin-sensitive information. (UI:R per the vector)
  2. Victim Loads attacker-controlled content that references the cross-origin script, so the engine produces a muted error object.
  3. Observation channel The error flows to a context where the DevTools object previewer runs on it and its preview becomes observable to the attacker’s measurement.
  4. Leak Pre-patch, the previewer invokes the unsafe name/message getters on the muted error and includes the result in the preview, exposing cross-origin error details.
  5. Primitive Cross-origin information disclosure (reading error data the same-origin/mute policy should have withheld).

Impact Assessment

Cross-origin information disclosure (CWE-346), C:L / no integrity or availability impact. The leak is of cross-origin script-error details (name/message) that muting is supposed to hide; it requires the attacker to get the muted error surfaced through the previewer path and observe the result. No memory corruption and no code execution. The fix is a pure policy/origin-isolation correction, consistent with the moderate rating.

Files Changed

  • devtools/server/actors/object/previewers.js

Audit Directions

  • Other `invokeUnsafeGetters: true` call sites
    Audit the DevTools previewers/actors for other places that force unsafe-getter invocation without consulting isMutedError or other cross-origin gating.
  • Muted-error handling across surfaces
    Review console, network, and error-reporting paths that read name/message/stack from error objects to ensure they honor muting consistently.
  • Debugger.Object cross-origin flags
    Confirm all consumers of Debugger.Object that render content check the relevant isolation flags (isMutedError, and related same-origin indicators) before exposing debuggee-controlled values.
diff --git a/devtools/server/actors/object/previewers.js b/devtools/server/actors/object/previewers.js
index 3fd4ec0122e..1c5052fe0f8 100644
--- a/devtools/server/actors/object/previewers.js
+++ b/devtools/server/actors/object/previewers.js
@@ -1034,10 +1034,13 @@ previewers.Object = [
 
     const { obj } = objectActor;
 
-    // The name and/or message could be getters, and even if it's unsafe, we do want
-    // to show it to the user (See Bug 1710694).
-    const name = DevToolsUtils.getProperty(obj, "name", true);
-    const msg = DevToolsUtils.getProperty(obj, "message", true);
+    // The name and/or message could be getters, and even if it's unsafe,
+    // we do want to show it to the user, unless the error is muted
+    // (See Bug 1710694).
+    const invokeUnsafeGetters = !obj.isMutedError;
+
+    const name = DevToolsUtils.getProperty(obj, "name", invokeUnsafeGetters);
+    const msg = DevToolsUtils.getProperty(obj, "message", invokeUnsafeGetters);
     const stack = DevToolsUtils.getProperty(obj, "stack");
     const fileName = DevToolsUtils.getProperty(obj, "fileName");
     const lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
Loading diff…