tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit d3486cb9ee571efdb6d32a20031cfd89a0205519
parent db5b83a59660343ad980df86781bd2760489ec92
Author: smayya <smayya@mozilla.com>
Date:   Wed, 15 Oct 2025 12:04:33 +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:
Mmodules/libpref/init/StaticPrefList.yaml | 8++++++++
Mnetwerk/protocol/http/nsHttpTransaction.cpp | 6++++++
Mnetwerk/test/browser/browser_test_local_network_access.js | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -14493,6 +14493,14 @@ value: true mirror: always +# 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 @@ -3741,6 +3741,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(); +});