commit b5afcc29507d3e84f61f27e26038f1393c4472a5
parent 9586335e869ce3552e48d0ea844496489b574ffd
Author: Yoshi Cheng-Hao Huang <allstars.chh@gmail.com>
Date: Fri, 17 Oct 2025 20:06:50 +0000
Bug 1988419 - No need to check document's CSP for WebExtensions. r=robwu
WebExtensions shouldn't be restricted by the document's CSP.
Differential Revision: https://phabricator.services.mozilla.com/D265237
Diffstat:
3 files changed, 122 insertions(+), 12 deletions(-)
diff --git a/dom/script/ModuleLoader.cpp b/dom/script/ModuleLoader.cpp
@@ -69,19 +69,20 @@ bool ModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest, nsresult* aRvOut) {
return false;
}
- // If this document is sandboxed without 'allow-scripts', abort.
- if (GetScriptLoader()->GetDocument()->HasScriptsBlockedBySandbox()) {
- *aRvOut = NS_OK;
- return false;
- }
-
- // To prevent dynamic code execution, content scripts can only
- // load moz-extension URLs.
nsCOMPtr<nsIPrincipal> principal = aRequest->TriggeringPrincipal();
- if (BasePrincipal::Cast(principal)->ContentScriptAddonPolicy() &&
- !aRequest->mURI->SchemeIs("moz-extension")) {
- *aRvOut = NS_ERROR_DOM_WEBEXT_CONTENT_SCRIPT_URI;
- return false;
+ if (BasePrincipal::Cast(principal)->ContentScriptAddonPolicy()) {
+ // To prevent dynamic code execution, content scripts can only
+ // load moz-extension URLs.
+ if (!aRequest->mURI->SchemeIs("moz-extension")) {
+ *aRvOut = NS_ERROR_DOM_WEBEXT_CONTENT_SCRIPT_URI;
+ return false;
+ }
+ } else {
+ // If this document is sandboxed without 'allow-scripts', abort.
+ if (GetScriptLoader()->GetDocument()->HasScriptsBlockedBySandbox()) {
+ *aRvOut = NS_ERROR_CONTENT_BLOCKED;
+ return false;
+ }
}
if (LOG_ENABLED()) {
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_module_import.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_module_import.js
@@ -11,6 +11,13 @@ server.registerPathHandler("/dummy", (request, response) => {
response.write("<!DOCTYPE html><html></html>");
});
+server.registerPathHandler("/sandbox.html", (request, response) => {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Content-Security-Policy", "sandbox allow-same-origin;");
+ response.write(`<!DOCTYPE html><html> </html>`);
+});
+
server.registerPathHandler("/script.js", () => {
ok(false, "Unexpected request to /script.js");
});
@@ -72,6 +79,42 @@ add_task(async function test_disallowed_import() {
await contentPage.close();
});
+// The document's CSP shouldn't restrict the extension.
+add_task(async function test_import_in_sandboxed_document() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ matches: ["http://example.com/sandbox.html"],
+ js: ["main.js"],
+ },
+ ],
+ },
+
+ files: {
+ "main.js": async function () {
+ try {
+ let mod = await import(browser.runtime.getURL("module2.js"));
+ browser.test.assertEq(mod.foo, 2);
+ } catch (e) {
+ browser.test.assertTrue(false, `Threw while importing module: ${e}`);
+ }
+
+ browser.test.sendMessage("done");
+ },
+ "module2.js": MODULE2,
+ },
+ });
+
+ await extension.startup();
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ "http://example.com/sandbox.html"
+ );
+ await extension.awaitMessage("done");
+ await extension.unload();
+ await contentPage.close();
+});
+
add_task(async function test_import_non_web_accessible_non_strict() {
// "non_strict" in the function name is referring to this pref. Setting it
// to 'false' should allow MV2 extensions to dynamically load content
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_sandboxed_resource.js b/toolkit/components/extensions/test/xpcshell/test_ext_sandboxed_resource.js
@@ -53,3 +53,69 @@ add_task(async function test_webext_background_sandbox_privileges() {
await extension.awaitFinish("webext-background-sandbox-privileges");
await extension.unload();
});
+
+add_task(async function test_webext_background_sandbox_iframe_import() {
+ function backgroundSubframeScript() {
+ document.title = "script_ran";
+ import("./module1.js");
+ }
+
+ function backgroundScript() {
+ window.onload = () => {
+ browser.test.assertEq(
+ "script_ran",
+ frames[0].document.title,
+ "Sandbox with allow-scripts executes"
+ );
+
+ browser.test.assertEq(
+ "Initial title",
+ frames[1].document.title,
+ "Sandbox without allow-scripts does not run"
+ );
+ browser.test.sendMessage("background_done");
+ };
+ }
+
+ const MODULE1 = `browser.test.sendMessage("module_done")`;
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ background: {
+ page: "background.html",
+ },
+ },
+
+ files: {
+ "background.html": `<!DOCTYPE>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <script src="background.js"><\/script>
+ <iframe src="background-subframe.html" sandbox="allow-same-origin allow-scripts"></iframe>
+ <iframe src="background-subframe.html" sandbox="allow-same-origin"></iframe>
+ </body>
+ </html>`,
+ "background-subframe.html": `<!DOCTYPE>
+ <html>
+ <title>Initial title</title>
+ <head>
+ <meta charset="utf-8">
+ <script src="background-subframe.js"><\/script>
+ </head>
+ </html>`,
+ "background-subframe.js": backgroundSubframeScript,
+ "background.js": backgroundScript,
+ "module1.js": MODULE1,
+ },
+ });
+
+ await extension.startup();
+ await Promise.all([
+ extension.awaitMessage("background_done"),
+ extension.awaitMessage("module_done"),
+ ]);
+ await extension.unload();
+});