test_local_network_access.js (23214B)
1 "use strict"; 2 3 const { HttpServer } = ChromeUtils.importESModule( 4 "resource://testing-common/httpd.sys.mjs" 5 ); 6 const { NodeHTTP2Server } = ChromeUtils.importESModule( 7 "resource://testing-common/NodeServer.sys.mjs" 8 ); 9 10 const override = Cc["@mozilla.org/network/native-dns-override;1"].getService( 11 Ci.nsINativeDNSResolverOverride 12 ); 13 14 function makeChannel(url, triggeringPrincipalURI = null) { 15 let uri2 = NetUtil.newURI(url); 16 // by default system principal is used, which cannot be used for permission based tests 17 // because the default system principal has all permissions 18 var principal = Services.scriptSecurityManager.createContentPrincipal( 19 uri2, 20 {} 21 ); 22 23 // For LNA tests, we need a cross-origin triggering principal to test blocking behavior 24 // If not specified, use a different origin to ensure cross-origin requests 25 var triggeringPrincipal; 26 if (triggeringPrincipalURI) { 27 let triggeringURI = NetUtil.newURI(triggeringPrincipalURI); 28 triggeringPrincipal = Services.scriptSecurityManager.createContentPrincipal( 29 triggeringURI, 30 {} 31 ); 32 } else { 33 // Default to a cross-origin principal (public.example.com) 34 let triggeringURI = NetUtil.newURI("https://public.example.com"); 35 triggeringPrincipal = Services.scriptSecurityManager.createContentPrincipal( 36 triggeringURI, 37 {} 38 ); 39 } 40 41 return NetUtil.newChannel({ 42 uri: url, 43 loadingPrincipal: principal, 44 triggeringPrincipal, 45 securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, 46 contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, 47 }).QueryInterface(Ci.nsIHttpChannel); 48 } 49 50 var ChannelCreationObserver = { 51 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), 52 observe(aSubject, aTopic) { 53 if (aTopic == "http-on-opening-request") { 54 var chan = aSubject.QueryInterface(Ci.nsIHttpChannel); 55 if (chan.URI.spec.includes("test_lna_social_tracker")) { 56 chan.loadInfo.triggeringThirdPartyClassificationFlags = 57 Ci.nsIClassifiedChannel.CLASSIFIED_ANY_SOCIAL_TRACKING; 58 } else if (chan.URI.spec.includes("test_lna_basic_tracker")) { 59 chan.loadInfo.triggeringThirdPartyClassificationFlags = 60 Ci.nsIClassifiedChannel.CLASSIFIED_ANY_BASIC_TRACKING; 61 } else if (chan.URI.spec.includes("test_lna_content_tracker")) { 62 chan.loadInfo.triggeringThirdPartyClassificationFlags = 63 Ci.nsIClassifiedChannel.CLASSIFIED_TRACKING_CONTENT; 64 } 65 } 66 }, 67 }; 68 69 ChromeUtils.defineLazyGetter(this, "H1_URL", function () { 70 return "http://localhost:" + httpServer.identity.primaryPort; 71 }); 72 73 ChromeUtils.defineLazyGetter(this, "H2_URL", function () { 74 return "https://localhost:" + server.port(); 75 }); 76 77 ChromeUtils.defineLazyGetter(this, "H1_EXAMPLE_URL", function () { 78 return "http://example.com:" + httpServer.identity.primaryPort; 79 }); 80 81 ChromeUtils.defineLazyGetter(this, "H1_TEST_EXAMPLE_URL", function () { 82 return "http://test.example.com:" + httpServer.identity.primaryPort; 83 }); 84 85 ChromeUtils.defineLazyGetter(this, "H1_SERVER_LOCAL_URL", function () { 86 return "http://server.local:" + httpServer.identity.primaryPort; 87 }); 88 89 ChromeUtils.defineLazyGetter(this, "H1_API_DEV_LOCAL_URL", function () { 90 return "http://api.dev.local:" + httpServer.identity.primaryPort; 91 }); 92 93 let httpServer = null; 94 let server = new NodeHTTP2Server(); 95 function pathHandler(metadata, response) { 96 response.setStatusLine(metadata.httpVersion, 200, "OK"); 97 let body = "success"; 98 response.bodyOutputStream.write(body, body.length); 99 } 100 101 add_setup(async () => { 102 Services.prefs.setBoolPref("network.lna.block_trackers", true); 103 Services.obs.addObserver(ChannelCreationObserver, "http-on-opening-request"); 104 // fail transactions on Local Network Access 105 Services.prefs.setBoolPref("network.lna.blocking", true); 106 107 // enable prompt for prefs testing, with this we can simulate the prompt actions by 108 // network.lna.blocking.prompt.allow = false/true 109 Services.prefs.setBoolPref("network.localhost.prompt.testing", true); 110 Services.prefs.setBoolPref("network.localnetwork.prompt.testing", true); 111 112 Services.prefs.setBoolPref( 113 "network.lna.local-network-to-localhost.skip-checks", 114 false 115 ); 116 117 Services.prefs.setBoolPref("network.lna.websocket.enabled", true); 118 119 // H1 Server 120 httpServer = new HttpServer(); 121 httpServer.registerPathHandler("/test_lna", pathHandler); 122 httpServer.start(-1); 123 // Add domain identities for testing domain skip patterns 124 httpServer.identity.add("http", "example.com", 80); 125 httpServer.identity.add("http", "test.example.com", 80); 126 httpServer.identity.add("http", "server.local", 80); 127 httpServer.identity.add("http", "api.dev.local", 80); 128 129 // H2 Server 130 let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( 131 Ci.nsIX509CertDB 132 ); 133 addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u"); 134 135 await server.start(); 136 registerCleanupFunction(async () => { 137 try { 138 await server.stop(); 139 await httpServer.stop(); 140 Services.prefs.clearUserPref("network.lna.blocking"); 141 Services.prefs.clearUserPref("network.lna.blocking.prompt.testing"); 142 Services.prefs.clearUserPref("network.localhost.prompt.testing.allow"); 143 Services.prefs.clearUserPref("network.localnetwork.prompt.testing.allow"); 144 Services.prefs.clearUserPref( 145 "network.lna.local-network-to-localhost.skip-checks" 146 ); 147 Services.prefs.clearUserPref("network.lna.websocket.enabled"); 148 149 Services.prefs.clearUserPref( 150 "network.lna.address_space.private.override" 151 ); 152 } catch (e) { 153 // Ignore errors during cleanup 154 info("Error during cleanup:", e); 155 } 156 }); 157 await server.registerPathHandler("/test_lna", (req, resp) => { 158 let content = `ok`; 159 resp.writeHead(200, { 160 "Content-Type": "text/plain", 161 "Content-Length": `${content.length}`, 162 }); 163 resp.end(content); 164 }); 165 }); 166 167 // This test simulates the failure of transaction due to local network access 168 // (local host) and subsequent retries based on user prompt actions. 169 // The user prompt actions are simulated by prefs `network.lna.blocking.prompt.testing.allow` 170 add_task(async function lna_blocking_tests_localhost_prompt() { 171 const localHostTestCases = [ 172 // [allowAction, parentIpAddressSpace, urlSuffix, expectedStatus] 173 [true, Ci.nsILoadInfo.Public, "/test_lna", Cr.NS_OK, H1_URL], 174 [true, Ci.nsILoadInfo.Private, "/test_lna", Cr.NS_OK, H1_URL], 175 [false, Ci.nsILoadInfo.Local, "/test_lna", Cr.NS_OK, H1_URL], 176 [ 177 false, 178 Ci.nsILoadInfo.Public, 179 "/test_lna", 180 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 181 H1_URL, 182 ], 183 [ 184 false, 185 Ci.nsILoadInfo.Private, 186 "/test_lna", 187 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 188 H1_URL, 189 ], 190 [true, Ci.nsILoadInfo.Public, "/test_lna", Cr.NS_OK, H2_URL], 191 [true, Ci.nsILoadInfo.Private, "/test_lna", Cr.NS_OK, H2_URL], 192 [true, Ci.nsILoadInfo.Local, "/test_lna", Cr.NS_OK, H2_URL], 193 [ 194 false, 195 Ci.nsILoadInfo.Public, 196 "/test_lna", 197 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 198 H2_URL, 199 ], 200 [ 201 false, 202 Ci.nsILoadInfo.Private, 203 "/test_lna", 204 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 205 H2_URL, 206 ], 207 [true, Ci.nsILoadInfo.Local, "/test_lna", Cr.NS_OK, H2_URL], 208 [ 209 false, 210 Ci.nsILoadInfo.Public, 211 "/test_lna", 212 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 213 H2_URL, 214 ], 215 [ 216 false, 217 Ci.nsILoadInfo.Private, 218 "/test_lna", 219 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 220 H2_URL, 221 ], 222 [false, Ci.nsILoadInfo.Local, "/test_lna", Cr.NS_OK, H2_URL], 223 // Test cases for local network access from trackers 224 // NO LNA then request should not be blocked 225 [false, Ci.nsILoadInfo.Local, "/test_lna_basic_tracker", Cr.NS_OK, H2_URL], 226 [false, Ci.nsILoadInfo.Local, "/test_lna_social_tracker", Cr.NS_OK, H2_URL], 227 [ 228 false, 229 Ci.nsILoadInfo.Local, 230 "/test_lna_content_tracker", 231 Cr.NS_OK, 232 H2_URL, 233 ], 234 [ 235 false, 236 Ci.nsILoadInfo.Public, 237 "/test_lna_basic_tracker", 238 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 239 H2_URL, 240 ], 241 [ 242 false, 243 Ci.nsILoadInfo.Public, 244 "/test_lna_social_tracker", 245 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 246 H2_URL, 247 ], 248 [ 249 true, 250 Ci.nsILoadInfo.Public, 251 "/test_lna_content_tracker", 252 Cr.NS_OK, 253 H2_URL, 254 ], 255 [ 256 false, 257 Ci.nsILoadInfo.Private, 258 "/test_lna_basic_tracker", 259 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 260 H2_URL, 261 ], 262 [ 263 false, 264 Ci.nsILoadInfo.Private, 265 "/test_lna_social_tracker", 266 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 267 H2_URL, 268 ], 269 [ 270 false, 271 Ci.nsILoadInfo.Private, 272 "/test_lna_content_tracker", 273 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 274 H2_URL, 275 ], 276 ]; 277 278 for (let [allow, space, suffix, expectedStatus, url] of localHostTestCases) { 279 info(`do_test ${url}${suffix}, ${space} -> ${expectedStatus}`); 280 281 Services.prefs.setBoolPref("network.localhost.prompt.testing.allow", allow); 282 283 let chan = makeChannel(url + suffix); 284 chan.loadInfo.parentIpAddressSpace = space; 285 286 let expectFailure = expectedStatus !== Cr.NS_OK ? CL_EXPECT_FAILURE : 0; 287 288 await new Promise(resolve => { 289 chan.asyncOpen(new ChannelListener(resolve, null, expectFailure)); 290 }); 291 292 Assert.equal(chan.status, expectedStatus); 293 if (expectedStatus === Cr.NS_OK) { 294 Assert.equal(chan.protocolVersion, url === H1_URL ? "http/1.1" : "h2"); 295 } 296 } 297 }); 298 299 add_task(async function lna_blocking_tests_local_network() { 300 // add override such that target servers is considered as local network (and not localhost) 301 var override_value = 302 "127.0.0.1" + 303 ":" + 304 httpServer.identity.primaryPort + 305 "," + 306 "127.0.0.1" + 307 ":" + 308 server.port(); 309 310 Services.prefs.setCharPref( 311 "network.lna.address_space.private.override", 312 override_value 313 ); 314 315 const localNetworkTestCases = [ 316 // [allowAction, parentIpAddressSpace, urlSuffix, expectedStatus] 317 [true, Ci.nsILoadInfo.Public, "/test_lna", Cr.NS_OK, H1_URL], 318 [false, Ci.nsILoadInfo.Private, "/test_lna", Cr.NS_OK, H1_URL], 319 [false, Ci.nsILoadInfo.Local, "/test_lna", Cr.NS_OK, H1_URL], 320 [ 321 false, 322 Ci.nsILoadInfo.Public, 323 "/test_lna", 324 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 325 H1_URL, 326 ], 327 [false, Ci.nsILoadInfo.Private, "/test_lna", Cr.NS_OK, H1_URL], 328 [true, Ci.nsILoadInfo.Public, "/test_lna", Cr.NS_OK, H2_URL], 329 [false, Ci.nsILoadInfo.Private, "/test_lna", Cr.NS_OK, H2_URL], 330 [false, Ci.nsILoadInfo.Local, "/test_lna", Cr.NS_OK, H2_URL], 331 [ 332 false, 333 Ci.nsILoadInfo.Public, 334 "/test_lna", 335 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 336 H2_URL, 337 ], 338 ]; 339 340 for (let [ 341 allow, 342 space, 343 suffix, 344 expectedStatus, 345 url, 346 ] of localNetworkTestCases) { 347 info(`do_test ${url}, ${space} -> ${expectedStatus}`); 348 349 Services.prefs.setBoolPref( 350 "network.localnetwork.prompt.testing.allow", 351 allow 352 ); 353 354 let chan = makeChannel(url + suffix); 355 chan.loadInfo.parentIpAddressSpace = space; 356 357 let expectFailure = expectedStatus !== Cr.NS_OK ? CL_EXPECT_FAILURE : 0; 358 359 await new Promise(resolve => { 360 chan.asyncOpen(new ChannelListener(resolve, null, expectFailure)); 361 }); 362 363 Assert.equal(chan.status, expectedStatus); 364 if (expectedStatus === Cr.NS_OK) { 365 Assert.equal(chan.protocolVersion, url === H1_URL ? "http/1.1" : "h2"); 366 } 367 } 368 Services.prefs.clearUserPref("network.lna.address_space.private.override"); 369 }); 370 371 // Test the network.lna.skip-domains preference 372 add_task(async function lna_domain_skip_tests() { 373 // Add DNS overrides to map test domains to 127.0.0.1 374 override.clearOverrides(); 375 Services.dns.clearCache(true); 376 377 override.addIPOverride("example.com", "127.0.0.1"); 378 override.addIPOverride("test.example.com", "127.0.0.1"); 379 override.addIPOverride("server.local", "127.0.0.1"); 380 override.addIPOverride("api.dev.local", "127.0.0.1"); 381 382 // Add override such that target servers are considered as local network (and not localhost) 383 // This includes all the domains we're testing with 384 var override_value = 385 "127.0.0.1" + 386 ":" + 387 httpServer.identity.primaryPort + 388 "," + 389 "127.0.0.1" + 390 ":" + 391 server.port(); 392 393 Services.prefs.setCharPref( 394 "network.lna.address_space.private.override", 395 override_value 396 ); 397 398 const domainSkipTestCases = [ 399 // [skipDomains, parentSpace, expectedStatus, baseURL, description] 400 // Exact domain match 401 [ 402 "localhost", 403 Ci.nsILoadInfo.Public, 404 Cr.NS_OK, 405 H1_URL, 406 "exact domain match - localhost", 407 ], 408 [ 409 "localhost", 410 Ci.nsILoadInfo.Public, 411 Cr.NS_OK, 412 H2_URL, 413 "exact domain match - localhost H2", 414 ], 415 [ 416 "example.com", 417 Ci.nsILoadInfo.Public, 418 Cr.NS_OK, 419 H1_EXAMPLE_URL, 420 "exact domain match - example.com", 421 ], 422 423 // Wildcard domain match 424 [ 425 "*.localhost", 426 Ci.nsILoadInfo.Public, 427 Cr.NS_OK, 428 H1_URL, 429 "wildcard domain match - *.localhost matches localhost", 430 ], 431 [ 432 "*.example.com", 433 Ci.nsILoadInfo.Public, 434 Cr.NS_OK, 435 H1_TEST_EXAMPLE_URL, 436 "wildcard domain match - *.example.com matches test.example.com", 437 ], 438 [ 439 "*.example.com", 440 Ci.nsILoadInfo.Public, 441 Cr.NS_OK, 442 H1_EXAMPLE_URL, 443 "wildcard domain match - *.example.com matches example.com", 444 ], 445 [ 446 "*.test.com", 447 Ci.nsILoadInfo.Public, 448 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 449 H1_EXAMPLE_URL, 450 "wildcard no match - *.test.com doesn't match example.com", 451 ], 452 453 // Multiple domains (comma-separated) 454 [ 455 "example.com,localhost,test.org", 456 Ci.nsILoadInfo.Public, 457 Cr.NS_OK, 458 H1_URL, 459 "multiple domains - localhost match", 460 ], 461 [ 462 "example.com,localhost,test.org", 463 Ci.nsILoadInfo.Public, 464 Cr.NS_OK, 465 H1_EXAMPLE_URL, 466 "multiple domains - example.com match", 467 ], 468 [ 469 "foo.com,test.org", 470 Ci.nsILoadInfo.Public, 471 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 472 H1_EXAMPLE_URL, 473 "multiple domains no match - example.com not in list", 474 ], 475 476 // Empty skip domains (should apply normal LNA rules) 477 [ 478 "", 479 Ci.nsILoadInfo.Public, 480 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 481 H1_URL, 482 "empty skip domains - should block", 483 ], 484 485 // .local domain tests 486 [ 487 "*.local", 488 Ci.nsILoadInfo.Public, 489 Cr.NS_OK, 490 H1_SERVER_LOCAL_URL, 491 "wildcard .local - *.local matches server.local", 492 ], 493 [ 494 "*.local", 495 Ci.nsILoadInfo.Public, 496 Cr.NS_OK, 497 H1_API_DEV_LOCAL_URL, 498 "wildcard .local - *.local matches api.dev.local", 499 ], 500 [ 501 "*.dev.local", 502 Ci.nsILoadInfo.Public, 503 Cr.NS_OK, 504 H1_API_DEV_LOCAL_URL, 505 "wildcard subdomain .local - *.dev.local matches api.dev.local", 506 ], 507 [ 508 "server.local", 509 Ci.nsILoadInfo.Public, 510 Cr.NS_OK, 511 H1_SERVER_LOCAL_URL, 512 "exact match .local - server.local matches server.local", 513 ], 514 [ 515 "*.local", 516 Ci.nsILoadInfo.Public, 517 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 518 H1_URL, 519 "wildcard .local - *.local doesn't match localhost", 520 ], 521 522 // localhost variations 523 [ 524 "localhost,*.local,*.internal", 525 Ci.nsILoadInfo.Public, 526 Cr.NS_OK, 527 H1_URL, 528 "combined patterns - localhost matches localhost", 529 ], 530 [ 531 "localhost,*.local,*.internal", 532 Ci.nsILoadInfo.Public, 533 Cr.NS_OK, 534 H1_SERVER_LOCAL_URL, 535 "combined patterns - *.local matches server.local", 536 ], 537 538 // Plain "*" wildcard matches all domains 539 [ 540 "*", 541 Ci.nsILoadInfo.Public, 542 Cr.NS_OK, 543 H1_URL, 544 "wildcard all - * matches localhost", 545 ], 546 [ 547 "*", 548 Ci.nsILoadInfo.Public, 549 Cr.NS_OK, 550 H1_EXAMPLE_URL, 551 "wildcard all - * matches example.com", 552 ], 553 [ 554 "*", 555 Ci.nsILoadInfo.Public, 556 Cr.NS_OK, 557 H1_SERVER_LOCAL_URL, 558 "wildcard all - * matches server.local", 559 ], 560 [ 561 "*", 562 Ci.nsILoadInfo.Public, 563 Cr.NS_OK, 564 H1_TEST_EXAMPLE_URL, 565 "wildcard all - * matches test.example.com", 566 ], 567 ]; 568 569 for (let [ 570 skipDomains, 571 parentSpace, 572 expectedStatus, 573 url, 574 description, 575 ] of domainSkipTestCases) { 576 info(`Testing domain skip: ${description} - domains: "${skipDomains}"`); 577 578 // Set the domain skip preference 579 Services.prefs.setCharPref("network.lna.skip-domains", skipDomains); 580 581 // Disable prompt simulation for clean testing 582 Services.prefs.setBoolPref("network.localhost.prompt.testing.allow", false); 583 584 let chan = makeChannel(url + "/test_lna"); 585 chan.loadInfo.parentIpAddressSpace = parentSpace; 586 587 let expectFailure = expectedStatus !== Cr.NS_OK ? CL_EXPECT_FAILURE : 0; 588 589 await new Promise(resolve => { 590 chan.asyncOpen(new ChannelListener(resolve, null, expectFailure)); 591 }); 592 593 Assert.equal( 594 chan.status, 595 expectedStatus, 596 `Status should match for: ${description}` 597 ); 598 if (expectedStatus === Cr.NS_OK) { 599 Assert.equal(chan.protocolVersion, url === H2_URL ? "h2" : "http/1.1"); 600 } 601 } 602 603 // Cleanup 604 Services.prefs.clearUserPref("network.lna.skip-domains"); 605 Services.prefs.clearUserPref("network.lna.address_space.private.override"); 606 override.clearOverrides(); 607 Services.dns.clearCache(true); 608 }); 609 // Test the new network.lna.local-network-to-localhost.skip-checks preference 610 add_task(async function lna_local_network_to_localhost_skip_checks() { 611 // Test cases: [skipPref, parentSpace, urlSuffix, expectedStatus, baseURL] 612 const skipTestCases = [ 613 // Skip pref disabled (false) - existing behavior should be preserved 614 [ 615 false, 616 Ci.nsILoadInfo.Private, 617 "/test_lna", 618 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 619 H1_URL, 620 ], 621 [ 622 false, 623 Ci.nsILoadInfo.Private, 624 "/test_lna", 625 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 626 H2_URL, 627 ], 628 [ 629 false, 630 Ci.nsILoadInfo.Public, 631 "/test_lna", 632 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 633 H1_URL, 634 ], 635 [ 636 false, 637 Ci.nsILoadInfo.Public, 638 "/test_lna", 639 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 640 H2_URL, 641 ], 642 643 // Skip pref enabled (true) - new behavior: Private->Local allowed, Public->Local still blocked 644 [true, Ci.nsILoadInfo.Private, "/test_lna", Cr.NS_OK, H1_URL], // Private->Local now allowed 645 [true, Ci.nsILoadInfo.Private, "/test_lna", Cr.NS_OK, H2_URL], // Private->Local now allowed 646 [ 647 true, 648 Ci.nsILoadInfo.Public, 649 "/test_lna", 650 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 651 H1_URL, 652 ], // Public->Local still blocked 653 [ 654 true, 655 Ci.nsILoadInfo.Public, 656 "/test_lna", 657 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 658 H2_URL, 659 ], // Public->Local still blocked 660 ]; 661 662 for (let [ 663 skipPref, 664 parentSpace, 665 suffix, 666 expectedStatus, 667 url, 668 ] of skipTestCases) { 669 info( 670 `Testing skip pref: ${skipPref}, ${parentSpace} -> Local, expect: ${expectedStatus}` 671 ); 672 673 // Set the new skip preference 674 Services.prefs.setBoolPref( 675 "network.lna.local-network-to-localhost.skip-checks", 676 skipPref 677 ); 678 679 // Disable prompt simulation for clean testing (prompt should not affect skip logic) 680 Services.prefs.setBoolPref("network.localhost.prompt.testing.allow", false); 681 682 let chan = makeChannel(url + suffix); 683 chan.loadInfo.parentIpAddressSpace = parentSpace; 684 // Target is always Local (localhost) since we're testing localhost servers 685 686 let expectFailure = expectedStatus !== Cr.NS_OK ? CL_EXPECT_FAILURE : 0; 687 688 await new Promise(resolve => { 689 chan.asyncOpen(new ChannelListener(resolve, null, expectFailure)); 690 }); 691 692 Assert.equal(chan.status, expectedStatus); 693 if (expectedStatus === Cr.NS_OK) { 694 Assert.equal(chan.protocolVersion, url === H1_URL ? "http/1.1" : "h2"); 695 } 696 } 697 698 // Cleanup 699 Services.prefs.clearUserPref( 700 "network.lna.local-network-to-localhost.skip-checks" 701 ); 702 }); 703 704 // Test that same-origin requests skip LNA checks 705 add_task(async function lna_same_origin_skip_checks() { 706 // Ensure the local-network-to-localhost skip pref is disabled for this test 707 Services.prefs.setBoolPref( 708 "network.lna.local-network-to-localhost.skip-checks", 709 false 710 ); 711 712 // Test cases: [triggeringOriginURI, targetURL, parentSpace, expectedStatus, description] 713 const sameOriginTestCases = [ 714 // Same origin cases - should skip LNA checks and allow the request 715 [ 716 H1_URL, 717 H1_URL + "/test_lna", 718 Ci.nsILoadInfo.Public, 719 Cr.NS_OK, 720 "same origin localhost to localhost from Public should be allowed", 721 ], 722 [ 723 H1_URL, 724 H1_URL + "/test_lna", 725 Ci.nsILoadInfo.Private, 726 Cr.NS_OK, 727 "same origin localhost to localhost from Private should be allowed", 728 ], 729 [ 730 H2_URL, 731 H2_URL + "/test_lna", 732 Ci.nsILoadInfo.Public, 733 Cr.NS_OK, 734 "same origin localhost to localhost (H2) from Public should be allowed", 735 ], 736 [ 737 H2_URL, 738 H2_URL + "/test_lna", 739 Ci.nsILoadInfo.Private, 740 Cr.NS_OK, 741 "same origin localhost to localhost (H2) from Private should be allowed", 742 ], 743 744 // Cross-origin cases - should apply normal LNA checks and block 745 // Use null to get the default cross-origin principal (public.example.com) 746 [ 747 null, 748 H1_URL + "/test_lna", 749 Ci.nsILoadInfo.Public, 750 Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED, 751 "cross origin to localhost from Public should be blocked", 752 ], 753 // Note: Private->Local transition test removed temporarily 754 // as there may be other logic affecting this transition 755 756 // Same origin but from local address space should still be allowed 757 [ 758 H1_URL, 759 H1_URL + "/test_lna", 760 Ci.nsILoadInfo.Local, 761 Cr.NS_OK, 762 "same origin localhost to localhost from Local should be allowed", 763 ], 764 ]; 765 766 for (let [ 767 triggeringOriginURI, 768 targetURL, 769 parentSpace, 770 expectedStatus, 771 description, 772 ] of sameOriginTestCases) { 773 info(`Testing same origin check: ${description}`); 774 775 // Disable prompt simulation for clean testing 776 Services.prefs.setBoolPref("network.localhost.prompt.testing.allow", false); 777 778 // Use makeChannel with explicit triggering principal 779 let chan = makeChannel(targetURL, triggeringOriginURI); 780 chan.loadInfo.parentIpAddressSpace = parentSpace; 781 782 let expectFailure = expectedStatus !== Cr.NS_OK ? CL_EXPECT_FAILURE : 0; 783 784 await new Promise(resolve => { 785 chan.asyncOpen(new ChannelListener(resolve, null, expectFailure)); 786 }); 787 788 Assert.equal( 789 chan.status, 790 expectedStatus, 791 `Status should match for: ${description}` 792 ); 793 if (expectedStatus === Cr.NS_OK) { 794 Assert.equal( 795 chan.protocolVersion, 796 targetURL.startsWith(H2_URL) ? "h2" : "http/1.1" 797 ); 798 } 799 } 800 });