"use strict"; const server = createHttpServer({ hosts: ["example.com", "x.example.com"], }); server.registerPathHandler("/dummy", (req, res) => { res.write("dummy"); }); server.registerPathHandler("/redir", (req, res) => { res.setStatusLine(req.httpVersion, 302, "Found"); res.setHeader("Access-Control-Allow-Origin", "http://example.com"); res.setHeader("Access-Control-Allow-Credentials", "true"); res.setHeader("Location", new URLSearchParams(req.queryString).get("url")); }); add_task(async function load_moz_extension_with_and_without_cors() { let extension = ExtensionTestUtils.loadExtension({ manifest: { web_accessible_resources: ["ok.js"], }, files: { "ok.js": "window.status = 'loaded';", "deny.js": "window.status = 'unexpected load'", }, }); await extension.startup(); const EXT_BASE_URL = `moz-extension://${extension.uuid}`; let contentPage = await ExtensionTestUtils.loadContentPage( "http://example.com/dummy" ); await contentPage.spawn([EXT_BASE_URL], async EXT_BASE_URL => { const { document, window } = this.content; async function checkScriptLoad({ setupScript, expectLoad, description }) { const scriptElem = document.createElement("script"); setupScript(scriptElem); return new Promise(resolve => { window.status = "initial"; scriptElem.onload = () => { Assert.equal(window.status, "loaded", "Script executed upon load"); Assert.ok(expectLoad, `Script loaded - ${description}`); resolve(); }; scriptElem.onerror = () => { Assert.equal(window.status, "initial", "not executed upon error"); Assert.ok(!expectLoad, `Script not loaded - ${description}`); resolve(); }; document.head.append(scriptElem); }); } function sameOriginRedirectUrl(url) { return `http://example.com/redir?url=` + encodeURIComponent(url); } function crossOriginRedirectUrl(url) { return `http://x.example.com/redir?url=` + encodeURIComponent(url); } // Direct load of web-accessible extension script. await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = `${EXT_BASE_URL}/ok.js`; }, expectLoad: true, description: "web-accessible script, plain load", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = `${EXT_BASE_URL}/ok.js`; scriptElem.crossOrigin = "anonymous"; }, expectLoad: true, description: "web-accessible script, cors", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = `${EXT_BASE_URL}/ok.js`; scriptElem.crossOrigin = "use-credentials"; }, expectLoad: true, description: "web-accessible script, cors+credentials", }); // Load of web-accessible extension scripts, after same-origin redirect. await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = sameOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); }, expectLoad: true, description: "same-origin redirect to web-accessible script, plain load", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = sameOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); scriptElem.crossOrigin = "anonymous"; }, expectLoad: true, description: "same-origin redirect to web-accessible script, cors", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = sameOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); scriptElem.crossOrigin = "use-credentials"; }, expectLoad: true, description: "same-origin redirect to web-accessible script, cors+credentials", }); // Load of web-accessible extension scripts, after cross-origin redirect. await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = crossOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); }, expectLoad: true, description: "cross-origin redirect to web-accessible script, plain load", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = crossOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); scriptElem.crossOrigin = "anonymous"; }, expectLoad: true, description: "cross-origin redirect to web-accessible script, cors", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = crossOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); scriptElem.crossOrigin = "use-credentials"; }, expectLoad: true, description: "cross-origin redirect to web-accessible script, cors+credentials", }); // Various loads of non-web-accessible extension script. await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = `${EXT_BASE_URL}/deny.js`; }, expectLoad: false, description: "non-accessible script, plain load", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = `${EXT_BASE_URL}/deny.js`; scriptElem.crossOrigin = "anonymous"; }, expectLoad: false, description: "non-accessible script, cors", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = sameOriginRedirectUrl(`${EXT_BASE_URL}/deny.js`); scriptElem.crossOrigin = "anonymous"; }, expectLoad: false, description: "same-origin redirect to non-accessible script, cors", }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = crossOriginRedirectUrl(`${EXT_BASE_URL}/deny.js`); scriptElem.crossOrigin = "anonymous"; }, expectLoad: false, description: "cross-origin redirect to non-accessible script, cors", }); // Sub-resource integrity usually requires CORS. Verify that web-accessible // extension resources are still subjected to SRI. const sriHashOkJs = // SRI hash for "window.status = 'loaded';" (=ok.js). "sha384-EAofaAZpgy6JshegITJJHeE3ROzn9ngGw1GAuuzjSJV1c/YS9PLvHMt9oh4RovrI"; async function testSRI({ integrityMatches }) { const integrity = integrityMatches ? sriHashOkJs : "sha384-bad-sri-hash"; const sriDescription = integrityMatches ? "web-accessible script, good sri, " : "web-accessible script, sri not matching, "; await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = `${EXT_BASE_URL}/ok.js`; scriptElem.integrity = integrity; }, expectLoad: integrityMatches, description: `${sriDescription} no cors, plain load`, }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = `${EXT_BASE_URL}/ok.js`; scriptElem.crossOrigin = "anonymous"; scriptElem.integrity = integrity; }, expectLoad: integrityMatches, description: `${sriDescription} cors, plain load`, }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = sameOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); scriptElem.crossOrigin = "anonymous"; scriptElem.integrity = integrity; }, expectLoad: integrityMatches, description: `${sriDescription} cors, same-origin redirect`, }); await checkScriptLoad({ setupScript(scriptElem) { scriptElem.src = crossOriginRedirectUrl(`${EXT_BASE_URL}/ok.js`); scriptElem.crossOrigin = "anonymous"; scriptElem.integrity = integrity; }, expectLoad: integrityMatches, description: `${sriDescription} cors, cross-origin redirect`, }); } await testSRI({ integrityMatches: true }); await testSRI({ integrityMatches: false }); }); await contentPage.close(); await extension.unload(); });