From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- ...browser_webextension_inspected_window_access.js | 315 +++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 devtools/shared/commands/inspected-window/tests/browser_webextension_inspected_window_access.js (limited to 'devtools/shared/commands/inspected-window/tests/browser_webextension_inspected_window_access.js') diff --git a/devtools/shared/commands/inspected-window/tests/browser_webextension_inspected_window_access.js b/devtools/shared/commands/inspected-window/tests/browser_webextension_inspected_window_access.js new file mode 100644 index 0000000000..3b32bb0aaa --- /dev/null +++ b/devtools/shared/commands/inspected-window/tests/browser_webextension_inspected_window_access.js @@ -0,0 +1,315 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +async function run_inspectedWindow_eval({ tab, codeToEval, extension }) { + const fakeExtCallerInfo = { + url: `moz-extension://${extension.uuid}/another/fake-caller-script.js`, + lineNumber: 1, + addonId: extension.id, + }; + const commands = await CommandsFactory.forTab(tab, { isWebExtension: true }); + await commands.targetCommand.startListening(); + const result = await commands.inspectedWindowCommand.eval( + fakeExtCallerInfo, + codeToEval, + {} + ); + await commands.destroy(); + return result; +} + +async function openAboutBlankTabWithExtensionOrigin(extension) { + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + `moz-extension://${extension.uuid}/manifest.json` + ); + const loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + await ContentTask.spawn(tab.linkedBrowser, null, () => { + // about:blank inherits the principal when opened from content. + content.wrappedJSObject.location.assign("about:blank"); + }); + await loaded; + // Sanity checks: + is(tab.linkedBrowser.currentURI.spec, "about:blank", "expected tab"); + is( + tab.linkedBrowser.contentPrincipal.originNoSuffix, + `moz-extension://${extension.uuid}`, + "about:blank should be at the extension origin" + ); + return tab; +} + +async function checkEvalResult({ + extension, + description, + url, + createTab = () => BrowserTestUtils.openNewForegroundTab(gBrowser, url), + expectedResult, +}) { + const tab = await createTab(); + is(tab.linkedBrowser.currentURI.spec, url, "Sanity check: tab URL"); + const result = await run_inspectedWindow_eval({ + tab, + codeToEval: "'code executed at ' + location.href", + extension, + }); + BrowserTestUtils.removeTab(tab); + SimpleTest.isDeeply( + result, + expectedResult, + `eval result for devtools.inspectedWindow.eval at ${url} (${description})` + ); +} + +async function checkEvalAllowed({ extension, description, url, createTab }) { + info(`checkEvalAllowed: ${description} (at URL: ${url})`); + await checkEvalResult({ + extension, + description, + url, + createTab, + expectedResult: { value: `code executed at ${url}` }, + }); +} +async function checkEvalDenied({ extension, description, url, createTab }) { + info(`checkEvalDenied: ${description} (at URL: ${url})`); + await checkEvalResult({ + extension, + description, + url, + createTab, + expectedResult: { + exceptionInfo: { + isError: true, + code: "E_PROTOCOLERROR", + details: [ + "This extension is not allowed on the current inspected window origin", + ], + description: "Inspector protocol error: %s", + }, + }, + }); +} + +add_task(async function test_eval_at_http() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_first", false]], + }); + + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + const httpUrl = "http://example.com/"; + + // When running with --use-http3-server, http:-URLs cannot be loaded. + try { + await fetch(httpUrl); + } catch { + info("Skipping test_eval_at_http because http:-URL cannot be loaded"); + return; + } + + const extension = ExtensionTestUtils.loadExtension({}); + await extension.startup(); + + await checkEvalAllowed({ + extension, + description: "http:-URL", + url: httpUrl, + }); + await extension.unload(); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function test_eval_at_https() { + const extension = ExtensionTestUtils.loadExtension({}); + await extension.startup(); + + const privilegedExtension = ExtensionTestUtils.loadExtension({ + isPrivileged: true, + }); + await privilegedExtension.startup(); + + await checkEvalAllowed({ + extension, + description: "https:-URL", + url: "https://example.com/", + }); + + await checkEvalDenied({ + extension, + description: "a restricted domain", + // Domain in extensions.webextensions.restrictedDomains by browser.toml. + url: "https://test2.example.com/", + }); + + await SpecialPowers.pushPrefEnv({ + set: [["extensions.quarantinedDomains.list", "example.com"]], + }); + + await checkEvalDenied({ + extension, + description: "a quarantined domain", + url: "https://example.com/", + }); + + await checkEvalAllowed({ + extension: privilegedExtension, + description: "a quarantined domain", + url: "https://example.com/", + }); + + await SpecialPowers.popPrefEnv(); + + await extension.unload(); + await privilegedExtension.unload(); +}); + +add_task(async function test_eval_at_sandboxed_page() { + const extension = ExtensionTestUtils.loadExtension({}); + await extension.startup(); + + await checkEvalAllowed({ + extension, + description: "page with CSP sandbox", + url: "https://example.com/document-builder.sjs?headers=Content-Security-Policy:sandbox&html=x", + }); + await checkEvalDenied({ + extension, + description: "restricted domain with CSP sandbox", + url: "https://test2.example.com/document-builder.sjs?headers=Content-Security-Policy:sandbox&html=x", + }); + + await extension.unload(); +}); + +add_task(async function test_eval_at_own_extension_origin_allowed() { + const extension = ExtensionTestUtils.loadExtension({ + background() { + // eslint-disable-next-line no-undef + browser.test.sendMessage( + "blob_url", + URL.createObjectURL(new Blob(["blob: here", { type: "text/html" }])) + ); + }, + files: { + "mozext.html": `moz-extension: here`, + }, + }); + await extension.startup(); + const blobUrl = await extension.awaitMessage("blob_url"); + + await checkEvalAllowed({ + extension, + description: "moz-extension:-URL from own extension", + url: `moz-extension://${extension.uuid}/mozext.html`, + }); + await checkEvalAllowed({ + extension, + description: "blob:-URL from own extension", + url: blobUrl, + }); + await checkEvalAllowed({ + extension, + description: "about:blank with origin from own extension", + url: "about:blank", + createTab: () => openAboutBlankTabWithExtensionOrigin(extension), + }); + + await extension.unload(); +}); + +add_task(async function test_eval_at_other_extension_denied() { + // The extension for which we simulate devtools_page, chosen as caller of + // devtools.inspectedWindow.eval API calls. + const extension = ExtensionTestUtils.loadExtension({}); + await extension.startup(); + + // The other extension, that |extension| should not be able to access: + const otherExt = ExtensionTestUtils.loadExtension({ + background() { + // eslint-disable-next-line no-undef + browser.test.sendMessage( + "blob_url", + URL.createObjectURL(new Blob(["blob: here", { type: "text/html" }])) + ); + }, + files: { + "mozext.html": `moz-extension: here`, + }, + }); + await otherExt.startup(); + const otherExtBlobUrl = await otherExt.awaitMessage("blob_url"); + + await checkEvalDenied({ + extension, + description: "moz-extension:-URL from another extension", + url: `moz-extension://${otherExt.uuid}/mozext.html`, + }); + await checkEvalDenied({ + extension, + description: "blob:-URL from another extension", + url: otherExtBlobUrl, + }); + await checkEvalDenied({ + extension, + description: "about:blank with origin from another extension", + url: "about:blank", + createTab: () => openAboutBlankTabWithExtensionOrigin(otherExt), + }); + + await otherExt.unload(); + await extension.unload(); +}); + +add_task(async function test_eval_at_about() { + const extension = ExtensionTestUtils.loadExtension({}); + await extension.startup(); + await checkEvalAllowed({ + extension, + description: "about:blank (null principal)", + url: "about:blank", + }); + await checkEvalDenied({ + extension, + description: "about:addons (system principal)", + url: "about:addons", + }); + await checkEvalDenied({ + extension, + description: "about:robots (about page)", + url: "about:robots", + }); + await extension.unload(); +}); + +add_task(async function test_eval_at_file() { + // FYI: There is also an equivalent test case with a full end-to-end test at: + // browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_file.js + + const extension = ExtensionTestUtils.loadExtension({}); + await extension.startup(); + + // A dummy file URL that can be loaded in a tab. + const fileUrl = + "file://" + + getTestFilePath("browser_webextension_inspected_window_access.js"); + + // checkEvalAllowed test helper cannot be used, because the file:-URL may + // redirect elsewhere, so the comparison with the full URL fails. + const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, fileUrl); + const result = await run_inspectedWindow_eval({ + tab, + codeToEval: "'code executed at ' + location.protocol", + extension, + }); + BrowserTestUtils.removeTab(tab); + SimpleTest.isDeeply( + result, + { value: "code executed at file:" }, + `eval result for devtools.inspectedWindow.eval at ${fileUrl}` + ); + + await extension.unload(); +}); -- cgit v1.2.3