Firefox · Toolkit
CVE-2025-1935
Logic Error in Toolkit
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifbrowser/components/tabbrowser/content/tabbrowser.js |
modified | |
constructortoolkit/content/widgets/notificationbox.js |
modified | |
iftoolkit/content/widgets/notificationbox.js |
modified | |
closeButtonTemplatetoolkit/content/widgets/notificationbox.js |
modified | |
handleEventtoolkit/content/widgets/notificationbox.js |
modified | |
setButtonstoolkit/content/widgets/notificationbox.js |
modified | |
fortoolkit/content/widgets/notificationbox.js |
modified | |
_initClickJackingProtectiontoolkit/content/widgets/notificationbox.js |
modified |
Files Changed
browser/base/content/browser.jsbrowser/components/tabbrowser/content/tabbrowser.jstoolkit/content/widgets/notificationbox.js
Patch
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index e5d9c4d9363..16790739e83 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -410,13 +410,17 @@ ChromeUtils.defineLazyGetter(this, "ReferrerInfo", () =>
// High priority notification bars shown at the top of the window.
ChromeUtils.defineLazyGetter(this, "gNotificationBox", () => {
+ let securityDelayMS = Services.prefs.getIntPref(
+ "security.notification_enable_delay"
+ );
+
return new MozElements.NotificationBox(element => {
element.classList.add("global-notificationbox");
element.setAttribute("notificationside", "top");
element.setAttribute("prepend-notifications", true);
// We want this before the tab notifications.
document.getElementById("notifications-toolbar").prepend(element);
- });
+ }, securityDelayMS);
});
ChromeUtils.defineLazyGetter(this, "InlineSpellCheckerUI", () => {
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 8a8351ae4e1..e815b9bd5a6 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -156,6 +156,12 @@
"browser.tabs.unloadTabInContextMenu",
false
);
+ XPCOMUtils.defineLazyPreferenceGetter(
+ this,
+ "_notificationEnableDelay",
+ "security.notification_enable_delay",
+ 500
+ );
if (AppConstants.MOZ_CRASHREPORTER) {
ChromeUtils.defineESModuleGetters(this, {
@@ -908,7 +914,7 @@
if (browser == this.selectedBrowser) {
this._updateVisibleNotificationBox(browser);
}
- });
+ }, this._notificationEnableDelay);
}
return browser._notificationBox;
}
diff --git a/toolkit/content/widgets/notificationbox.js b/toolkit/content/widgets/notificationbox.js
index 7da1344f04b..983c566aaae 100644
--- a/toolkit/content/widgets/notificationbox.js
+++ b/toolkit/content/widgets/notificationbox.js
@@ -12,12 +12,14 @@
* Creates a new class to handle a notification box, but does not add any
* elements to the DOM until a notification has to be displayed.
*
- * @param insertElementFn
- * Called with the "notification-stack" element as an argument when the
- * first notification has to be displayed.
+ * @param insertElementFn Called with the "notification-stack" element as an
+ * argument when the first notification has to be displayed.
+ * @param {Number} securityDelayMS - Delay in milliseconds until buttons are enabled to
+ * protect against click- and tapjacking.
*/
- constructor(insertElementFn) {
+ constructor(insertElementFn, securityDelayMS = 0) {
this._insertElementFn = insertElementFn;
+ this._securityDelayMS = securityDelayMS;
this._animating = false;
this.currentNotification = null;
}
@@ -127,10 +129,18 @@
* Defines a Custom Element name to use as the "is" value on
* button creation.
* }
+ * aDisableClickJackingDelay
+ * Optional boolean arg to disable clickjacking protections. By
+ * default the security delay is enabled.
*
* @returns {Promise<Object>} The <notification-message> element that is shown.
*/
- async appendNotification(aType, aNotification, aButtons) {
+ async appendNotification(
+ aType,
+ aNotification,
+ aButtons,
+ aDisableClickJackingDelay = false
+ ) {
if (
aNotification.priority < this.PRIORITY_SYSTEM ||
aNotification.priority > this.PRIORITY_CRITICAL_HIGH
@@ -213,6 +223,13 @@
newitem.setAttribute("type", "warning");
}
+ // If clickjacking protection is not explicitly disabled, enable it.
+ // aDisableClickJackingDelay is per notification, this._securityDelayMS is
+ // global for the entire notification box.
+ if (!aDisableClickJackingDelay && this._securityDelayMS > 0) {
+ newitem._initClickJackingProtection(this._securityDelayMS);
+ }
+
// Animate the notification.
newitem.style.display = "block";
newitem.style.position = "fixed";
@@ -381,6 +398,13 @@
this.timeout = 0;
this.dismissable = true;
+ // Variables used for security delay / clickjacking protection.
+ this._clickjackingDelayActive = false;
+ this._securityDelayMS = 0;
+ this._delayTimer = null;
+ this._focusHandler = null;
+ this._buttons = [];
+
this.addEventListener("click", this);
this.addEventListener("command", this);
}
@@ -403,6 +427,8 @@
if (this.eventCallback) {
this.eventCallback("disconnected");
}
+ // Clean up clickjacking listeners if active.
+ this._uninitClickJackingProtection();
}
closeButtonTemplate() {
@@ -439,6 +465,24 @@
}
handleEvent(e) {
+ // If clickjacking delay is active, prevent any "click"/"command" from
+ // going through. Also restart the delay if the user tries to click too early.
+ if (this._clickjackingDelayActive) {
+ // Only relevant if user clicked on the notification’s actual button/link area.
+ if (
+ e.type === "click" &&
+ (e.target.localName === "button" ||
+ e.target.classList.contains("text-link") ||
+ e.target.classList.contains("notification-link"))
+ ) {
+ // Stop immediate action, restart the delay
+ e.stopPropagation();
+ e.preventDefault();
+ this._startClickJackingDelay();
+ return;
+ }
+ }
+
if (e.type == "click" && e.target.localName != "label") {
return;
}
@@ -487,7 +531,7 @@
}
setButtons(buttons) {
- this._buttons = buttons;
+ this._buttons = [];
for (let button of buttons) {
let link = button.link || button.supportPage;
let localeId = button["l10n-id"];
@@ -536,7 +580,9 @@
} else {
this.buttonContainer.appendChild(buttonElem);
}
+
buttonElem.buttonInfo = button;
+ this._buttons.push(buttonElem);
}
}
@@ -546,7 +592,75 @@
}
super.dismiss();
}
+
+ /**
+ * Initialize clickjacking protection for this notification, disabling
+ * buttons initially and re-enabling them after a short delay. The delay
+ * restarts on window focus or if the user attempts to click during the
+ * disabled period.
+ *
+ * @param {Number} securityDelayMS - ClickJacking delay to apply
+ * (milliseconds).
+ */
+ _initClickJackingProtection(securityDelayMS) {
+ if (this._clickjackingDelayActive) {
+ return; // Already enabled.
+ }
+
+ this._securityDelayMS = securityDelayMS;
+ // Attach a global focus handler so we can restart the delay when the window
+ // refocuses (e.g., user navigated away or used a popup).
+ this._focusHandler = () => {
+ // If the notification is still connected, restart the delay.
+ if (this.isConnected) {
+ this._startClickJackingDelay();
+ }
Loading diff…
References
On This Page