tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 15d2393fd4d89b070c3ddf402e7a5fdba3461636
parent ce12fe1250a15a290f59b643c52e99878645abb2
Author: Valentin Gosu <valentin.gosu@gmail.com>
Date:   Thu, 16 Oct 2025 11:05:03 +0000

Bug 1433890 - Test that webextension dir listing does not contain file or jar URLs r=robwu

Differential Revision: https://phabricator.services.mozilla.com/D268357

Diffstat:
Mtoolkit/components/extensions/moz.build | 1+
Atoolkit/components/extensions/test/browser/protocol-remote-false/browser.toml | 7+++++++
Atoolkit/components/extensions/test/browser/protocol-remote-false/browser_ext_dir_listing.js | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 138 insertions(+), 0 deletions(-)

diff --git a/toolkit/components/extensions/moz.build b/toolkit/components/extensions/moz.build @@ -132,6 +132,7 @@ JAR_MANIFESTS += ["jar.mn"] BROWSER_CHROME_MANIFESTS += [ "test/browser/browser.toml", + "test/browser/protocol-remote-false/browser.toml", ] MOCHITEST_MANIFESTS += [ diff --git a/toolkit/components/extensions/test/browser/protocol-remote-false/browser.toml b/toolkit/components/extensions/test/browser/protocol-remote-false/browser.toml @@ -0,0 +1,7 @@ +[DEFAULT] +prefs = [ + "extensions.webextensions.remote=false", # Needed to display index of unpacked ext + "extensions.webextensions.protocol.remote=false" +] + +["browser_ext_dir_listing.js"] diff --git a/toolkit/components/extensions/test/browser/protocol-remote-false/browser_ext_dir_listing.js b/toolkit/components/extensions/test/browser/protocol-remote-false/browser_ext_dir_listing.js @@ -0,0 +1,130 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); +// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix +const { ExtensionTestCommon } = ChromeUtils.importESModule( + "resource://testing-common/ExtensionTestCommon.sys.mjs" +); + +AddonTestUtils.initMochitest(this); + +async function checkNoFileOrJarURLs(extensionURL) { + // Load the root of the extension using moz-extension:// protocol + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: extensionURL, + }, + async browser => { + info("Successfully loaded extension root page"); + + // Check the page title + let title = await SpecialPowers.spawn(browser, [], () => { + return content.document.title; + }); + is( + title, + `Index of ${extensionURL}`, + `Page title should start with "Index of moz-extension://", got: ${title}` + ); + + // Check that at least the manifest entry is there + let hasManifest = await SpecialPowers.spawn(browser, [], () => { + for (let elem of content.document.querySelectorAll("[href]")) { + let url = elem.href; + if (url.endsWith("manifest.json")) { + return true; + } + } + return false; + }); + ok(hasManifest, "Directory listing should include manifest.json"); + + // Check that no URLs are file:// or jar:// + let invalidURLs = await SpecialPowers.spawn(browser, [], () => { + let invalid = []; + for (let elem of content.document.querySelectorAll("[href],[src]")) { + let url = elem.href || elem.src; + if (url.startsWith("file:") || url.startsWith("jar:")) { + invalid.push(elem.outerHTML); + } + } + return invalid; + }); + + Assert.deepEqual( + invalidURLs, + [], + "No URLs should use file:// or jar:// schemes." + ); + } + ); +} + +// Test that all the URLs in the dirlisting of an extensions root dir +// does not include any file or jar URLs when extensions.webextensions.protocol.remote +// is false. +add_task(async function test_webextension_dir_listing() { + // Verify the pref is set correctly + is( + Services.prefs.getBoolPref("extensions.webextensions.protocol.remote"), + false, + "extensions.webextensions.protocol.remote should be false" + ); + + // Get the extension's base URL. This is a packed extension + // that happens to already be a part of the test environment. + let policy = WebExtensionPolicy.getByID("mochikit@mozilla.org"); + ok(policy, "This extension must exist"); + + let extensionURL = policy.getURL("/"); + info(`Extension base URL: ${extensionURL}`); + await checkNoFileOrJarURLs(extensionURL); +}); + +add_task(async function test_temp_webextension_dir_listing() { + // Verify the pref is set correctly + is( + Services.prefs.getBoolPref("extensions.webextensions.protocol.remote"), + false, + "extensions.webextensions.protocol.remote should be false" + ); + + const ID = "addon@tests.mozilla.org"; + + const addonDir = AddonTestUtils.tempDir.clone().clone(); + addonDir.append("exttest"); + await AddonTestUtils.promiseWriteFilesToDir( + addonDir.path, + ExtensionTestCommon.generateFiles({ + manifest: { + browser_specific_settings: { gecko: { id: ID } }, + version: "1.0", + }, + }) + ); + + // Wait for the extension to start up + let startupPromise = AddonTestUtils.promiseWebExtensionStartup(ID); + let addon = await AddonManager.installTemporaryAddon(addonDir); + await startupPromise; + + // Get the extension policy + let policy = WebExtensionPolicy.getByID(ID); + ok(policy, "Extension policy found"); + + // Get the extension's base URL and check it + let extensionURL = policy.getURL("/"); + info(`Temporary extension base URL: ${extensionURL}`); + await checkNoFileOrJarURLs(extensionURL); + + // Unload the extension + await addon.uninstall(); + + // Clean up the temporary directory + addonDir.remove(true); +});