Firefox · Toolkit
CVE-2026-74966
Logic Error in Toolkit
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
isAllowedFieldnametoolkit/components/satchel/FormHistory.sys.mjs |
modified | |
isAllowedEntrytoolkit/components/satchel/FormHistory.sys.mjs |
modified | |
iftoolkit/components/satchel/FormHistory.sys.mjs |
modified | |
iftoolkit/components/satchel/FormHistoryChild.sys.mjs |
modified | |
iftoolkit/components/satchel/FormHistoryParent.sys.mjs |
modified |
Files Changed
toolkit/components/satchel/FormHistory.sys.mjstoolkit/components/satchel/FormHistoryChild.sys.mjstoolkit/components/satchel/FormHistoryParent.sys.mjs
Patch
diff --git a/toolkit/components/satchel/FormHistory.sys.mjs b/toolkit/components/satchel/FormHistory.sys.mjs
index f621105779b..a55bcb0e7ca 100644
--- a/toolkit/components/satchel/FormHistory.sys.mjs
+++ b/toolkit/components/satchel/FormHistory.sys.mjs
@@ -86,6 +86,7 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
+ CreditCard: "resource://gre/modules/CreditCard.sys.mjs",
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
@@ -95,6 +96,8 @@ const DAY_IN_MS = 86400000; // 1 day in milliseconds
const MAX_SEARCH_TOKENS = 10;
const DB_FILENAME = "formhistory.sqlite";
+const MAX_FIELD_LENGTH = 200;
+
var supportsDeletedTable = AppConstants.platform == "android";
const wait = ms => new Promise(res => lazy.setTimeout(res, ms));
@@ -1015,6 +1018,45 @@ export let FormHistory = {
return Prefs.get("enabled");
},
+ /**
+ * Whether a fieldname is allowed to be written through FormHistory.
+ *
+ * @param {string} fieldname - the fieldname to test
+ * @returns {boolean}
+ */
+ isAllowedFieldname(fieldname) {
+ return !["searchbar-history"].includes(fieldname);
+ },
+
+ /**
+ * Whether an entry is eligible to be stored in form history.
+ *
+ * @param {string} name - the fieldname
+ * @param {string} value - the value
+ * @returns {boolean}
+ */
+ isAllowedEntry(name, value) {
+ if (typeof name != "string" || typeof value != "string") {
+ return false;
+ }
+
+ if (!this.isAllowedFieldname(name)) {
+ return false;
+ }
+
+ // Limit stored data to 200 characters.
+ if (name.length > MAX_FIELD_LENGTH || value.length > MAX_FIELD_LENGTH) {
+ return false;
+ }
+
+ // Don't save credit card numbers.
+ if (lazy.CreditCard.isValidNumber(value)) {
+ return false;
+ }
+
+ return true;
+ },
+
async search(aSelectTerms, aSearchData, aRowFunc) {
// if no terms selected, select everything
if (!aSelectTerms) {
diff --git a/toolkit/components/satchel/FormHistoryChild.sys.mjs b/toolkit/components/satchel/FormHistoryChild.sys.mjs
index dbc3f9a1f78..52ce9d67717 100644
--- a/toolkit/components/satchel/FormHistoryChild.sys.mjs
+++ b/toolkit/components/satchel/FormHistoryChild.sys.mjs
@@ -7,7 +7,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
- CreditCard: "resource://gre/modules/CreditCard.sys.mjs",
+ FormHistory: "resource://gre/modules/FormHistory.sys.mjs",
FormHistoryAutoCompleteResult:
"resource://gre/modules/FormHistoryAutoComplete.sys.mjs",
FormScenarios: "resource://gre/modules/FormScenarios.sys.mjs",
@@ -101,25 +101,13 @@ export class FormHistoryChild extends JSWindowActorChild {
continue;
}
- // Don't save credit card numbers.
- if (lazy.CreditCard.isValidNumber(value)) {
- log("skipping saving a credit card number");
- continue;
- }
-
const name = FormHistoryChild.getInputName(input);
if (!name) {
continue;
}
- if (name == "searchbar-history") {
- log('addEntry for input name "' + name + '" is denied');
- continue;
- }
-
- // Limit stored data to 200 characters.
- if (name.length > 200 || value.length > 200) {
- log("skipping input that has a name/value too large");
+ if (!lazy.FormHistory.isAllowedEntry(name, value)) {
+ log("skipping input that is not eligible to be stored");
continue;
}
diff --git a/toolkit/components/satchel/FormHistoryParent.sys.mjs b/toolkit/components/satchel/FormHistoryParent.sys.mjs
index 42e5054ae44..54abed8a57b 100644
--- a/toolkit/components/satchel/FormHistoryParent.sys.mjs
+++ b/toolkit/components/satchel/FormHistoryParent.sys.mjs
@@ -43,13 +43,23 @@ export class FormHistoryParent extends JSWindowActorParent {
}
#onFormSubmitEntries(entries) {
- const changes = entries.map(entry => ({
- op: "bump",
- fieldname: entry.name,
- value: entry.value,
- }));
+ // Don't store form history in private browsing sessions, or when the
+ // browsing context cannot be determined.
+ if (this.browsingContext?.usePrivateBrowsing ?? true) {
+ return;
+ }
+
+ const changes = entries
+ .filter(entry => lazy.FormHistory.isAllowedEntry(entry.name, entry.value))
+ .map(entry => ({
+ op: "bump",
+ fieldname: entry.name,
+ value: entry.value,
+ }));
- lazy.FormHistory.update(changes);
+ if (changes.length) {
+ lazy.FormHistory.update(changes);
+ }
}
get formOrigin() {
@@ -61,6 +71,11 @@ export class FormHistoryParent extends JSWindowActorParent {
async #onAutoCompleteSearch({ searchString, params, scenarioName }) {
searchString = searchString.trim().toLowerCase();
+ // The search bar manages its own history and is not served here.
+ if (!lazy.FormHistory.isAllowedFieldname(params?.fieldname)) {
+ return { formHistoryEntries: [], externalEntries: [] };
+ }
+
let formHistoryPromise;
if (
FormHistoryParent.canSearchIncrementally(
@@ -99,6 +114,12 @@ export class FormHistoryParent extends JSWindowActorParent {
}
#onRemoveEntry({ inputName, value, guid }) {
+ // Removals must be scoped to a specific, allowed fieldname so the
+ // fieldname always constrains the query.
+ if (!inputName || !lazy.FormHistory.isAllowedFieldname(inputName)) {
+ return;
+ }
+
lazy.FormHistory.update({
op: "remove",
fieldname: inputName,
Loading diff…
References
On This Page