commit f951c238c0e5bc2c94f0fa4ed5808b960533b132
parent 0d028b595d313c6427f66206eb71d9c1f6554b50
Author: smayya <smayya@mozilla.com>
Date: Fri, 17 Oct 2025 09:48:51 +0000
Bug 1993938 - Add network.lna.websocket.enabled preference for WebSocket LNA control. r=necko-reviewers,kershaw,valentin
This commit implements enhanced Local Network Access (LNA) configuration
for WebSocket connections by adding the network.lna.websocket.enabled preference.
Changes:
- Add network.lna.websocket.enabled preference (default: true)
- Modify nsHttpTransaction::AllowedToConnectToIpAddressSpace to skip LNA
checks for WebSocket connections when preference is disabled
- Add browser test for WebSocket LNA preference functionality
The preference allows administrators to selectively disable LNA checks
for WebSocket connections while maintaining checks for regular HTTP requests.
Differential Revision: https://phabricator.services.mozilla.com/D266461
Diffstat:
4 files changed, 89 insertions(+), 0 deletions(-)
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
@@ -14482,6 +14482,14 @@
value: ""
mirror: never
+# When this pref is false, skip all LNA checks for WebSocket connections.
+# When true, WebSocket connections follow normal LNA rules.
+# Currently this is disabled for parity with chrome
+- name: network.lna.websocket.enabled
+ type: RelaxedAtomicBool
+ value: false
+ mirror: always
+
# The proxy type. See nsIProtocolProxyService.idl
# PROXYCONFIG_DIRECT = 0
# PROXYCONFIG_MANUAL = 1
diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -3749,6 +3749,12 @@ bool nsHttpTransaction::AllowedToConnectToIpAddressSpace(
return true;
}
+ // Skip LNA checks entirely for WebSocket connections if websocket LNA is
+ // disabled
+ if (!StaticPrefs::network_lna_websocket_enabled() && IsWebsocketUpgrade()) {
+ return true; // Allow all WebSocket connections
+ }
+
// store targetIpAddress space which is required later by nsHttpChannel for
// permission prompts
{
diff --git a/netwerk/test/browser/browser_test_local_network_access.js b/netwerk/test/browser/browser_test_local_network_access.js
@@ -260,6 +260,7 @@ add_task(async function test_lna_prompt_behavior() {
await runPromptedLnaTest(test, "private", "local-network");
}
+ Services.prefs.clearUserPref("network.lna.address_space.public.override");
Services.prefs.clearUserPref("network.lna.address_space.private.override");
});
@@ -461,3 +462,76 @@ add_task(async function test_lna_top_level_navigation_disabled() {
await SpecialPowers.popPrefEnv();
});
+
+add_task(async function test_lna_websocket_preference() {
+ info("Testing network.lna.websocket.enabled preference");
+
+ // Set up LNA to trigger for localhost connections
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["network.lna.address_space.public.override", "127.0.0.1:4443"],
+ ["network.lna.blocking", true],
+ ["network.lna.websocket.enabled", false], // Disable WebSocket LNA checks
+ ],
+ });
+
+ try {
+ // Test WebSocket with LNA disabled - should bypass LNA and get connection refused
+ const websocketTest = {
+ type: "websocket",
+ allowStatus: Cr.NS_ERROR_WEBSOCKET_CONNECTION_REFUSED,
+ denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
+ };
+
+ const rand = Math.random();
+ const promise = observeAndCheck(
+ websocketTest.type,
+ rand,
+ websocketTest.allowStatus, // Should get connection refused, not LNA denied
+ "WebSocket test with LNA disabled should bypass LNA checks"
+ );
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `${baseURL}page_with_non_trackers.html?test=${websocketTest.type}&rand=${rand}`
+ );
+
+ await promise;
+ gBrowser.removeTab(tab);
+
+ info(
+ "WebSocket LNA disabled test completed - connection was allowed to proceed"
+ );
+
+ // Now test with WebSocket LNA enabled - should trigger LNA denial
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["network.lna.websocket.enabled", true], // Enable WebSocket LNA checks
+ ["network.localhost.prompt.testing", true],
+ ["network.localhost.prompt.testing.allow", false],
+ ],
+ });
+
+ const rand2 = Math.random();
+ const promise2 = observeAndCheck(
+ websocketTest.type,
+ rand2,
+ websocketTest.denyStatus, // Should get LNA denied
+ "WebSocket test with LNA enabled should trigger LNA checks"
+ );
+
+ const tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `${baseURL}page_with_non_trackers.html?test=${websocketTest.type}&rand=${rand2}`
+ );
+
+ await promise2;
+ gBrowser.removeTab(tab2);
+
+ info("WebSocket LNA enabled test completed - LNA checks were applied");
+ } catch (error) {
+ ok(false, `WebSocket LNA preference test failed: ${error.message}`);
+ }
+
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/netwerk/test/browser/browser_test_local_network_trackers.js b/netwerk/test/browser/browser_test_local_network_trackers.js
@@ -52,6 +52,7 @@ add_setup(async function () {
["network.lna.block_trackers", true],
["network.lna.address_space.public.override", "127.0.0.1:4443"],
["network.lna.blocking", true],
+ ["network.lna.websocket.enabled", true],
// always select allow actions for user prompts
["network.localhost.prompt.testing", true],
["network.localnetwork.prompt.testing", true],