Firefox · Core
CVE-2026-6769
Logic Error in Core
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
constructordevtools/client/debugger/src/components/QuickOpenModal.js |
modified | |
renderHighlightdevtools/client/debugger/src/components/QuickOpenModal.js |
modified | |
highlightQueryMatchesdevtools/client/debugger/src/components/QuickOpenModal.js |
modified | |
ifdevtools/client/debugger/src/components/QuickOpenModal.js |
modified |
Files Changed
devtools/client/debugger/src/.eslintrc.mjsdevtools/client/debugger/src/components/QuickOpenModal.jsdevtools/client/debugger/test/mochitest/browser_dbg-quick-open.js
Patch
diff --git a/devtools/client/debugger/src/.eslintrc.mjs b/devtools/client/debugger/src/.eslintrc.mjs
index f940fd0f3cf..11f35cce6e6 100644
--- a/devtools/client/debugger/src/.eslintrc.mjs
+++ b/devtools/client/debugger/src/.eslintrc.mjs
@@ -47,6 +47,8 @@ export default [
process: true,
global: true,
L10N: true,
+ // TODO: Add this to the main ESlint globals Bug 2025542
+ Sanitizer: true,
},
},
rules: {
diff --git a/devtools/client/debugger/src/components/QuickOpenModal.js b/devtools/client/debugger/src/components/QuickOpenModal.js
index df0c68ec8eb..ded47cceb6d 100644
--- a/devtools/client/debugger/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/src/components/QuickOpenModal.js
@@ -2,11 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
-import React, { Component } from "devtools/client/shared/vendor/react";
-import { div } from "devtools/client/shared/vendor/react-dom-factories";
+import React, {
+ Component,
+ createRef,
+} from "devtools/client/shared/vendor/react";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
import { connect } from "devtools/client/shared/vendor/react-redux";
-import { basename } from "../utils/path";
import { createLocation } from "../utils/location";
const fuzzyAldrin = require("resource://devtools/client/shared/vendor/fuzzaldrin-plus.js");
@@ -59,6 +60,7 @@ export class QuickOpenModal extends Component {
constructor(props) {
super(props);
this.state = { results: null, selectedIndex: 0 };
+ this.resultListRef = createRef();
}
static get propTypes() {
@@ -222,20 +224,14 @@ export class QuickOpenModal extends Component {
if (query == "" && !this.isShortcutQuery()) {
this.showTopSources();
- return;
- }
-
- if (this.isSymbolSearch()) {
+ } else if (this.isSymbolSearch()) {
await this.searchSymbols(query);
- return;
- }
-
- if (this.isShortcutQuery()) {
+ } else if (this.isShortcutQuery()) {
this.searchShortcuts(query);
- return;
+ } else {
+ this.searchSources(query);
}
-
- this.searchSources(query);
+ this.highlightQueryMatches(this.props.query);
} catch (e) {
// Due to throttling this might get scheduled after the component and the
// toolbox are destroyed.
@@ -391,23 +387,31 @@ export class QuickOpenModal extends Component {
isSourcesQuery = () => this.props.searchType === "sources";
isSourceSearch = () => this.isSourcesQuery() || this.isGotoSourceQuery();
- /* eslint-disable react/no-danger */
- renderHighlight(candidateString, query) {
+ highlightQueryMatches(query) {
const options = {
wrap: {
tagOpen: '<mark class="highlight">',
tagClose: "</mark>",
},
};
- const html = fuzzyAldrin.wrap(candidateString, query, options);
- return div({
- dangerouslySetInnerHTML: {
- __html: html,
- },
- });
+ if (this.resultListRef.current) {
+ const domEl = this.resultListRef.current.ref.current;
+ for (const titleNode of domEl.querySelectorAll(".title")) {
+ const htmlString = fuzzyAldrin.wrap(
+ titleNode.innerText,
+ query,
+ options
+ );
+ const sanitizer = new Sanitizer({
+ elements: ["mark"],
+ attributes: ["class"],
+ });
+ titleNode.setHTML(htmlString, { sanitizer });
+ }
+ }
}
- highlightMatching = (query, results) => {
+ renderResults = (query, results) => {
let newQuery = query;
if (newQuery === "") {
return results;
@@ -418,11 +422,7 @@ export class QuickOpenModal extends Component {
if (typeof result.title == "string") {
return {
...result,
- title: this.renderHighlight(
- result.title,
- basename(newQuery),
- "title"
- ),
+ title: result.title,
};
}
return result;
@@ -451,7 +451,7 @@ export class QuickOpenModal extends Component {
const { query } = this.props;
const { selectedIndex, results } = this.state;
- const items = this.highlightMatching(query, results || []);
+ const items = this.renderResults(query, results || []);
const expanded = !!items && !!items.length;
return React.createElement(
Modal,
@@ -484,7 +484,7 @@ export class QuickOpenModal extends Component {
items,
selected: selectedIndex,
selectItem: this.selectResultItem,
- ref: "resultList",
+ ref: this.resultListRef,
expanded,
...(this.isSourceSearch() ? SIZE_BIG : SIZE_DEFAULT),
})
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js b/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js
index b32febf51a3..0b77e952211 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js
@@ -94,6 +94,18 @@ add_task(async function () {
pressKey(dbg, "Escape");
assertQuickOpenDisabled(dbg);
+ info("Test that the highlighted result matches match the query");
+ await quickOpen(dbg, "sw");
+ await waitForResults(dbg, [
+ "script-switching-01.js",
+ "script-switching-02.js",
+ ]);
+ await assertHighlightMatches(dbg, 1, "sw");
+ await assertHighlightMatches(dbg, 2, "sw");
+ EventUtils.sendString("i");
+ await assertHighlightMatches(dbg, 1, "swi");
+ pressKey(dbg, "Escape");
+
info("Testing goto line:column");
assertLine(dbg, 0);
assertColumn(dbg, 1);
@@ -171,3 +183,13 @@ async function assertResultIsTab(dbg, index) {
"Result should be a tab"
);
}
+
+async function assertHighlightMatches(dbg, resultIndex, expectedMatchText) {
+ const el = await findResultEl(dbg, resultIndex);
+ const highlight = await waitForElementWithSelector(dbg, "mark.highlight");
+ ok(el && !!highlight, "The query match is highlighted");
+ await waitUntil(
+ () => el.querySelector("mark.highlight").innerText == expectedMatchText
+ );
+ ok(true, "The highlighted text matches the query text");
+}
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js b/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js
index b32febf51a3..0b77e952211 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js
@@ -94,6 +94,18 @@ add_task(async function () {
pressKey(dbg, "Escape");
assertQuickOpenDisabled(dbg);
+ info("Test that the highlighted result matches match the query");
+ await quickOpen(dbg, "sw");
+ await waitForResults(dbg, [
+ "script-switching-01.js",
+ "script-switching-02.js",
+ ]);
+ await assertHighlightMatches(dbg, 1, "sw");
+ await assertHighlightMatches(dbg, 2, "sw");
+ EventUtils.sendString("i");
+ await assertHighlightMatches(dbg, 1, "swi");
+ pressKey(dbg, "Escape");
+
info("Testing goto line:column");
assertLine(dbg, 0);
assertColumn(dbg, 1);
@@ -171,3 +183,13 @@ async function assertResultIsTab(dbg, index) {
"Result should be a tab"
);
}
+
+async function assertHighlightMatches(dbg, resultIndex, expectedMatchText) {
+ const el = await findResultEl(dbg, resultIndex);
+ const highlight = await waitForElementWithSelector(dbg, "mark.highlight");
+ ok(el && !!highlight, "The query match is highlighted");
+ await waitUntil(
+ () => el.querySelector("mark.highlight").innerText == expectedMatchText
+ );
+ ok(true, "The highlighted text matches the query text");
+}
Loading diff…
References
On This Page