Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

"use strict";
Services.scriptloader.loadSubScript(
new URL("head_local_network_access.js", gTestPath).href,
this
);
add_setup(async function () {
await setupLnaPrefs();
await setupLnaServer();
});
requestLongerTimeout(4);
/**
* Test: Same-origin iframe inherits Feature Policy from parent
*
* Scenario: Parent page (example.com) creates a same-origin iframe (example.com)
* Expected: Iframe automatically inherits the parent's Feature Policy allowlist
* and can make LNA requests after permission is granted to the parent.
*
* This validates that same-origin iframes don't need explicit delegation via
* the allow attribute - they inherit permissions from their parent document.
*/
add_task(async function test_feature_policy_same_origin_iframe() {
info("Test: Same-origin iframe inherits Feature Policy from parent");
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand = Math.random();
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?iframe=same-origin&rand=${rand}`;
const promise = observeAndCheck(
"fetch",
rand,
Cr.NS_OK,
"Same-origin iframe request should succeed after permission granted"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(
PROMPT_ALLOW_BUTTON,
gBrowser.selectedBrowser,
"loopback-network"
);
await promise;
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
/**
* Test: Cross-origin iframe blocked without allow attribute
*
* Scenario: Parent page (example.com) creates a cross-origin iframe (example.org)
* WITHOUT an allow attribute
* Expected: The iframe's LNA request is blocked by Feature Policy BEFORE showing
* a permission prompt to the user.
*
* This validates Feature Policy's default-deny behavior for cross-origin iframes.
* Cross-origin contexts must explicitly opt-in via the allow attribute to access
* powerful features like LNA.
*/
add_task(async function test_feature_policy_cross_origin_blocked() {
info("Test: Cross-origin iframe blocked without allow attribute");
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand = Math.random();
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?iframe=cross-origin-no-allow&rand=${rand}`;
const promise = observeAndCheck(
"fetch",
rand,
Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
"Cross-origin iframe without allow should be blocked by Feature Policy"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await promise;
let popup = PopupNotifications.getNotification(
"loopback-network",
tab.linkedBrowser
);
ok(
!popup,
"No permission prompt should appear when Feature Policy blocks request"
);
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
/**
* Test: Cross-origin iframe allowed with explicit loopback-network delegation
*
* Scenario: Parent page (example.com) creates a cross-origin iframe (example.org)
* WITH allow="loopback-network" attribute
* Expected: After the parent grants permission, the iframe can successfully make
* loopback network requests (e.g., to localhost/127.0.0.1).
*
* This validates that the loopback-network permission can be explicitly delegated
* to cross-origin iframes via the allow attribute, and that delegated iframes can
* use the parent's granted permission.
*/
add_task(async function test_feature_policy_cross_origin_loopback_allowed() {
info(
"Test: Cross-origin iframe allowed with explicit loopback-network delegation"
);
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand = Math.random();
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?iframe=cross-origin-loopback&rand=${rand}`;
const promise = observeAndCheck(
"fetch",
rand,
Cr.NS_OK,
"Cross-origin iframe with allow=loopback-network should succeed after permission"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(
PROMPT_ALLOW_BUTTON,
gBrowser.selectedBrowser,
"loopback-network"
);
await promise;
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
/**
* Test: Cross-origin iframe allowed with explicit local-network delegation
*
* Scenario: Parent page (example.com) creates a cross-origin iframe (example.org)
* WITH allow="local-network" attribute
* Expected: After the parent grants permission, the iframe can successfully make
* local network requests (e.g., to private IP addresses like 192.168.x.x).
*
* This validates that the local-network permission (distinct from loopback-network)
* can be separately delegated to cross-origin iframes, allowing access to private
* network resources.
*/
add_task(
async function test_feature_policy_cross_origin_local_network_allowed() {
info(
"Test: Cross-origin iframe allowed with explicit local-network delegation"
);
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [
["network.lna.address_space.public.override", "127.0.0.1:4443"],
["network.lna.address_space.private.override", "127.0.0.1:21555"],
],
});
const rand = Math.random();
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?iframe=cross-origin-local-network&rand=${rand}`;
const promise = observeAndCheck(
"fetch",
rand,
Cr.NS_OK,
"Cross-origin iframe with allow=local-network should succeed after permission"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(
PROMPT_ALLOW_BUTTON,
gBrowser.selectedBrowser,
"local-network"
);
await promise;
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
}
);
/**
* Test: Both loopback-network and local-network can be delegated simultaneously
*
* Scenario: Parent page (example.com) creates a cross-origin iframe (example.org)
* WITH allow="loopback-network; local-network" attribute
* Expected: After the parent grants permission, the iframe can make both loopback
* and local network requests.
*
* This validates:
* 1. Multiple permissions can be delegated simultaneously
* 2. The correct syntax uses semicolons to separate multiple permissions
* (not spaces: "loopback-network; local-network" not "loopback-network local-network")
*/
add_task(async function test_feature_policy_cross_origin_both_permissions() {
info("Test: Both loopback-network and local-network can be delegated");
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand = Math.random();
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?iframe=cross-origin-both&rand=${rand}`;
const promise = observeAndCheck(
"fetch",
rand,
Cr.NS_OK,
"Cross-origin iframe with both permissions delegated should succeed"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(
PROMPT_ALLOW_BUTTON,
gBrowser.selectedBrowser,
"loopback-network"
);
await promise;
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
/**
* Test: Same-origin iframe inherits granted permission from parent document
*
* Scenario: Parent page (example.com) makes an LNA request and gets permission.
* AFTER permission is granted, a same-origin iframe (example.com) is
* dynamically created.
* Expected: The iframe can immediately make LNA requests using the parent's
* cached permission without showing a new permission prompt.
*
* This validates permission inheritance with cached permissions, ensuring no
* race conditions occur when iframes are created after permission is granted.
* The test uses parentRand to trigger parent request first, then manually
* creates the iframe with a different rand value.
*/
add_task(async function test_feature_policy_same_origin_inherits_permission() {
info(
"Test: Same-origin iframe inherits granted permission from parent document"
);
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand1 = Math.random();
const rand2 = Math.random();
info("Step 1: Load page and trigger parent request to grant permission");
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?parentRand=${rand1}`;
const promise1 = observeAndCheck(
"fetch",
rand1,
Cr.NS_OK,
"Parent request should succeed"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(
PROMPT_ALLOW_BUTTON,
gBrowser.selectedBrowser,
"loopback-network"
);
await promise1;
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 300));
info("Step 2: Now create iframe - it should inherit the granted permission");
const promise2 = observeAndCheck(
"fetch",
rand2,
Cr.NS_OK,
"Same-origin iframe should inherit granted permission without new prompt"
);
// Create same-origin iframe that will auto-trigger fetch
await SpecialPowers.spawn(tab.linkedBrowser, [rand2], async r => {
const container = content.document.getElementById("iframe-container");
const iframe = content.document.createElement("iframe");
iframe.id = "test-iframe";
container.appendChild(iframe);
});
await promise2;
let popup = PopupNotifications.getNotification(
"loopback-network",
tab.linkedBrowser
);
ok(
!popup,
"No new permission prompt should appear for same-origin iframe with cached permission"
);
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
/**
* Test: Cross-origin iframe with allow attribute can use parent's cached permission
*
* Scenario: Parent page (example.com) makes an LNA request and gets permission.
* AFTER permission is granted, a cross-origin iframe (example.org) with
* allow="loopback-network" attribute is dynamically created.
* Expected: The iframe can immediately make LNA requests using the parent's
* cached permission without showing a new permission prompt.
*
* This validates that Feature Policy delegation works correctly with cached
* permissions - a cross-origin iframe with proper allow attribute can leverage
* the parent's existing permission grant without re-prompting the user.
*/
add_task(
async function test_feature_policy_cross_origin_with_allow_uses_permission() {
await restorePermissions();
info(
"Test: Cross-origin iframe with allow attribute can use parent permission"
);
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand1 = Math.random();
const rand2 = Math.random();
info("Step 1: Load page and trigger parent request to grant permission");
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?parentRand=${rand1}`;
const promise1 = observeAndCheck(
"fetch",
rand1,
Cr.NS_OK,
"Parent request should succeed"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(
PROMPT_ALLOW_BUTTON,
gBrowser.selectedBrowser,
"loopback-network"
);
await promise1;
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 300));
info(
"Step 2: Create cross-origin iframe with allow attribute - should use parent permission"
);
const promise2 = observeAndCheck(
"fetch",
rand2,
Cr.NS_OK,
"Cross-origin iframe with allow can use parent permission"
);
await SpecialPowers.spawn(tab.linkedBrowser, [rand2], async r => {
const container = content.document.getElementById("iframe-container");
const iframe = content.document.createElement("iframe");
iframe.id = "test-iframe";
iframe.setAttribute("allow", "loopback-network");
container.appendChild(iframe);
});
await promise2;
let popup = PopupNotifications.getNotification(
"loopback-network",
tab.linkedBrowser
);
ok(
!popup,
"No new prompt for cross-origin iframe with allow when parent has permission"
);
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
}
);
/**
* Test: Cross-origin iframe without allow attribute is blocked even with parent permission
*
* Scenario: Parent page (example.com) makes an LNA request and gets permission.
* AFTER permission is granted, a cross-origin iframe (example.org) WITHOUT
* an allow attribute is dynamically created.
* Expected: The iframe's LNA request is blocked by Feature Policy, even though the
* parent has a cached permission grant.
*
* This validates that Feature Policy enforcement is independent of the parent's
* permission state - cross-origin iframes must have explicit delegation via the
* allow attribute to access LNA, regardless of whether the parent has permission.
* This prevents cross-origin contexts from silently inheriting powerful permissions.
*/
add_task(
async function test_feature_policy_cross_origin_without_allow_still_blocked() {
info(
"Test: Cross-origin iframe without allow is blocked even if parent has permission"
);
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand1 = Math.random();
const rand2 = Math.random();
info("Step 1: Load page and trigger parent request to grant permission");
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?parentRand=${rand1}`;
const promise1 = observeAndCheck(
"fetch",
rand1,
Cr.NS_OK,
"Parent request should succeed"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(
PROMPT_ALLOW_BUTTON,
gBrowser.selectedBrowser,
"loopback-network"
);
await promise1;
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 300));
info(
"Step 2: Create cross-origin iframe WITHOUT allow - should be blocked by Feature Policy"
);
const promise2 = observeAndCheck(
"fetch",
rand2,
Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
"Cross-origin iframe without allow blocked even when parent has permission"
);
await SpecialPowers.spawn(tab.linkedBrowser, [rand2], async r => {
const container = content.document.getElementById("iframe-container");
const iframe = content.document.createElement("iframe");
iframe.id = "test-iframe";
container.appendChild(iframe);
});
await promise2;
let popup = PopupNotifications.getNotification(
"loopback-network",
tab.linkedBrowser
);
ok(!popup, "No prompt should appear - Feature Policy blocks it");
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
}
);
/**
* Test: Nested iframes - Feature Policy checks the full delegation chain
*
* Scenario: Parent page (example.com) creates a same-origin iframe (example.com)
* with allow="loopback-network". That same-origin iframe contains a
* nested cross-origin iframe (example.org) WITHOUT an allow attribute.
* Expected: The nested cross-origin iframe's LNA request is blocked by Feature
* Policy.
*
* This validates that Feature Policy checks the entire delegation chain, not just
* the immediate parent-child relationship. Even though:
* - The parent (example.com) delegates to the middle iframe (example.com)
* - The middle iframe is same-origin with parent (inherits permission)
* The nested cross-origin iframe (example.org) is still blocked because the middle
* iframe did not explicitly delegate to it via an allow attribute.
*
* Structure: Parent (example.com) → Middle (example.com, has delegation) →
* Nested (example.org, no delegation) = BLOCKED
*/
add_task(async function test_feature_policy_nested_iframes() {
info(
"Test: Nested iframes respect Feature Policy (cross-origin inside same-origin)"
);
await restorePermissions();
await SpecialPowers.pushPrefEnv({
set: [["network.lna.address_space.public.override", "127.0.0.1:4443"]],
});
const rand = Math.random();
const testURL = `${LNA_BASE_URL}lna_feature_policy_parent.html?iframe=nested&rand=${rand}`;
const promise = observeAndCheck(
"fetch",
rand,
Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
"Nested cross-origin iframe without delegation should be blocked"
);
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
await promise;
let popup = PopupNotifications.getNotification(
"loopback-network",
tab.linkedBrowser
);
ok(
!popup,
"No prompt for nested cross-origin iframe without delegation in allow chain"
);
gBrowser.removeTab(tab);
await SpecialPowers.popPrefEnv();
});