Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionMitigation bypass in the Enterprise Policies component
ComponentCore
Bug ClassLogic Error
Tracker2044527
Fix commite5d6e2bb8b50 (firefox) +68/-2
CISA KEVNot listed
CreditedSouma Ohsawa
Disclosed2026-07-21

Changed Functions

FunctionChangeNotes
isAllowed
browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs
modified
if
browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs
modified
normalizeURL
browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs
modified
add_task
browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js
modified

Files Changed

  • browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs
  • browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js
  • browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml
diff --git a/browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs b/browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs
index cda98f60e1c..8b26f43109b 100644
--- a/browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs
+++ b/browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs
@@ -165,14 +165,30 @@ export let WebsiteFilter = {
     return this.QueryInterface(iid);
   },
   isAllowed(url) {
-    if (this._blockPatterns?.matches(url.toLowerCase())) {
+    let normalizedURL = this.normalizeURL(url);
+    // A URL we are about to load should always parse, so this is unexpected.
+    // Block it rather than let an unparseable URL skip the filter.
+    if (normalizedURL == null) {
+      return false;
+    }
+    if (this._blockPatterns?.matches(normalizedURL)) {
       if (
         !this._exceptionsPatterns ||
-        !this._exceptionsPatterns.matches(url.toLowerCase())
+        !this._exceptionsPatterns.matches(normalizedURL)
       ) {
         return false;
       }
     }
     return true;
   },
+  normalizeURL(url) {
+    let parsed = URL.parse(url);
+    if (!parsed) {
+      return null;
+    }
+    if (parsed.hostname.endsWith(".")) {
+      parsed.hostname = parsed.hostname.replace(/\.+$/, "");
+    }
+    return parsed.href.toLowerCase();
+  },
 };
diff --git a/browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js b/browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js
new file mode 100644
index 00000000000..103004c8a82
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { WebsiteFilter } = ChromeUtils.importESModule(
+  "resource:///modules/policies/WebsiteFilter.sys.mjs"
+);
+
+add_task(async function setup() {
+  await setupPolicyEngineWithJson({
+    policies: {
+      WebsiteFilter: {
+        Block: ["*://accounts.firefox.com/*"],
+      },
+    },
+  });
+});
+
+add_task(async function test_host_normalization() {
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com/"),
+    "Bare host should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com./"),
+    "Trailing-dot host should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com.../"),
+    "Multiple trailing dots should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com%2e/"),
+    "Percent-encoded trailing dot should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com。/"),
+    "Ideographic full-stop (folds to a trailing dot) should be blocked"
+  );
+  ok(
+    WebsiteFilter.isAllowed("https://example.org./"),
+    "Unrelated trailing-dot host should not be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("not a parseable url"),
+    "Unparseable URL should be blocked"
+  );
+});
diff --git a/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml b/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml
index a5532c4b387..67a25157634 100644
--- a/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml
+++ b/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml
@@ -80,3 +80,5 @@ support-files = ["config_popups_cookies_addons.json"]
 ["test_telemetry.js"]
 
 ["test_webserial.js"]
+
+["test_websitefilter.js"]
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js b/browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js
new file mode 100644
index 00000000000..103004c8a82
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/xpcshell/test_websitefilter.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { WebsiteFilter } = ChromeUtils.importESModule(
+  "resource:///modules/policies/WebsiteFilter.sys.mjs"
+);
+
+add_task(async function setup() {
+  await setupPolicyEngineWithJson({
+    policies: {
+      WebsiteFilter: {
+        Block: ["*://accounts.firefox.com/*"],
+      },
+    },
+  });
+});
+
+add_task(async function test_host_normalization() {
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com/"),
+    "Bare host should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com./"),
+    "Trailing-dot host should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com.../"),
+    "Multiple trailing dots should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com%2e/"),
+    "Percent-encoded trailing dot should be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("https://accounts.firefox.com。/"),
+    "Ideographic full-stop (folds to a trailing dot) should be blocked"
+  );
+  ok(
+    WebsiteFilter.isAllowed("https://example.org./"),
+    "Unrelated trailing-dot host should not be blocked"
+  );
+  ok(
+    !WebsiteFilter.isAllowed("not a parseable url"),
+    "Unparseable URL should be blocked"
+  );
+});
diff --git a/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml b/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml
index a5532c4b387..67a25157634 100644
--- a/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml
+++ b/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.toml
@@ -80,3 +80,5 @@ support-files = ["config_popups_cookies_addons.json"]
 ["test_telemetry.js"]
 
 ["test_webserial.js"]
+
+["test_websitefilter.js"]
Loading diff…