diff options
Diffstat (limited to 'caps/tests/unit/test_precursor_principal.js')
-rw-r--r-- | caps/tests/unit/test_precursor_principal.js | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/caps/tests/unit/test_precursor_principal.js b/caps/tests/unit/test_precursor_principal.js new file mode 100644 index 0000000000..156d9775b4 --- /dev/null +++ b/caps/tests/unit/test_precursor_principal.js @@ -0,0 +1,259 @@ +"use strict"; +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); +const { ExtensionTestUtils } = ChromeUtils.import( + "resource://testing-common/ExtensionXPCShellUtils.jsm" +); + +XPCShellContentUtils.init(this); +ExtensionTestUtils.init(this); + +const server = XPCShellContentUtils.createHttpServer({ + hosts: ["example.com", "example.org"], +}); + +server.registerPathHandler("/static_frames", (request, response) => { + response.setHeader("Content-Type", "text/html"); + response.write(` + <iframe name="same_origin" sandbox="allow-scripts allow-same-origin" src="http://example.com/frame"></iframe> + <iframe name="same_origin_sandbox" sandbox="allow-scripts" src="http://example.com/frame"></iframe> + <iframe name="cross_origin" sandbox="allow-scripts allow-same-origin" src="http://example.org/frame"></iframe> + <iframe name="cross_origin_sandbox" sandbox="allow-scripts" src="http://example.org/frame"></iframe> + <iframe name="data_uri" sandbox="allow-scripts allow-same-origin" src="data:text/html,<h1>Data Subframe</h1>"></iframe> + <iframe name="data_uri_sandbox" sandbox="allow-scripts" src="data:text/html,<h1>Data Subframe</h1>"></iframe> + <iframe name="srcdoc" sandbox="allow-scripts allow-same-origin" srcdoc="<h1>Srcdoc Subframe</h1>"></iframe> + <iframe name="srcdoc_sandbox" sandbox="allow-scripts" srcdoc="<h1>Srcdoc Subframe</h1>"></iframe> + <iframe name="blank" sandbox="allow-scripts allow-same-origin"></iframe> + <iframe name="blank_sandbox" sandbox="allow-scripts"></iframe> + <iframe name="redirect_com" sandbox="allow-scripts allow-same-origin" src="/redirect?com"></iframe> + <iframe name="redirect_com_sandbox" sandbox="allow-scripts" src="/redirect?com"></iframe> + <iframe name="redirect_org" sandbox="allow-scripts allow-same-origin" src="/redirect?org"></iframe> + <iframe name="redirect_org_sandbox" sandbox="allow-scripts" src="/redirect?org"></iframe> + <iframe name="ext_redirect_com_com" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?com"></iframe> + <iframe name="ext_redirect_com_com_sandbox" sandbox="allow-scripts" src="/ext_redirect?com"></iframe> + <iframe name="ext_redirect_org_com" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?com"></iframe> + <iframe name="ext_redirect_org_com_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?com"></iframe> + <iframe name="ext_redirect_com_org" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?org"></iframe> + <iframe name="ext_redirect_com_org_sandbox" sandbox="allow-scripts" src="/ext_redirect?org"></iframe> + <iframe name="ext_redirect_org_org" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?org"></iframe> + <iframe name="ext_redirect_org_org_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?org"></iframe> + <iframe name="ext_redirect_com_data" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?data"></iframe> + <iframe name="ext_redirect_com_data_sandbox" sandbox="allow-scripts" src="/ext_redirect?data"></iframe> + <iframe name="ext_redirect_org_data" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?data"></iframe> + <iframe name="ext_redirect_org_data_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?data"></iframe> + + <!-- XXX(nika): These aren't static as they perform loads dynamically - perhaps consider testing them separately? --> + <iframe name="client_replace_org_blank" sandbox="allow-scripts allow-same-origin" src="http://example.org/client_replace?blank"></iframe> + <iframe name="client_replace_org_blank_sandbox" sandbox="allow-scripts" src="http://example.org/client_replace?blank"></iframe> + <iframe name="client_replace_org_data" sandbox="allow-scripts allow-same-origin" src="http://example.org/client_replace?data"></iframe> + <iframe name="client_replace_org_data_sandbox" sandbox="allow-scripts" src="http://example.org/client_replace?data"></iframe> + `); +}); + +server.registerPathHandler("/frame", (request, response) => { + response.setHeader("Content-Type", "text/html"); + response.write(`<h1>HTTP Subframe</h1>`); +}); + +server.registerPathHandler("/redirect", (request, response) => { + let redirect; + if (request.queryString == "com") { + redirect = "http://example.com/frame"; + } else if (request.queryString == "org") { + redirect = "http://example.org/frame"; + } else { + response.setStatusLine(request.httpVersion, 404, "Not found"); + return; + } + + response.setStatusLine(request.httpVersion, 302, "Found"); + response.setHeader("Location", redirect); +}); + +server.registerPathHandler("/client_replace", (request, response) => { + let redirect; + if (request.queryString == "blank") { + redirect = "about:blank"; + } else if (request.queryString == "data") { + redirect = "data:text/html,<h1>Data Subframe</h1>"; + } else { + response.setStatusLine(request.httpVersion, 404, "Not found"); + return; + } + + response.setHeader("Content-Type", "text/html"); + response.write(` + <script> + window.location.replace(${JSON.stringify(redirect)}); + </script> + `); +}); + +add_task(async function sandboxed_precursor() { + // Bug 1725345: Make XPCShellContentUtils.createHttpServer support https + Services.prefs.setBoolPref("dom.security.https_first", false); + + let extension = await ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["webRequest", "webRequestBlocking", "<all_urls>"], + }, + background() { + // eslint-disable-next-line no-undef + browser.webRequest.onBeforeRequest.addListener( + details => { + let url = new URL(details.url); + if (!url.pathname.includes("ext_redirect")) { + return {}; + } + + let redirectUrl; + if (url.search == "?com") { + redirectUrl = "http://example.com/frame"; + } else if (url.search == "?org") { + redirectUrl = "http://example.org/frame"; + } else if (url.search == "?data") { + redirectUrl = "data:text/html,<h1>Data Subframe</h1>"; + } + return { redirectUrl }; + }, + { urls: ["<all_urls>"] }, + ["blocking"] + ); + }, + }); + await extension.startup(); + + registerCleanupFunction(async function() { + await extension.unload(); + }); + + for (let userContextId of [undefined, 1]) { + let comURI = Services.io.newURI("http://example.com"); + let comPrin = Services.scriptSecurityManager.createContentPrincipal( + comURI, + { userContextId } + ); + let orgURI = Services.io.newURI("http://example.org"); + let orgPrin = Services.scriptSecurityManager.createContentPrincipal( + orgURI, + { userContextId } + ); + + let page = await XPCShellContentUtils.loadContentPage( + "http://example.com/static_frames", + { + remote: true, + remoteSubframes: true, + userContextId, + } + ); + let bc = page.browsingContext; + + ok( + bc.currentWindowGlobal.documentPrincipal.equals(comPrin), + "toplevel principal matches" + ); + + // XXX: This is sketchy as heck, but it's also the easiest way to wait for + // the `window.location.replace` loads to finish. + await TestUtils.waitForCondition( + () => + bc.children.every( + child => + !child.currentWindowGlobal.documentURI.spec.includes( + "/client_replace" + ) + ), + "wait for every client_replace global to be replaced" + ); + + let principals = {}; + for (let child of bc.children) { + notEqual(child.name, "", "child frames must have names"); + ok(!(child.name in principals), "duplicate child frame name"); + principals[child.name] = child.currentWindowGlobal.documentPrincipal; + } + + function principal_is(name, expected) { + let principal = principals[name]; + info(`${name} = ${principal.origin}`); + ok(principal.equals(expected), `${name} is correct`); + } + function precursor_is(name, precursor) { + let principal = principals[name]; + info(`${name} = ${principal.origin}`); + ok(principal.isNullPrincipal, `${name} is null`); + ok( + principal.precursorPrincipal.equals(precursor), + `${name} has the correct precursor` + ); + } + + // Basic loads should have the principals or precursor principals for the + // document being loaded. + principal_is("same_origin", comPrin); + precursor_is("same_origin_sandbox", comPrin); + + principal_is("cross_origin", orgPrin); + precursor_is("cross_origin_sandbox", orgPrin); + + // Loads of a data: URI should complete with a sandboxed principal based on + // the principal which tried to perform the load. + precursor_is("data_uri", comPrin); + precursor_is("data_uri_sandbox", comPrin); + + // Loads which inherit principals, such as srcdoc an about:blank loads, + // should also inherit sandboxed precursor principals. + principal_is("srcdoc", comPrin); + precursor_is("srcdoc_sandbox", comPrin); + + principal_is("blank", comPrin); + precursor_is("blank_sandbox", comPrin); + + // Redirects shouldn't interfere with the final principal, and it should be + // based only on the final URI. + principal_is("redirect_com", comPrin); + precursor_is("redirect_com_sandbox", comPrin); + + principal_is("redirect_org", orgPrin); + precursor_is("redirect_org_sandbox", orgPrin); + + // Extension redirects should act like normal redirects, and still resolve + // with the principal or sandboxed principal of the final URI. + principal_is("ext_redirect_com_com", comPrin); + precursor_is("ext_redirect_com_com_sandbox", comPrin); + + principal_is("ext_redirect_com_org", orgPrin); + precursor_is("ext_redirect_com_org_sandbox", orgPrin); + + principal_is("ext_redirect_org_com", comPrin); + precursor_is("ext_redirect_org_com_sandbox", comPrin); + + principal_is("ext_redirect_org_org", orgPrin); + precursor_is("ext_redirect_org_org_sandbox", orgPrin); + + // When an extension redirects to a data: URI, we use the last non-data: URI + // in the chain as the precursor principal. + // FIXME: This should perhaps use the extension's principal instead? + precursor_is("ext_redirect_com_data", comPrin); + precursor_is("ext_redirect_com_data_sandbox", comPrin); + + precursor_is("ext_redirect_org_data", orgPrin); + precursor_is("ext_redirect_org_data_sandbox", orgPrin); + + // Check that navigations triggred by script within the frames will have the + // correct behaviour when navigating to blank and data URIs. + principal_is("client_replace_org_blank", orgPrin); + precursor_is("client_replace_org_blank_sandbox", orgPrin); + + precursor_is("client_replace_org_data", orgPrin); + precursor_is("client_replace_org_data_sandbox", orgPrin); + + await page.close(); + } + Services.prefs.clearUserPref("dom.security.https_first"); +}); |