Moderate firefox Input Validation 🔧 Commit mapped

Overview

Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionOther issue in the Console component
ComponentCore
Bug ClassInput Validation
Tracker2077418
Fix commitb67d49d4c6ab (firefox) +1/-1
CISA KEVNot listed
Creditedquanhx
Disclosed2026-10-27

Background

Web Console (DevTools)
devtools/client/webconsole/actions/messages.js implements console UI actions. messageGetMatchingElements powers the “matching elements” feature: given a console message that carries CSS selectors, it asks the inspected page how many/which elements those selectors match.
`commands.scriptCommand.execute(expr, ...)`
evaluates a JavaScript expression string expr in the context of the inspected target (the page/frame identified by selectedTargetFront/innerWindowID).
`message.cssSelectors`
a selector string originating from a console message, i.e. content/page-influenced data.
`CSS.escape(value)`
standard DOM API that escapes a string so it is safe to embed as a CSS identifier/selector literal, neutralizing characters like quotes and backslashes.

Root Cause Analysis

messageGetMatchingElements built the evaluated expression by directly string-interpolating the page-derived selector into a JavaScript source string: document.querySelectorAll('${message.cssSelectors}'). That whole string is then handed to commands.scriptCommand.execute(...) and evaluated as JavaScript in the target. Because message.cssSelectors is attacker-influenced and is placed inside a single-quoted JS string literal with no escaping, a crafted selector containing a single quote (and arbitrary following text) breaks out of the string literal and injects attacker-chosen JavaScript into the evaluated expression. The violated invariant is that untrusted data embedded into an evaluated code string must be escaped/encoded for that context; it was not.

The patch wraps the value: document.querySelectorAll('${CSS.escape(message.cssSelectors)}'). CSS.escape encodes the selector so embedded quotes/backslashes can no longer terminate the string literal, preventing the breakout and confining the value to a harmless selector argument. (The advisory’s generic “Other issue in the Console component” understates this: the diff substantiates a code-injection/input-validation flaw in an evaluated expression.)

Key insight
Page-controlled selector text was concatenated unescaped into a JavaScript expression string that DevTools evaluates in the inspected target; a quote in message.cssSelectors escapes the literal and injects script. CSS.escape() neutralizes the breakout.

Attack Path

  1. Emit a crafted message A malicious page produces a console message whose cssSelectors field contains a payload like ') ; <arbitrary JS> // designed to close the string literal. (web-reachable: the page controls console message content.)
  2. User invokes the feature The developer opens the Web Console and triggers “get matching elements” on that message, calling messageGetMatchingElements.
  3. Injection into evaluated code The unescaped selector is interpolated into document.querySelectorAll('...'); the quote terminates the literal and the attacker’s JavaScript becomes part of the expression string.
  4. Execution in target scriptCommand.execute evaluates the combined string in the inspected target context, running attacker JavaScript there.
  5. Primitive Arbitrary JavaScript execution in the inspected target’s context, driven by page-supplied data rather than the developer’s input.

Impact Assessment

This is a DevTools client-side injection: untrusted console-message data is evaluated as JavaScript via scriptCommand.execute. Exploitation requires the victim to open the Web Console and use the matching-elements action on an attacker-influenced message (user interaction gating). The injected code runs in the inspected target’s execution context; because that context is the page the attacker already influences, the direct gain is bounded, but evaluating attacker-chosen expressions through the DevTools evaluation path can broaden beyond what the page’s own script could do and is the reason it is tracked as a security fix. The fix is a straightforward escaping of untrusted input; no memory-safety sink is involved.

Files Changed

  • devtools/client/webconsole/actions/messages.js

Audit Directions

  • String-built evaluated expressions
    grep DevTools for template literals passed to scriptCommand.execute/eval-style evaluators that interpolate message.*, node/attribute values, or other target-derived data without escaping.
  • Selector/identifier interpolation
    look for other places user/page strings are embedded in querySelector, querySelectorAll, or generated CSS/JS, and verify CSS.escape/JSON.stringify is applied.
  • Console message fields as trust boundary
    review all consumers of message.* fields in the webconsole actions/reducers treating them as untrusted, since they derive from page output.
diff --git a/devtools/client/webconsole/actions/messages.js b/devtools/client/webconsole/actions/messages.js
index ac14fa4e49d..e8d98d70cf4 100644
--- a/devtools/client/webconsole/actions/messages.js
+++ b/devtools/client/webconsole/actions/messages.js
@@ -123,7 +123,7 @@ function messageGetMatchingElements(message) {
       const selectedTargetFront = message?.targetFront;
 
       const response = await commands.scriptCommand.execute(
-        `document.querySelectorAll('${message.cssSelectors}')`,
+        `document.querySelectorAll('${CSS.escape(message.cssSelectors)}')`,
         {
           selectedTargetFront,
           innerWindowID: message.innerWindowID,
Loading diff…