diff options
Diffstat (limited to 'dom/security/test/general')
100 files changed, 5151 insertions, 0 deletions
diff --git a/dom/security/test/general/browser.toml b/dom/security/test/general/browser.toml new file mode 100644 index 0000000000..0f4ec5b224 --- /dev/null +++ b/dom/security/test/general/browser.toml @@ -0,0 +1,81 @@ +[DEFAULT] + +["browser_file_nonscript.js"] +support-files = [ + "file_loads_nonscript.html", + "file_nonscript", + "file_nonscript.xyz", + "file_nonscript.html", + "file_nonscript.txt", + "file_nonscript.json", + "file_script.js", +] + +["browser_restrict_privileged_about_script.js"] +# This test intentionally asserts when in debug builds. Let's rely on opt builds when in CI. +skip-if = ["debug"] +support-files = [ + "file_about_child.html", + "file_1767581.js", +] + +["browser_same_site_cookies_bug1748693.js"] +support-files = ["file_same_site_cookies_bug1748693.sjs"] + +["browser_test_assert_systemprincipal_documents.js"] +skip-if = ["!nightly_build"] +support-files = [ + "file_assert_systemprincipal_documents.html", + "file_assert_systemprincipal_documents_iframe.html", +] + +["browser_test_data_download.js"] +support-files = ["file_data_download.html"] + +["browser_test_data_text_csv.js"] +support-files = ["file_data_text_csv.html"] + +["browser_test_framing_error_pages.js"] +support-files = [ + "file_framing_error_pages_csp.html", + "file_framing_error_pages_xfo.html", + "file_framing_error_pages.sjs", +] + +["browser_test_gpc_privateBrowsingMode.js"] +support-files = [ + "file_empty.html", + "file_gpc_server.sjs", +] + +["browser_test_referrer_loadInOtherProcess.js"] + +["browser_test_report_blocking.js"] +support-files = [ + "file_framing_error_pages_xfo.html", + "file_framing_error_pages_csp.html", + "file_framing_error_pages.sjs", +] + +["browser_test_toplevel_data_navigations.js"] +skip-if = [ + "verify && debug && os == 'mac'", + "debug && (os == 'mac' || os == 'linux')", # Bug 1403815 +] +support-files = [ + "file_toplevel_data_navigations.sjs", + "file_toplevel_data_meta_redirect.html", +] + +["browser_test_view_image_data_navigation.js"] +support-files = [ + "file_view_image_data_navigation.html", + "file_view_bg_image_data_navigation.html", +] + +["browser_test_xfo_embed_object.js"] +support-files = [ + "file_framing_xfo_embed.html", + "file_framing_xfo_object.html", + "file_framing_xfo_embed_object.sjs", +] diff --git a/dom/security/test/general/browser_file_nonscript.js b/dom/security/test/general/browser_file_nonscript.js new file mode 100644 index 0000000000..95243c32a7 --- /dev/null +++ b/dom/security/test/general/browser_file_nonscript.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_fileurl_nonscript_load() { + await SpecialPowers.pushPrefEnv({ + set: [["security.block_fileuri_script_with_wrong_mime", true]], + }); + + let file = getChromeDir(getResolvedURI(gTestPath)); + file.append("file_loads_nonscript.html"); + let uriString = Services.io.newFileURI(file).spec; + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uriString); + registerCleanupFunction(async function () { + BrowserTestUtils.removeTab(tab); + }); + + let counter = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + Cu.exportFunction(Assert.equal.bind(Assert), content.window, { + defineAs: "equal", + }); + content.window.postMessage("run", "*"); + + await new Promise(resolve => { + content.window.addEventListener("message", event => { + if (event.data === "done") { + resolve(); + } + }); + }); + + return content.window.wrappedJSObject.counter; + }); + + is(counter, 1, "Only one script should have run"); +}); diff --git a/dom/security/test/general/browser_restrict_privileged_about_script.js b/dom/security/test/general/browser_restrict_privileged_about_script.js new file mode 100644 index 0000000000..0baa6e3d4d --- /dev/null +++ b/dom/security/test/general/browser_restrict_privileged_about_script.js @@ -0,0 +1,70 @@ +"use strict"; + +const kChildPage = getRootDirectory(gTestPath) + "file_about_child.html"; + +const kAboutPagesRegistered = BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-about-privileged-with-scripts", + kChildPage, + Ci.nsIAboutModule.ALLOW_SCRIPT | + Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD | + Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | + Ci.nsIAboutModule.IS_SECURE_CHROME_UI +); + +add_task(async function test_principal_click() { + await kAboutPagesRegistered; + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.skip_about_page_has_csp_assert", true]], + }); + await BrowserTestUtils.withNewTab( + "about:test-about-privileged-with-scripts", + async function (browser) { + // Wait for page to fully load + info("Waiting for tab to be loaded.."); + // let's look into the fully loaded about page + await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + async function () { + let channel = content.docShell.currentDocumentChannel; + is( + channel.originalURI.asciiSpec, + "about:test-about-privileged-with-scripts", + "sanity check - make sure we test the principal for the correct URI" + ); + + let triggeringPrincipal = channel.loadInfo.triggeringPrincipal; + ok( + triggeringPrincipal.isSystemPrincipal, + "loading about: from privileged page must have a triggering of System" + ); + + let contentPolicyType = channel.loadInfo.externalContentPolicyType; + is( + contentPolicyType, + Ci.nsIContentPolicy.TYPE_DOCUMENT, + "sanity check - loading a top level document" + ); + + let loadingPrincipal = channel.loadInfo.loadingPrincipal; + is( + loadingPrincipal, + null, + "sanity check - load of TYPE_DOCUMENT must have a null loadingPrincipal" + ); + ok( + !content.document.nodePrincipal.isSystemPrincipal, + "sanity check - loaded about page does not have the system principal" + ); + isnot( + content.testResult, + "fail-script-was-loaded", + "The script from https://example.com shouldn't work in an about: page." + ); + } + ); + } + ); +}); diff --git a/dom/security/test/general/browser_same_site_cookies_bug1748693.js b/dom/security/test/general/browser_same_site_cookies_bug1748693.js new file mode 100644 index 0000000000..66a7927889 --- /dev/null +++ b/dom/security/test/general/browser_same_site_cookies_bug1748693.js @@ -0,0 +1,61 @@ +"use strict"; + +const HTTPS_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); +const HTTP_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // Disable eslint, since we explicitly need a insecure URL here for this test. + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" +); + +function checkCookies(expectedCookies = {}) { + info(JSON.stringify(expectedCookies)); + return SpecialPowers.spawn( + gBrowser.selectedBrowser, + [expectedCookies], + async function (expectedCookies) { + let cookies = content.document.getElementById("msg").innerHTML; + info(cookies); + for (const [cookie, expected] of Object.entries(expectedCookies)) { + if (expected) { + ok(cookies.includes(cookie), `${cookie} should be sent`); + } else { + ok(!cookies.includes(cookie), `${cookie} should not be sent`); + } + } + } + ); +} + +add_task(async function bug1748693() { + waitForExplicitFinish(); + + // HTTPS-First would interfere with this test. We want to check wether + // cookies orignally set on a secure site without a "Secure" attribute + // get loaded on a insecure site. For that, we need to visit a + // insecure site, which would otherwise be upgraded by HTTPS-First. + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_first", false]], + }); + + let loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + BrowserTestUtils.startLoadingURIString( + gBrowser, + `${HTTPS_PATH}file_same_site_cookies_bug1748693.sjs?setcookies` + ); + await loaded; + + loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + BrowserTestUtils.startLoadingURIString( + gBrowser, + `${HTTP_PATH}file_same_site_cookies_bug1748693.sjs` + ); + await loaded; + + await checkCookies({ auth: true, auth_secure: false }); + + finish(); +}); diff --git a/dom/security/test/general/browser_test_assert_systemprincipal_documents.js b/dom/security/test/general/browser_test_assert_systemprincipal_documents.js new file mode 100644 index 0000000000..8804e85b2c --- /dev/null +++ b/dom/security/test/general/browser_test_assert_systemprincipal_documents.js @@ -0,0 +1,41 @@ +//"use strict" + +const kTestPath = getRootDirectory(gTestPath); +const kTestURI = kTestPath + "file_assert_systemprincipal_documents.html"; + +add_setup(async function () { + // We expect the assertion in function + // CheckSystemPrincipalLoads as defined in + // file dom/security/nsContentSecurityManager.cpp + SimpleTest.expectAssertions(1); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.disallow_non_local_systemprincipal_in_tests", true], + ["security.allow_unsafe_parent_loads", true], + ], + }); +}); + +add_task(async function open_test_iframe_in_tab() { + // This looks at the iframe (load type SUBDOCUMENT) + await BrowserTestUtils.withNewTab( + { gBrowser, url: kTestURI }, + async browser => { + await SpecialPowers.spawn(browser, [], async function () { + let outerPrincipal = content.document.nodePrincipal; + ok( + outerPrincipal.isSystemPrincipal, + "Sanity: Using SystemPrincipal for test file on chrome://" + ); + const iframeDoc = + content.document.getElementById("testframe").contentDocument; + is( + iframeDoc.body.innerHTML, + "", + "iframe with systemprincipal should be empty document" + ); + }); + } + ); +}); diff --git a/dom/security/test/general/browser_test_data_download.js b/dom/security/test/general/browser_test_data_download.js new file mode 100644 index 0000000000..df5a8aeac4 --- /dev/null +++ b/dom/security/test/general/browser_test_data_download.js @@ -0,0 +1,113 @@ +"use strict"; + +const kTestPath = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" +); +const kTestURI = kTestPath + "file_data_download.html"; + +function addWindowListener(aURL) { + return new Promise(resolve => { + Services.wm.addListener({ + onOpenWindow(aXULWindow) { + info("window opened, waiting for focus"); + Services.wm.removeListener(this); + var domwindow = aXULWindow.docShell.domWindow; + waitForFocus(function () { + is( + domwindow.document.location.href, + aURL, + "should have seen the right window open" + ); + resolve(domwindow); + }, domwindow); + }, + onCloseWindow(aXULWindow) {}, + }); + }); +} + +function waitDelay(delay) { + return new Promise((resolve, reject) => { + /* eslint-disable mozilla/no-arbitrary-setTimeout */ + window.setTimeout(resolve, delay); + }); +} + +function promisePanelOpened() { + if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") { + return Promise.resolve(); + } + return BrowserTestUtils.waitForEvent(DownloadsPanel.panel, "popupshown"); +} + +add_task(async function test_with_downloads_pref_disabled() { + waitForExplicitFinish(); + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.data_uri.block_toplevel_data_uri_navigations", true], + ["browser.download.always_ask_before_handling_new_types", true], + ], + }); + let windowPromise = addWindowListener( + "chrome://mozapps/content/downloads/unknownContentType.xhtml" + ); + BrowserTestUtils.startLoadingURIString(gBrowser, kTestURI); + let win = await windowPromise; + + is( + win.document.getElementById("location").value, + "data-foo.html", + "file name of download should match" + ); + + let mainWindowActivated = BrowserTestUtils.waitForEvent(window, "activate"); + await BrowserTestUtils.closeWindow(win); + await mainWindowActivated; +}); + +add_task(async function test_with_always_ask_pref_disabled() { + waitForExplicitFinish(); + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.data_uri.block_toplevel_data_uri_navigations", true], + ["browser.download.always_ask_before_handling_new_types", false], + ], + }); + let downloadsPanelPromise = promisePanelOpened(); + let downloadsPromise = Downloads.getList(Downloads.PUBLIC); + + BrowserTestUtils.startLoadingURIString(gBrowser, kTestURI); + // wait until downloadsPanel opens before continuing with test + await downloadsPanelPromise; + let downloadList = await downloadsPromise; + + is(DownloadsPanel.isPanelShowing, true, "DownloadsPanel should be open."); + is( + downloadList._downloads.length, + 1, + "File should be successfully downloaded." + ); + + let [download] = downloadList._downloads; + is(download.contentType, "text/html", "File contentType should be correct."); + is( + download.source.url, + "data:text/html,<body>data download</body>", + "File name should be correct." + ); + + info("cleaning up downloads"); + try { + if (Services.appinfo.OS === "WINNT") { + // We need to make the file writable to delete it on Windows. + await IOUtils.setPermissions(download.target.path, 0o600); + } + await IOUtils.remove(download.target.path); + } catch (error) { + info("The file " + download.target.path + " is not removed, " + error); + } + + await downloadList.remove(download); + await download.finalize(); +}); diff --git a/dom/security/test/general/browser_test_data_text_csv.js b/dom/security/test/general/browser_test_data_text_csv.js new file mode 100644 index 0000000000..9855ddce46 --- /dev/null +++ b/dom/security/test/general/browser_test_data_text_csv.js @@ -0,0 +1,108 @@ +"use strict"; + +const kTestPath = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" +); +const kTestURI = kTestPath + "file_data_text_csv.html"; + +function addWindowListener(aURL, aCallback) { + return new Promise(resolve => { + Services.wm.addListener({ + onOpenWindow(aXULWindow) { + info("window opened, waiting for focus"); + Services.wm.removeListener(this); + var domwindow = aXULWindow.docShell.domWindow; + waitForFocus(function () { + is( + domwindow.document.location.href, + aURL, + "should have seen the right window open" + ); + resolve(domwindow); + }, domwindow); + }, + onCloseWindow(aXULWindow) {}, + }); + }); +} + +function promisePanelOpened() { + if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") { + return Promise.resolve(); + } + return BrowserTestUtils.waitForEvent(DownloadsPanel.panel, "popupshown"); +} + +add_task(async function test_with_pref_enabled() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.data_uri.block_toplevel_data_uri_navigations", true], + ["browser.download.always_ask_before_handling_new_types", true], + ], + }); + + let windowPromise = addWindowListener( + "chrome://mozapps/content/downloads/unknownContentType.xhtml" + ); + BrowserTestUtils.startLoadingURIString(gBrowser, kTestURI); + let win = await windowPromise; + + let expectedValue = "Untitled.csv"; + is( + win.document.getElementById("location").value, + expectedValue, + "file name of download should match" + ); + let mainWindowActivated = BrowserTestUtils.waitForEvent(window, "activate"); + await BrowserTestUtils.closeWindow(win); + await mainWindowActivated; +}); + +add_task(async function test_with_pref_disabled() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.data_uri.block_toplevel_data_uri_navigations", true], + ["browser.download.always_ask_before_handling_new_types", false], + ], + }); + let downloadsPanelPromise = promisePanelOpened(); + let downloadsPromise = Downloads.getList(Downloads.PUBLIC); + let sourceURLBit = "text/csv;foo,bar,foobar"; + + info("Loading URI for pref enabled"); + BrowserTestUtils.startLoadingURIString(gBrowser, kTestURI); + info("Waiting for downloads panel to open"); + await downloadsPanelPromise; + info("Getting downloads info after opening downloads panel"); + let downloadList = await downloadsPromise; + + is(DownloadsPanel.isPanelShowing, true, "DownloadsPanel should be open."); + is( + downloadList._downloads.length, + 1, + "File should be successfully downloaded." + ); + + let [download] = downloadList._downloads; + is(download.contentType, "text/csv", "File contentType should be correct."); + is( + download.source.url, + `data:${sourceURLBit}`, + "File name should be correct." + ); + + info("Cleaning up downloads"); + try { + if (Services.appinfo.OS === "WINNT") { + // We need to make the file writable to delete it on Windows. + await IOUtils.setPermissions(download.target.path, 0o600); + } + await IOUtils.remove(download.target.path); + } catch (ex) { + info("The file " + download.target.path + " is not removed, " + ex); + } + + await downloadList.remove(download); + await download.finalize(); +}); diff --git a/dom/security/test/general/browser_test_framing_error_pages.js b/dom/security/test/general/browser_test_framing_error_pages.js new file mode 100644 index 0000000000..9fc10f34c7 --- /dev/null +++ b/dom/security/test/general/browser_test_framing_error_pages.js @@ -0,0 +1,53 @@ +"use strict"; + +const kTestPath = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); +const kTestXFrameOptionsURI = kTestPath + "file_framing_error_pages_xfo.html"; +const kTestXFrameOptionsURIFrame = + kTestPath + "file_framing_error_pages.sjs?xfo"; + +const kTestFrameAncestorsURI = kTestPath + "file_framing_error_pages_csp.html"; +const kTestFrameAncestorsURIFrame = + kTestPath + "file_framing_error_pages.sjs?csp"; + +add_task(async function open_test_xfo_error_page() { + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + let loaded = BrowserTestUtils.browserLoaded( + browser, + true, + kTestXFrameOptionsURIFrame, + true + ); + BrowserTestUtils.startLoadingURIString(browser, kTestXFrameOptionsURI); + await loaded; + + await SpecialPowers.spawn(browser, [], async function () { + const iframeDoc = + content.document.getElementById("testframe").contentDocument; + let errorPage = iframeDoc.body.innerHTML; + ok(errorPage.includes("csp-xfo-error-title"), "xfo error page correct"); + }); + }); +}); + +add_task(async function open_test_csp_frame_ancestor_error_page() { + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + let loaded = BrowserTestUtils.browserLoaded( + browser, + true, + kTestFrameAncestorsURIFrame, + true + ); + BrowserTestUtils.startLoadingURIString(browser, kTestFrameAncestorsURI); + await loaded; + + await SpecialPowers.spawn(browser, [], async function () { + const iframeDoc = + content.document.getElementById("testframe").contentDocument; + let errorPage = iframeDoc.body.innerHTML; + ok(errorPage.includes("csp-xfo-error-title"), "csp error page correct"); + }); + }); +}); diff --git a/dom/security/test/general/browser_test_gpc_privateBrowsingMode.js b/dom/security/test/general/browser_test_gpc_privateBrowsingMode.js new file mode 100644 index 0000000000..5c056395a8 --- /dev/null +++ b/dom/security/test/general/browser_test_gpc_privateBrowsingMode.js @@ -0,0 +1,67 @@ +"use strict"; + +const kTestPath = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); +const kTestURI = kTestPath + "file_empty.html"; + +add_task(async function test_privateModeGPCEnabled() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.globalprivacycontrol.enabled", false], + ["privacy.globalprivacycontrol.pbmode.enabled", true], + ["privacy.globalprivacycontrol.functionality.enabled", true], + ], + }); + let win = await BrowserTestUtils.openNewBrowserWindow({ private: true }); + let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, kTestURI); + let browser = win.gBrowser.getBrowserForTab(tab); + let result = await SpecialPowers.spawn(browser, [], async function () { + return content.window + .fetch("file_gpc_server.sjs") + .then(response => response.text()) + .then(response => { + is(response, "true", "GPC header provided"); + is( + content.window.navigator.globalPrivacyControl, + true, + "GPC on navigator" + ); + // Bug 1320796: Service workers are not enabled in PB Mode + return true; + }); + }); + ok(result, "Promise chain resolves in content process"); + win.close(); +}); + +add_task(async function test_privateModeGPCDisabled() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.globalprivacycontrol.enabled", false], + ["privacy.globalprivacycontrol.pbmode.enabled", false], + ["privacy.globalprivacycontrol.functionality.enabled", true], + ], + }); + let win = await BrowserTestUtils.openNewBrowserWindow({ private: true }); + let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, kTestURI); + let browser = win.gBrowser.getBrowserForTab(tab); + let result = await SpecialPowers.spawn(browser, [], async function () { + return content.window + .fetch("file_gpc_server.sjs") + .then(response => response.text()) + .then(response => { + isnot(response, "true", "GPC header provided"); + isnot( + content.window.navigator.globalPrivacyControl, + true, + "GPC on navigator" + ); + // Bug 1320796: Service workers are not enabled in PB Mode + return true; + }); + }); + ok(result, "Promise chain resolves in content process"); + win.close(); +}); diff --git a/dom/security/test/general/browser_test_referrer_loadInOtherProcess.js b/dom/security/test/general/browser_test_referrer_loadInOtherProcess.js new file mode 100644 index 0000000000..7da60b727d --- /dev/null +++ b/dom/security/test/general/browser_test_referrer_loadInOtherProcess.js @@ -0,0 +1,156 @@ +const TEST_PAGE = + "https://example.org/browser/browser/base/content/test/general/dummy_page.html"; +const TEST_REFERRER = "http://mochi.test:8888/"; + +const ReferrerInfo = Components.Constructor( + "@mozilla.org/referrer-info;1", + "nsIReferrerInfo", + "init" +); + +let referrerInfo = new ReferrerInfo( + Ci.nsIReferrerInfo.ORIGIN, + true, + Services.io.newURI(TEST_REFERRER) +); +let deReferrerInfo = E10SUtils.serializeReferrerInfo(referrerInfo); + +var checkResult = async function (isRemote, browserKey, uri) { + is( + gBrowser.selectedBrowser.isRemoteBrowser, + isRemote, + "isRemoteBrowser should be correct" + ); + + is( + gBrowser.selectedBrowser.permanentKey, + browserKey, + "browser.permanentKey should be correct" + ); + + if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { + let sessionHistory = + gBrowser.selectedBrowser.browsingContext.sessionHistory; + let entry = sessionHistory.getEntryAtIndex(sessionHistory.count - 1); + let args = { uri, referrerInfo: deReferrerInfo, isRemote }; + Assert.equal(entry.URI.spec, args.uri, "Uri should be correct"); + + // Main process like about:mozilla does not trigger the real network request. + // So we don't store referrerInfo in sessionHistory in that case. + // Besides, the referrerInfo stored in sessionHistory was computed, we only + // check pre-computed things. + if (args.isRemote) { + let resultReferrerInfo = entry.referrerInfo; + let expectedReferrerInfo = E10SUtils.deserializeReferrerInfo( + args.referrerInfo + ); + + Assert.equal( + resultReferrerInfo.originalReferrer.spec, + expectedReferrerInfo.originalReferrer.spec, + "originalReferrer should be correct" + ); + Assert.equal( + resultReferrerInfo.sendReferrer, + expectedReferrerInfo.sendReferrer, + "sendReferrer should be correct" + ); + Assert.equal( + resultReferrerInfo.referrerPolicy, + expectedReferrerInfo.referrerPolicy, + "referrerPolicy should be correct" + ); + } else { + Assert.equal(entry.referrerInfo, null, "ReferrerInfo should be correct"); + } + + return; + } + + await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [{ uri, referrerInfo: deReferrerInfo, isRemote }], + async function (args) { + let webNav = content.docShell.QueryInterface(Ci.nsIWebNavigation); + let sessionHistory = webNav.sessionHistory; + let entry = sessionHistory.legacySHistory.getEntryAtIndex( + sessionHistory.count - 1 + ); + + var { E10SUtils } = SpecialPowers.ChromeUtils.importESModule( + "resource://gre/modules/E10SUtils.sys.mjs" + ); + + Assert.equal(entry.URI.spec, args.uri, "Uri should be correct"); + + // Main process like about:mozilla does not trigger the real network request. + // So we don't store referrerInfo in sessionHistory in that case. + // Besides, the referrerInfo stored in sessionHistory was computed, we only + // check pre-computed things. + if (args.isRemote) { + let resultReferrerInfo = entry.referrerInfo; + let expectedReferrerInfo = E10SUtils.deserializeReferrerInfo( + args.referrerInfo + ); + + Assert.equal( + resultReferrerInfo.originalReferrer.spec, + expectedReferrerInfo.originalReferrer.spec, + "originalReferrer should be correct" + ); + Assert.equal( + resultReferrerInfo.sendReferrer, + expectedReferrerInfo.sendReferrer, + "sendReferrer should be correct" + ); + Assert.equal( + resultReferrerInfo.referrerPolicy, + expectedReferrerInfo.referrerPolicy, + "referrerPolicy should be correct" + ); + } else { + Assert.equal( + entry.referrerInfo, + null, + "ReferrerInfo should be correct" + ); + } + } + ); +}; +var waitForLoad = async function (uri) { + info("waitForLoad " + uri); + let loadURIOptions = { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + referrerInfo, + }; + gBrowser.selectedBrowser.webNavigation.loadURI( + Services.io.newURI(uri), + loadURIOptions + ); + + await BrowserTestUtils.browserStopped(gBrowser, uri); +}; + +// Tests referrerInfo when navigating from a page in the remote process to main +// process and vice versa. +add_task(async function test_navigation() { + // Navigate from non remote to remote + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank"); + let testURI = TEST_PAGE; + let { permanentKey } = gBrowser.selectedBrowser; + await waitForLoad(testURI); + await checkResult(true, permanentKey, testURI); + gBrowser.removeCurrentTab(); + + // Navigate from remote to non-remote + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TEST_PAGE); + // Wait for the non-blank page to finish loading + await BrowserTestUtils.browserStopped(gBrowser, TEST_PAGE); + testURI = "about:mozilla"; + permanentKey = gBrowser.selectedBrowser.permanentKey; + await waitForLoad(testURI); + await checkResult(false, permanentKey, testURI); + + gBrowser.removeCurrentTab(); +}); diff --git a/dom/security/test/general/browser_test_report_blocking.js b/dom/security/test/general/browser_test_report_blocking.js new file mode 100644 index 0000000000..ebd7514097 --- /dev/null +++ b/dom/security/test/general/browser_test_report_blocking.js @@ -0,0 +1,218 @@ +"use strict"; + +const { TelemetryArchiveTesting } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryArchiveTesting.sys.mjs" +); + +const kTestPath = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +const kTestXFrameOptionsURI = kTestPath + "file_framing_error_pages_xfo.html"; +const kTestCspURI = kTestPath + "file_framing_error_pages_csp.html"; +const kTestXFrameOptionsURIFrame = + kTestPath + "file_framing_error_pages.sjs?xfo"; +const kTestCspURIFrame = kTestPath + "file_framing_error_pages.sjs?csp"; + +const kTestExpectedPingXFO = [ + [["payload", "error_type"], "xfo"], + [["payload", "xfo_header"], "deny"], + [["payload", "csp_header"], ""], + [["payload", "frame_hostname"], "example.com"], + [["payload", "top_hostname"], "example.com"], + [ + ["payload", "frame_uri"], + "https://example.com/browser/dom/security/test/general/file_framing_error_pages.sjs", + ], + [ + ["payload", "top_uri"], + "https://example.com/browser/dom/security/test/general/file_framing_error_pages_xfo.html", + ], +]; + +const kTestExpectedPingCSP = [ + [["payload", "error_type"], "csp"], + [["payload", "xfo_header"], ""], + [["payload", "csp_header"], "'none'"], + [["payload", "frame_hostname"], "example.com"], + [["payload", "top_hostname"], "example.com"], + [ + ["payload", "frame_uri"], + "https://example.com/browser/dom/security/test/general/file_framing_error_pages.sjs", + ], + [ + ["payload", "top_uri"], + "https://example.com/browser/dom/security/test/general/file_framing_error_pages_csp.html", + ], +]; + +const TEST_CASES = [ + { + type: "xfo", + test_uri: kTestXFrameOptionsURI, + frame_uri: kTestXFrameOptionsURIFrame, + expected_ping: kTestExpectedPingXFO, + }, + { + type: "csp", + test_uri: kTestCspURI, + frame_uri: kTestCspURIFrame, + expected_ping: kTestExpectedPingCSP, + }, +]; + +add_setup(async function () { + Services.telemetry.setEventRecordingEnabled("security.ui.xfocsperror", true); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.xfocsp.errorReporting.enabled", true], + ["security.xfocsp.errorReporting.automatic", false], + ], + }); +}); + +add_task(async function testReportingCases() { + for (const test of TEST_CASES) { + await testReporting(test); + } +}); + +async function testReporting(test) { + // Clear telemetry event before testing. + Services.telemetry.clearEvents(); + + let telemetryChecker = new TelemetryArchiveTesting.Checker(); + await telemetryChecker.promiseInit(); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:blank" + ); + let browser = tab.linkedBrowser; + + let loaded = BrowserTestUtils.browserLoaded( + browser, + true, + test.frame_uri, + true + ); + BrowserTestUtils.startLoadingURIString(browser, test.test_uri); + await loaded; + + let { type } = test; + + let frameBC = await SpecialPowers.spawn(browser, [], async _ => { + const iframe = content.document.getElementById("testframe"); + return iframe.browsingContext; + }); + + await SpecialPowers.spawn(frameBC, [type], async obj => { + // Wait until the reporting UI is visible. + await ContentTaskUtils.waitForCondition(() => { + let reportUI = content.document.getElementById("blockingErrorReporting"); + return ContentTaskUtils.isVisible(reportUI); + }); + + let reportCheckBox = content.document.getElementById( + "automaticallyReportBlockingInFuture" + ); + is( + reportCheckBox.checked, + false, + "The checkbox of the reporting ui should be not checked." + ); + + // Click on the checkbox. + await EventUtils.synthesizeMouseAtCenter(reportCheckBox, {}, content); + }); + BrowserTestUtils.removeTab(tab); + + // Open the error page again + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"); + browser = tab.linkedBrowser; + + loaded = BrowserTestUtils.browserLoaded(browser, true, test.frame_uri, true); + BrowserTestUtils.startLoadingURIString(browser, test.test_uri); + await loaded; + + frameBC = await SpecialPowers.spawn(browser, [], async _ => { + const iframe = content.document.getElementById("testframe"); + return iframe.browsingContext; + }); + + await SpecialPowers.spawn(frameBC, [], async _ => { + // Wait until the reporting UI is visible. + await ContentTaskUtils.waitForCondition(() => { + let reportUI = content.document.getElementById("blockingErrorReporting"); + return ContentTaskUtils.isVisible(reportUI); + }); + + let reportCheckBox = content.document.getElementById( + "automaticallyReportBlockingInFuture" + ); + is( + reportCheckBox.checked, + true, + "The checkbox of the reporting ui should be checked." + ); + + // Click on the checkbox again to disable the reporting. + await EventUtils.synthesizeMouseAtCenter(reportCheckBox, {}, content); + + is( + reportCheckBox.checked, + false, + "The checkbox of the reporting ui should be unchecked." + ); + }); + BrowserTestUtils.removeTab(tab); + + // Open the error page again to see if the reporting is disabled. + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"); + browser = tab.linkedBrowser; + + loaded = BrowserTestUtils.browserLoaded(browser, true, test.frame_uri, true); + BrowserTestUtils.startLoadingURIString(browser, test.test_uri); + await loaded; + + frameBC = await SpecialPowers.spawn(browser, [], async _ => { + const iframe = content.document.getElementById("testframe"); + return iframe.browsingContext; + }); + + await SpecialPowers.spawn(frameBC, [], async _ => { + // Wait until the reporting UI is visible. + await ContentTaskUtils.waitForCondition(() => { + let reportUI = content.document.getElementById("blockingErrorReporting"); + return ContentTaskUtils.isVisible(reportUI); + }); + + let reportCheckBox = content.document.getElementById( + "automaticallyReportBlockingInFuture" + ); + is( + reportCheckBox.checked, + false, + "The checkbox of the reporting ui should be unchecked." + ); + }); + BrowserTestUtils.removeTab(tab); + + // Finally, check if the ping has been archived. + await new Promise(resolve => { + telemetryChecker + .promiseFindPing("xfocsp-error-report", test.expected_ping) + .then( + found => { + ok(found, "Telemetry ping submitted successfully"); + resolve(); + }, + err => { + ok(false, "Exception finding telemetry ping: " + err); + resolve(); + } + ); + }); +} diff --git a/dom/security/test/general/browser_test_toplevel_data_navigations.js b/dom/security/test/general/browser_test_toplevel_data_navigations.js new file mode 100644 index 0000000000..0e006f1fd2 --- /dev/null +++ b/dom/security/test/general/browser_test_toplevel_data_navigations.js @@ -0,0 +1,70 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +"use strict"; + +const kDataBody = "toplevel navigation to data: URI allowed"; +const kDataURI = "data:text/html,<body>" + kDataBody + "</body>"; +const kTestPath = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" +); +const kRedirectURI = kTestPath + "file_toplevel_data_navigations.sjs"; +const kMetaRedirectURI = kTestPath + "file_toplevel_data_meta_redirect.html"; + +add_task(async function test_nav_data_uri() { + await SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + await BrowserTestUtils.withNewTab(kDataURI, async function (browser) { + await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [{ kDataBody }], + async function ({ kDataBody }) { + // eslint-disable-line + is( + content.document.body.innerHTML, + kDataBody, + "data: URI navigation from system should be allowed" + ); + } + ); + }); +}); + +add_task(async function test_nav_data_uri_redirect() { + await SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + let tab = BrowserTestUtils.addTab(gBrowser, kRedirectURI); + registerCleanupFunction(async function () { + BrowserTestUtils.removeTab(tab); + }); + // wait to make sure data: URI did not load before checking that it got blocked + await new Promise(resolve => setTimeout(resolve, 500)); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + is( + content.document.body.innerHTML, + "", + "data: URI navigation after server redirect should be blocked" + ); + }); +}); + +add_task(async function test_nav_data_uri_meta_redirect() { + await SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + let tab = BrowserTestUtils.addTab(gBrowser, kMetaRedirectURI); + registerCleanupFunction(async function () { + BrowserTestUtils.removeTab(tab); + }); + // wait to make sure data: URI did not load before checking that it got blocked + await new Promise(resolve => setTimeout(resolve, 500)); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + is( + content.document.body.innerHTML, + "", + "data: URI navigation after meta redirect should be blocked" + ); + }); +}); diff --git a/dom/security/test/general/browser_test_view_image_data_navigation.js b/dom/security/test/general/browser_test_view_image_data_navigation.js new file mode 100644 index 0000000000..90aace1e3e --- /dev/null +++ b/dom/security/test/general/browser_test_view_image_data_navigation.js @@ -0,0 +1,71 @@ +"use strict"; + +add_task(async function test_principal_right_click_open_link_in_new_tab() { + await SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + + const TEST_PAGE = + getRootDirectory(gTestPath) + "file_view_image_data_navigation.html"; + + await BrowserTestUtils.withNewTab(TEST_PAGE, async function (browser) { + let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); + + // simulate right-click->view-image + BrowserTestUtils.waitForEvent(document, "popupshown", false, event => { + // These are operations that must be executed synchronously with the event. + document.getElementById("context-viewimage").doCommand(); + event.target.hidePopup(); + return true; + }); + BrowserTestUtils.synthesizeMouseAtCenter( + "#testimage", + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + let tab = await loadPromise; + + let spec = tab.linkedBrowser.currentURI.spec; + ok( + spec.startsWith("data:image/svg+xml;"), + "data:image/svg navigation allowed through right-click view-image" + ); + + gBrowser.removeTab(tab); + }); +}); + +add_task(async function test_right_click_open_bg_image() { + await SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + + const TEST_PAGE = + getRootDirectory(gTestPath) + "file_view_bg_image_data_navigation.html"; + + await BrowserTestUtils.withNewTab(TEST_PAGE, async function (browser) { + let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); + + // simulate right-click->view-image + BrowserTestUtils.waitForEvent(document, "popupshown", false, event => { + // These are operations that must be executed synchronously with the event. + document.getElementById("context-viewimage").doCommand(); + event.target.hidePopup(); + return true; + }); + BrowserTestUtils.synthesizeMouseAtCenter( + "#testbody", + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + let tab = await loadPromise; + + let spec = tab.linkedBrowser.currentURI.spec; + ok( + spec.startsWith("data:image/svg+xml;"), + "data:image/svg navigation allowed through right-click view-image with background image" + ); + + gBrowser.removeTab(tab); + }); +}); diff --git a/dom/security/test/general/browser_test_xfo_embed_object.js b/dom/security/test/general/browser_test_xfo_embed_object.js new file mode 100644 index 0000000000..bcc48b984c --- /dev/null +++ b/dom/security/test/general/browser_test_xfo_embed_object.js @@ -0,0 +1,41 @@ +"use strict"; + +const kTestPath = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); +const kTestXFOEmbedURI = kTestPath + "file_framing_xfo_embed.html"; +const kTestXFOObjectURI = kTestPath + "file_framing_xfo_object.html"; + +const errorMessage = `The loading of “https://example.com/browser/dom/security/test/general/file_framing_xfo_embed_object.sjs” in a frame is denied by “X-Frame-Options“ directive set to “deny“`; + +let xfoBlocked = false; + +function onXFOMessage(msgObj) { + const message = msgObj.message; + + if (message.includes(errorMessage)) { + ok(true, "XFO error message logged"); + xfoBlocked = true; + } +} + +add_task(async function open_test_xfo_embed_blocked() { + xfoBlocked = false; + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + Services.console.registerListener(onXFOMessage); + BrowserTestUtils.startLoadingURIString(browser, kTestXFOEmbedURI); + await BrowserTestUtils.waitForCondition(() => xfoBlocked); + Services.console.unregisterListener(onXFOMessage); + }); +}); + +add_task(async function open_test_xfo_object_blocked() { + xfoBlocked = false; + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + Services.console.registerListener(onXFOMessage); + BrowserTestUtils.startLoadingURIString(browser, kTestXFOObjectURI); + await BrowserTestUtils.waitForCondition(() => xfoBlocked); + Services.console.unregisterListener(onXFOMessage); + }); +}); diff --git a/dom/security/test/general/bug1277803.html b/dom/security/test/general/bug1277803.html new file mode 100644 index 0000000000..c8033551a0 --- /dev/null +++ b/dom/security/test/general/bug1277803.html @@ -0,0 +1,11 @@ +<html> + +<head> + <link rel='icon' href='favicon_bug1277803.ico'> +</head> + +<body> +Nothing to see here... +</body> + +</html> diff --git a/dom/security/test/general/chrome.toml b/dom/security/test/general/chrome.toml new file mode 100644 index 0000000000..88feda944b --- /dev/null +++ b/dom/security/test/general/chrome.toml @@ -0,0 +1,15 @@ +[DEFAULT] +support-files = [ + "favicon_bug1277803.ico", + "bug1277803.html", +] + +["test_bug1277803.xhtml"] +skip-if = [ + "os == 'android'", + "verify", +] + +["test_innerhtml_sanitizer.html"] + +["test_innerhtml_sanitizer.xhtml"] diff --git a/dom/security/test/general/closeWindow.sjs b/dom/security/test/general/closeWindow.sjs new file mode 100644 index 0000000000..996db36f6f --- /dev/null +++ b/dom/security/test/general/closeWindow.sjs @@ -0,0 +1,24 @@ +const BODY = ` + <script> + opener.postMessage("ok!", "*"); + close(); + </script>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.queryString.includes("unset")) { + response.setHeader("Set-Cookie", "test=wow", true); + } + + if (request.queryString.includes("none")) { + response.setHeader("Set-Cookie", "test2=wow2; samesite=none", true); + } + + if (request.queryString.includes("lax")) { + response.setHeader("Set-Cookie", "test3=wow3; samesite=lax", true); + } + + response.write(BODY); +} diff --git a/dom/security/test/general/favicon_bug1277803.ico b/dom/security/test/general/favicon_bug1277803.ico Binary files differnew file mode 100644 index 0000000000..d44438903b --- /dev/null +++ b/dom/security/test/general/favicon_bug1277803.ico diff --git a/dom/security/test/general/file_1767581.js b/dom/security/test/general/file_1767581.js new file mode 100644 index 0000000000..259435b1e4 --- /dev/null +++ b/dom/security/test/general/file_1767581.js @@ -0,0 +1 @@ +window.testResult = "fail-script-was-loaded"; diff --git a/dom/security/test/general/file_about_child.html b/dom/security/test/general/file_about_child.html new file mode 100644 index 0000000000..d83e0e4d41 --- /dev/null +++ b/dom/security/test/general/file_about_child.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1767581</title> + <script id="script" src="https://example.com/browser/dom/security/test/general/file_1767581.js"></script> +</head> +<body> + Just an about page that loads in the privileged about process! +</body> +</html>
\ No newline at end of file diff --git a/dom/security/test/general/file_assert_systemprincipal_documents.html b/dom/security/test/general/file_assert_systemprincipal_documents.html new file mode 100644 index 0000000000..2d7ff4d253 --- /dev/null +++ b/dom/security/test/general/file_assert_systemprincipal_documents.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1543579: Block web documents loading into system land </title> +</head> +<body> +<h1>This page loads documents from the SystemPrincipal (which should be blocked)</h1> +<iframe type="chrome" id="testframe" src="http://example.com/browser/dom/security/test/general/file_assert_systemprincipal_documents_iframe.html"></iframe> +</body> +</html> + diff --git a/dom/security/test/general/file_assert_systemprincipal_documents_iframe.html b/dom/security/test/general/file_assert_systemprincipal_documents_iframe.html new file mode 100644 index 0000000000..704625a1da --- /dev/null +++ b/dom/security/test/general/file_assert_systemprincipal_documents_iframe.html @@ -0,0 +1,9 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1543579: Block web documents loading into system land </title> +</head> +<body> +<h1>This is the iframe that should not load.</h1> +</body> +</html> diff --git a/dom/security/test/general/file_block_script_wrong_mime_server.sjs b/dom/security/test/general/file_block_script_wrong_mime_server.sjs new file mode 100644 index 0000000000..d034c797a4 --- /dev/null +++ b/dom/security/test/general/file_block_script_wrong_mime_server.sjs @@ -0,0 +1,37 @@ +// Custom *.sjs specifically for the needs of: +// Bug 1288361 - Block scripts with wrong MIME type + +"use strict"; + +const WORKER = ` + onmessage = function(event) { + postMessage("worker-loaded"); + };`; + +function handleRequest(request, response) { + const query = new URLSearchParams(request.queryString); + + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + // Set MIME type + response.setHeader("Content-Type", query.get("mime"), false); + + // Deliver response + switch (query.get("type")) { + case "script": + response.write(""); + break; + case "worker": + response.write(WORKER); + break; + case "worker-import": + response.write( + `importScripts("file_block_script_wrong_mime_server.sjs?type=script&mime=${query.get( + "mime" + )}");` + ); + response.write(WORKER); + break; + } +} diff --git a/dom/security/test/general/file_block_subresource_redir_to_data.sjs b/dom/security/test/general/file_block_subresource_redir_to_data.sjs new file mode 100644 index 0000000000..1e312bc810 --- /dev/null +++ b/dom/security/test/general/file_block_subresource_redir_to_data.sjs @@ -0,0 +1,33 @@ +"use strict"; + +let SCRIPT_DATA = "alert('this alert should be blocked');"; +let WORKER_DATA = + "onmessage = function(event) { postMessage('worker-loaded'); }"; + +function handleRequest(request, response) { + const query = request.queryString; + + response.setHeader("Cache-Control", "no-cache", false); + response.setStatusLine("1.1", 302, "Found"); + + if (query === "script" || query === "modulescript") { + response.setHeader( + "Location", + "data:text/javascript," + escape(SCRIPT_DATA), + false + ); + return; + } + + if (query === "worker") { + response.setHeader( + "Location", + "data:text/javascript," + escape(WORKER_DATA), + false + ); + return; + } + + // we should never get here; just in case return something unexpected + response.write("do'h"); +} diff --git a/dom/security/test/general/file_block_toplevel_data_navigation.html b/dom/security/test/general/file_block_toplevel_data_navigation.html new file mode 100644 index 0000000000..d6e083a247 --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_navigation.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Toplevel data navigation</title> +</head> +<body> +test1: clicking data: URI tries to navigate window<br/> +<!-- postMessage will not be sent if data: URI is blocked --> +<a id="testlink" href="data:text/html,<body>toplevel data: URI navigations +should be blocked</body>">click me</a> +<script> + document.getElementById('testlink').click(); +</script> +</body> +</html> diff --git a/dom/security/test/general/file_block_toplevel_data_navigation2.html b/dom/security/test/general/file_block_toplevel_data_navigation2.html new file mode 100644 index 0000000000..957189ce07 --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_navigation2.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Toplevel data navigation</title> +</head> +<body> +test2: data: URI in iframe tries to window.open(data:, _blank);<br/> +<iframe id="testFrame" src=""></iframe> +<script> + let DATA_URI = `data:text/html,<body><script> + var win = window.open("data:text/html,<body>toplevel data: URI navigations should be blocked</body>", "_blank"); + <\/script></body>`; + document.getElementById('testFrame').src = DATA_URI; +</script> +</body> +</html> diff --git a/dom/security/test/general/file_block_toplevel_data_navigation3.html b/dom/security/test/general/file_block_toplevel_data_navigation3.html new file mode 100644 index 0000000000..3743a72034 --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_navigation3.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Toplevel data navigation</title> +</head> +<body> +test3: performing data: URI navigation through win.loc.href<br/> +<script> + // postMessage will not be sent if data: URI is blocked + window.location.href = "data:text/html,<body><script>" + + "window.opener.postMessage('test3','*');<\/script>toplevel data: URI " + + "navigations should be blocked</body>"; +</script> +</body> +</html> diff --git a/dom/security/test/general/file_block_toplevel_data_redirect.sjs b/dom/security/test/general/file_block_toplevel_data_redirect.sjs new file mode 100644 index 0000000000..c03ace5f23 --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_redirect.sjs @@ -0,0 +1,13 @@ +// Custom *.sjs file specifically for the needs of Bug: +// Bug 1394554 - Block toplevel data: URI navigations after redirect + +var DATA_URI = + "<body>toplevel data: URI navigations after redirect should be blocked</body>"; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", "data:text/html," + escape(DATA_URI), false); +} diff --git a/dom/security/test/general/file_cache_splitting_isloaded.sjs b/dom/security/test/general/file_cache_splitting_isloaded.sjs new file mode 100644 index 0000000000..a40b9674e5 --- /dev/null +++ b/dom/security/test/general/file_cache_splitting_isloaded.sjs @@ -0,0 +1,35 @@ +/* + Helper Server - + Send a Request with ?queryResult - response will be the + queryString of the next request. + +*/ +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + // save the object state of the initial request, which returns + // async once the server has processed the img request. + if (request.queryString.includes("wait")) { + response.processAsync(); + setObjectState("wait", response); + return; + } + + response.write(IMG_BYTES); + + // return the result + getObjectState("wait", function (queryResponse) { + if (!queryResponse) { + return; + } + queryResponse.write("1"); + queryResponse.finish(); + }); +} diff --git a/dom/security/test/general/file_cache_splitting_server.sjs b/dom/security/test/general/file_cache_splitting_server.sjs new file mode 100644 index 0000000000..da75986f74 --- /dev/null +++ b/dom/security/test/general/file_cache_splitting_server.sjs @@ -0,0 +1,27 @@ +function handleRequest(request, response) { + var receivedRequests = parseInt(getState("requests")); + if (isNaN(receivedRequests)) { + receivedRequests = 0; + } + if (request.queryString.includes("state")) { + response.write(receivedRequests); + return; + } + if (request.queryString.includes("flush")) { + setState("requests", "0"); + response.write("OK"); + return; + } + response.setHeader("Cache-Control", "max-age=999999"); // Force caching + response.setHeader("Content-Type", "text/css"); + receivedRequests = receivedRequests + 1; + setState("requests", "" + receivedRequests); + response.write(` + .test{ + color:red; + } + .test h1{ + font-size:200px; + } + `); +} diff --git a/dom/security/test/general/file_cache_splitting_window.html b/dom/security/test/general/file_cache_splitting_window.html new file mode 100644 index 0000000000..59a2ff2ca9 --- /dev/null +++ b/dom/security/test/general/file_cache_splitting_window.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>Document</title> + <link rel="stylesheet" href="https://example.com/tests/dom/security/test/general/file_cache_splitting_server.sjs"> +</head> +<body> + <h1>HELLO WORLD!</h1> + + <script> + window.addEventListener("load",()=>{ + fetch("file_cache_splitting_isloaded.sjs"); + }); + </script> +</body> +</html> diff --git a/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs b/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs new file mode 100644 index 0000000000..9ee73ae3c4 --- /dev/null +++ b/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs @@ -0,0 +1,45 @@ +// custom *.sjs for Bug 1255240 + +const TEST_FRAME = ` + <!DOCTYPE HTML> + <html> + <head><meta charset='utf-8'></head> + <body> + <a id='testlink' target='innerframe' href='file_contentpolicytype_targeted_link_iframe.sjs?innerframe'>click me</a> + <iframe name='innerframe'></iframe> + <script type='text/javascript'> + var link = document.getElementById('testlink'); + testlink.click(); + </script> + </body> + </html> `; + +const INNER_FRAME = ` + <!DOCTYPE HTML> + <html> + <head><meta charset='utf-8'></head> + hello world! + </body> + </html>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + + var queryString = request.queryString; + + if (queryString === "testframe") { + response.write(TEST_FRAME); + return; + } + + if (queryString === "innerframe") { + response.write(INNER_FRAME); + return; + } + + // we should never get here, but just in case + // return something unexpected + response.write("do'h"); +} diff --git a/dom/security/test/general/file_data_download.html b/dom/security/test/general/file_data_download.html new file mode 100644 index 0000000000..4cc92fe8f5 --- /dev/null +++ b/dom/security/test/general/file_data_download.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test download attribute for data: URI</title> +</head> +<body> + <a href="data:text/html,<body>data download</body>" download="data-foo.html" id="testlink">download data</a> + <script> + // click the link to have the downoad panel appear + let testlink = document.getElementById("testlink"); + testlink.click(); + </script> + </body> +</html> diff --git a/dom/security/test/general/file_data_text_csv.html b/dom/security/test/general/file_data_text_csv.html new file mode 100644 index 0000000000..a9ac369d16 --- /dev/null +++ b/dom/security/test/general/file_data_text_csv.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test open data:text/csv</title> +</head> +<body> + <a href="data:text/csv;foo,bar,foobar" id="testlink">test text/csv</a> + <script> + // click the link to have the downoad panel appear + let testlink = document.getElementById("testlink"); + testlink.click(); + </script> + </body> +</html> diff --git a/dom/security/test/general/file_empty.html b/dom/security/test/general/file_empty.html new file mode 100644 index 0000000000..865879c583 --- /dev/null +++ b/dom/security/test/general/file_empty.html @@ -0,0 +1 @@ +<!-- this file intentionally left blank --> diff --git a/dom/security/test/general/file_framing_error_pages.sjs b/dom/security/test/general/file_framing_error_pages.sjs new file mode 100644 index 0000000000..fb62a34bdb --- /dev/null +++ b/dom/security/test/general/file_framing_error_pages.sjs @@ -0,0 +1,27 @@ +"use strict"; + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + + let query = request.queryString; + if (query === "xfo") { + response.setHeader("x-frame-options", "deny", false); + response.write("<html>xfo test loaded</html>"); + return; + } + + if (query === "csp") { + response.setHeader( + "content-security-policy", + "frame-ancestors 'none'", + false + ); + response.write("<html>csp test loaded</html>"); + return; + } + + // we should never get here, but just in case + // return something unexpected + response.write("do'h"); +} diff --git a/dom/security/test/general/file_framing_error_pages_csp.html b/dom/security/test/general/file_framing_error_pages_csp.html new file mode 100644 index 0000000000..2764ed4aa6 --- /dev/null +++ b/dom/security/test/general/file_framing_error_pages_csp.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<body> +iframe should be blocked <br/> +<iframe id="testframe" src="https://example.com/browser/dom/security/test/general/file_framing_error_pages.sjs?csp" height=800 width=800></iframe> +</body> +</html> diff --git a/dom/security/test/general/file_framing_error_pages_xfo.html b/dom/security/test/general/file_framing_error_pages_xfo.html new file mode 100644 index 0000000000..82dd1ee459 --- /dev/null +++ b/dom/security/test/general/file_framing_error_pages_xfo.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<body> +iframe should be blocked <br/> +<iframe id="testframe" src="https://example.com/browser/dom/security/test/general/file_framing_error_pages.sjs?xfo" height=800 width=800></iframe> +</body> +</html> diff --git a/dom/security/test/general/file_framing_xfo_embed.html b/dom/security/test/general/file_framing_xfo_embed.html new file mode 100644 index 0000000000..f5cc761b5b --- /dev/null +++ b/dom/security/test/general/file_framing_xfo_embed.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<body> + embed should be blocked <br/> + <embed src="https://example.com/browser/dom/security/test/general/file_framing_xfo_embed_object.sjs"></embed> +</body> +</html> diff --git a/dom/security/test/general/file_framing_xfo_embed_object.sjs b/dom/security/test/general/file_framing_xfo_embed_object.sjs new file mode 100644 index 0000000000..56616b7930 --- /dev/null +++ b/dom/security/test/general/file_framing_xfo_embed_object.sjs @@ -0,0 +1,7 @@ +"use strict"; + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("x-frame-options", "deny", false); + response.write("<html>doc with x-frame-options: deny</html>"); +} diff --git a/dom/security/test/general/file_framing_xfo_object.html b/dom/security/test/general/file_framing_xfo_object.html new file mode 100644 index 0000000000..c8480a2c42 --- /dev/null +++ b/dom/security/test/general/file_framing_xfo_object.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<body> + object should be blocked <br/> + <object data="https://example.com/browser/dom/security/test/general/file_framing_xfo_embed_object.sjs"></object> +</body> +</html> diff --git a/dom/security/test/general/file_gpc_server.sjs b/dom/security/test/general/file_gpc_server.sjs new file mode 100644 index 0000000000..d0b14215b4 --- /dev/null +++ b/dom/security/test/general/file_gpc_server.sjs @@ -0,0 +1,14 @@ +"use strict"; + +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Cache-Control", "no-cache", false); + + var gpc = request.hasHeader("Sec-GPC") ? request.getHeader("Sec-GPC") : ""; + + if (gpc === "1") { + response.write("true"); + } else { + response.write("false"); + } +} diff --git a/dom/security/test/general/file_loads_nonscript.html b/dom/security/test/general/file_loads_nonscript.html new file mode 100644 index 0000000000..f7692b8066 --- /dev/null +++ b/dom/security/test/general/file_loads_nonscript.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>File that loads a non-script file-extension as script</title> +</head> +<body> + <script> + /* global equal */ + + const files = ["file_nonscript", + "file_nonscript.xyz", + "file_nonscript.html", + "file_nonscript.txt", + "file_nonscript.json"]; + + async function run() { + window.counter = 0; + + for (let file of files) { + let script = document.createElement("script"); + let promise = new Promise((resolve, reject) => { + script.addEventListener("error", resolve, {once: true}); + script.addEventListener("load", reject, {once: true}); + }); + script.src = file; + document.body.append(script); + + let event = await promise; + equal(event.type, "error"); + equal(window.counter, 0); + } + + let script = document.createElement("script"); + let promise = new Promise((resolve, reject) => { + script.addEventListener("load", resolve, {once: true}); + script.addEventListener("error", reject, {once: true}); + }); + script.src = "file_script.js"; + document.body.append(script); + + let event = await promise; + equal(event.type, "load"); + equal(window.counter, 1); + + window.postMessage("done", "*"); + } + window.addEventListener("message", run, {once: true}) + </script> +</html> diff --git a/dom/security/test/general/file_meta_referrer_in_head.html b/dom/security/test/general/file_meta_referrer_in_head.html new file mode 100644 index 0000000000..9c4c4cd695 --- /dev/null +++ b/dom/security/test/general/file_meta_referrer_in_head.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<meta name="referrer" content="no-referrer" /> +<title>Bug 1704473 - Remove head requirement for meta name=referrer</title> +<script type="application/javascript"> + fetch("https://example.com"); +</script> +</head> +<body> +</body> +</html> diff --git a/dom/security/test/general/file_meta_referrer_notin_head.html b/dom/security/test/general/file_meta_referrer_notin_head.html new file mode 100644 index 0000000000..55bd38e4c5 --- /dev/null +++ b/dom/security/test/general/file_meta_referrer_notin_head.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Bug 1704473 - Remove head requirement for meta name=referrer</title> + +</head> +<body> + <meta name="referrer" content="no-referrer" /> + <script type="application/javascript"> + fetch("https://example.com"); + </script> +</body> +</html> diff --git a/dom/security/test/general/file_nonscript b/dom/security/test/general/file_nonscript new file mode 100644 index 0000000000..c339e45d5d --- /dev/null +++ b/dom/security/test/general/file_nonscript @@ -0,0 +1 @@ +window.counter++; diff --git a/dom/security/test/general/file_nonscript.html b/dom/security/test/general/file_nonscript.html new file mode 100644 index 0000000000..c339e45d5d --- /dev/null +++ b/dom/security/test/general/file_nonscript.html @@ -0,0 +1 @@ +window.counter++; diff --git a/dom/security/test/general/file_nonscript.json b/dom/security/test/general/file_nonscript.json new file mode 100644 index 0000000000..c339e45d5d --- /dev/null +++ b/dom/security/test/general/file_nonscript.json @@ -0,0 +1 @@ +window.counter++; diff --git a/dom/security/test/general/file_nonscript.txt b/dom/security/test/general/file_nonscript.txt new file mode 100644 index 0000000000..c339e45d5d --- /dev/null +++ b/dom/security/test/general/file_nonscript.txt @@ -0,0 +1 @@ +window.counter++; diff --git a/dom/security/test/general/file_nonscript.xyz b/dom/security/test/general/file_nonscript.xyz new file mode 100644 index 0000000000..c339e45d5d --- /dev/null +++ b/dom/security/test/general/file_nonscript.xyz @@ -0,0 +1 @@ +window.counter++; diff --git a/dom/security/test/general/file_nosniff_navigation.sjs b/dom/security/test/general/file_nosniff_navigation.sjs new file mode 100644 index 0000000000..d332d860fd --- /dev/null +++ b/dom/security/test/general/file_nosniff_navigation.sjs @@ -0,0 +1,39 @@ +// Custom *.sjs file specifically for the needs of Bug 1286861 + +// small red image +const IMG = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +// https://stackoverflow.com/questions/17279712/what-is-the-smallest-possible-valid-pdf +const PDF = `%PDF-1.0 +1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj +trailer<</Size 4/Root 1 0 R>>`; + +function getSniffableContent(type) { + switch (type) { + case "xml": + return `<?xml version="1.0"?><test/>`; + case "html": + return `<!Doctype html> <html> <head></head> <body> Test test </body></html>`; + case "css": + return `*{ color: pink !important; }`; + case "json": + return `{ 'test':'yes' }`; + case "img": + return IMG; + case "pdf": + return PDF; + } + return "Basic UTF-8 Text"; +} + +function handleRequest(request, response) { + let query = new URLSearchParams(request.queryString); + + // avoid confusing cache behaviors (XXXX no sure what this means?) + response.setHeader("X-Content-Type-Options", "nosniff"); // Disable Sniffing + response.setHeader("Content-Type", query.get("mime")); + response.write(getSniffableContent(query.get("content"))); +} diff --git a/dom/security/test/general/file_nosniff_testserver.sjs b/dom/security/test/general/file_nosniff_testserver.sjs new file mode 100644 index 0000000000..d3e52979a4 --- /dev/null +++ b/dom/security/test/general/file_nosniff_testserver.sjs @@ -0,0 +1,60 @@ +"use strict"; + +const SCRIPT = "var foo = 24;"; +const CSS = "body { background-color: green; }"; + +// small red image +const IMG = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +function handleRequest(request, response) { + const query = new URLSearchParams(request.queryString); + + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + // set the nosniff header + response.setHeader("X-Content-Type-Options", " NoSniFF , foo ", false); + + if (query.has("cssCorrectType")) { + response.setHeader("Content-Type", "teXt/cSs", false); + response.write(CSS); + return; + } + + if (query.has("cssWrongType")) { + response.setHeader("Content-Type", "text/html", false); + response.write(CSS); + return; + } + + if (query.has("scriptCorrectType")) { + response.setHeader("Content-Type", "appLIcation/jAvaScriPt;blah", false); + response.write(SCRIPT); + return; + } + + if (query.has("scriptWrongType")) { + response.setHeader("Content-Type", "text/html", false); + response.write(SCRIPT); + return; + } + + if (query.has("imgCorrectType")) { + response.setHeader("Content-Type", "iMaGe/pnG;blah", false); + response.write(IMG); + return; + } + + if (query.has("imgWrongType")) { + response.setHeader("Content-Type", "text/html", false); + response.write(IMG); + return; + } + + // we should never get here, but just in case + response.setHeader("Content-Type", "text/html", false); + response.write("do'h"); +} diff --git a/dom/security/test/general/file_same_site_cookies_about.sjs b/dom/security/test/general/file_same_site_cookies_about.sjs new file mode 100644 index 0000000000..421eb999be --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_about.sjs @@ -0,0 +1,99 @@ +// Custom *.sjs file specifically for the needs of Bug 1454721 + +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +const IFRAME_INC = `<iframe src='http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_about.sjs?inclusion'></iframe>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + // using startsWith and discard the math random + if (request.queryString.startsWith("setSameSiteCookie")) { + response.setHeader( + "Set-Cookie", + "myKey=mySameSiteAboutCookie; samesite=strict", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + // navigation tests + if (request.queryString.includes("loadsrcdocframeNav")) { + let FRAME = ` + <iframe srcdoc="foo" + onload="document.location='http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_about.sjs?navigation'"> + </iframe>`; + response.write(FRAME); + return; + } + + if (request.queryString.includes("loadblankframeNav")) { + let FRAME = ` + <iframe src="about:blank" + onload="document.location='http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_about.sjs?navigation'"> + </iframe>`; + response.write(FRAME); + return; + } + + // inclusion tets + if (request.queryString.includes("loadsrcdocframeInc")) { + response.write('<iframe srcdoc="' + IFRAME_INC + '"></iframe>'); + return; + } + + if (request.queryString.includes("loadblankframeInc")) { + let FRAME = + ` + <iframe id="blankframe" src="about:blank"></iframe> + <script> + document.getElementById("blankframe").contentDocument.write(\"` + + IFRAME_INC + + `\"); + <\/script>`; + response.write(FRAME); + return; + } + + if (request.queryString.includes("navigation")) { + const cookies = request.hasHeader("Cookie") + ? request.getHeader("Cookie") + : ""; + response.write(` + <!DOCTYPE html> + <html> + <body> + <script type="application/javascript"> + window.parent.postMessage({result: "${cookies}" }, '*'); + </script> + </body> + </html> + `); + } + + if (request.queryString.includes("inclusion")) { + const cookies = request.hasHeader("Cookie") + ? request.getHeader("Cookie") + : ""; + response.write(` + <!DOCTYPE html> + <html> + <body> + <script type="application/javascript"> + window.parent.parent.parent.postMessage({result: "${cookies}" }, '*'); + </script> + </body> + </html> + `); + } + + // we should never get here, but just in case return something unexpected + response.write("D'oh"); +} diff --git a/dom/security/test/general/file_same_site_cookies_blob_iframe_inclusion.html b/dom/security/test/general/file_same_site_cookies_blob_iframe_inclusion.html new file mode 100644 index 0000000000..b3456f0b90 --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_blob_iframe_inclusion.html @@ -0,0 +1,34 @@ +<html> +<body> +<iframe id="testframe"></iframe> +<script type="application/javascript"> + + // simply passing on the message from the child to parent + window.addEventListener("message", receiveMessage); + function receiveMessage(event) { + window.removeEventListener("message", receiveMessage); + window.parent.postMessage({result: event.data.result}, '*'); + } + + const NESTED_IFRAME_INCLUSION = ` + <html> + <body> + <script type="application/javascript"> + window.addEventListener("message", receiveMessage); + function receiveMessage(event) { + window.removeEventListener("message", receiveMessage); + window.parent.postMessage({result: event.data.result}, '*'); + } + <\/script> + <iframe src="http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_iframe.sjs"></iframe> + </body> + </html>`; + + let NESTED_BLOB_IFRAME_INCLUSION = new Blob([NESTED_IFRAME_INCLUSION], {type:'text/html'}); + + // query the testframe and set blob URL + let testframe = document.getElementById("testframe"); + testframe.src = window.URL.createObjectURL(NESTED_BLOB_IFRAME_INCLUSION); +</script> +</body> +</html> diff --git a/dom/security/test/general/file_same_site_cookies_blob_iframe_navigation.html b/dom/security/test/general/file_same_site_cookies_blob_iframe_navigation.html new file mode 100644 index 0000000000..815c6a6bfc --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_blob_iframe_navigation.html @@ -0,0 +1,30 @@ +<html> +<body> +<iframe id="testframe"></iframe> +<script type="application/javascript"> + + // simply passing on the message from the child to parent + window.addEventListener("message", receiveMessage); + function receiveMessage(event) { + window.removeEventListener("message", receiveMessage); + window.parent.postMessage({result: event.data.result}, '*'); + } + + const NESTED_IFRAME_NAVIGATION = ` + <html> + <body> + <a id="testlink" href="http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_iframe.sjs"></a> + <script type="application/javascript"> + let link = document.getElementById("testlink"); + link.click(); + <\/script> + </body> + </html>`; + let NESTED_BLOB_IFRAME_NAVIGATION = new Blob([NESTED_IFRAME_NAVIGATION], {type:'text/html'}); + + // query the testframe and set blob URL + let testframe = document.getElementById("testframe"); + testframe.src = window.URL.createObjectURL(NESTED_BLOB_IFRAME_NAVIGATION); +</script> +</body> +</html> diff --git a/dom/security/test/general/file_same_site_cookies_bug1748693.sjs b/dom/security/test/general/file_same_site_cookies_bug1748693.sjs new file mode 100644 index 0000000000..6890bafa17 --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_bug1748693.sjs @@ -0,0 +1,31 @@ +const MESSAGE_PAGE = function (msg) { + return ` +<!DOCTYPE html> +<html> + <body> + <p id="msg">${msg}</p> + <body> +</html> +`; +}; + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-store"); + response.setHeader("Content-Type", "text/html"); + + if (request.queryString.includes("setcookies")) { + response.setHeader( + "Set-Cookie", + "auth_secure=foo; SameSite=None; HttpOnly; Secure", + true + ); + response.setHeader("Set-Cookie", "auth=foo; HttpOnly;", true); + response.write(MESSAGE_PAGE(request.queryString)); + return; + } + + const cookies = request.hasHeader("Cookie") + ? request.getHeader("Cookie") + : ""; + response.write(MESSAGE_PAGE(cookies)); +} diff --git a/dom/security/test/general/file_same_site_cookies_cross_origin_context.sjs b/dom/security/test/general/file_same_site_cookies_cross_origin_context.sjs new file mode 100644 index 0000000000..9103941653 --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_cross_origin_context.sjs @@ -0,0 +1,54 @@ +// Custom *.sjs file specifically for the needs of Bug 1452496 + +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +const FRAME = ` + <!DOCTYPE html> + <html> + <head> + <title>Bug 1452496 - Do not allow same-site cookies in cross site context</title> + </head> + <body> + <script type="application/javascript"> + let cookie = document.cookie; + // now reset the cookie for the next test + document.cookie = "myKey=;" + "expires=Thu, 01 Jan 1970 00:00:00 GMT"; + window.parent.postMessage({result: cookie}, 'http://mochi.test:8888'); + </script> + </body> + </html>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.queryString.includes("setSameSiteCookie")) { + response.setHeader( + "Set-Cookie", + "myKey=strictSameSiteCookie; samesite=strict", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + if (request.queryString.includes("setRegularCookie")) { + response.setHeader("Set-Cookie", "myKey=regularCookie;", true); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + if (request.queryString.includes("loadFrame")) { + response.write(FRAME); + return; + } + + // we should never get here, but just in case return something unexpected + response.write("D'oh"); +} diff --git a/dom/security/test/general/file_same_site_cookies_from_script.sjs b/dom/security/test/general/file_same_site_cookies_from_script.sjs new file mode 100644 index 0000000000..0df217cf45 --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_from_script.sjs @@ -0,0 +1,48 @@ +// Custom *.sjs file specifically for the needs of Bug 1452496 + +const SET_COOKIE_FRAME = ` + <!DOCTYPE html> + <html> + <head> + <title>Bug 1452496 - Do not allow same-site cookies in cross site context</title> + </head> + <body> + <script type="application/javascript"> + document.cookie = "myKey=sameSiteCookieInlineScript;SameSite=strict"; + </script> + </body> + </html>`; + +const GET_COOKIE_FRAME = ` + <!DOCTYPE html> + <html> + <head> + <title>Bug 1452496 - Do not allow same-site cookies in cross site context</title> + </head> + <body> + <script type="application/javascript"> + let cookie = document.cookie; + // now reset the cookie for the next test + document.cookie = "myKey=;" + "expires=Thu, 01 Jan 1970 00:00:00 GMT"; + window.parent.postMessage({result: cookie}, 'http://mochi.test:8888'); + </script> + </body> + </html>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.queryString.includes("setSameSiteCookieUsingInlineScript")) { + response.write(SET_COOKIE_FRAME); + return; + } + + if (request.queryString.includes("getCookieFrame")) { + response.write(GET_COOKIE_FRAME); + return; + } + + // we should never get here, but just in case return something unexpected + response.write("D'oh"); +} diff --git a/dom/security/test/general/file_same_site_cookies_iframe.sjs b/dom/security/test/general/file_same_site_cookies_iframe.sjs new file mode 100644 index 0000000000..7b511257c3 --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_iframe.sjs @@ -0,0 +1,99 @@ +// Custom *.sjs file specifically for the needs of Bug 1454027 + +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +const NESTED_IFRAME_NAVIGATION = ` + <html> + <body> + <a id="testlink" href="http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_iframe.sjs"></a> + <script type="application/javascript"> + let link = document.getElementById("testlink"); + link.click(); + <\/script> + </body> + </html>`; + +const NESTED_IFRAME_INCLUSION = ` + <html> + <body> + <script type="application/javascript"> + // simply passing on the message from the child to parent + window.addEventListener("message", receiveMessage); + function receiveMessage(event) { + window.removeEventListener("message", receiveMessage); + window.parent.postMessage({result: event.data.result}, '*'); + } + <\/script> + <iframe src="http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_iframe.sjs"></iframe> + </body> + </html>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + // using startsWith and discard the math random + if (request.queryString.startsWith("setSameSiteCookie")) { + response.setHeader( + "Set-Cookie", + "myKey=mySameSiteIframeTestCookie; samesite=strict", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + // navigation tests + if (request.queryString === "nestedIframeNavigation") { + response.write(NESTED_IFRAME_NAVIGATION); + return; + } + + if (request.queryString === "nestedSandboxIframeNavigation") { + response.setHeader( + "Content-Security-Policy", + "sandbox allow-scripts", + false + ); + response.write(NESTED_IFRAME_NAVIGATION); + return; + } + + // inclusion tests + if (request.queryString === "nestedIframeInclusion") { + response.write(NESTED_IFRAME_INCLUSION); + return; + } + + if (request.queryString === "nestedSandboxIframeInclusion") { + response.setHeader( + "Content-Security-Policy", + "sandbox allow-scripts", + false + ); + response.write(NESTED_IFRAME_INCLUSION); + return; + } + + const cookies = request.hasHeader("Cookie") + ? request.getHeader("Cookie") + : ""; + response.write(` + <!DOCTYPE html> + <html> + <head> + <title>Bug 1454027 - Update SameSite cookie handling inside iframes</title> + </head> + <body> + <script type="application/javascript"> + window.parent.postMessage({result: "${cookies}" }, '*'); + </script> + </body> + </html> + `); +} diff --git a/dom/security/test/general/file_same_site_cookies_redirect.sjs b/dom/security/test/general/file_same_site_cookies_redirect.sjs new file mode 100644 index 0000000000..f7451fb504 --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_redirect.sjs @@ -0,0 +1,103 @@ +// Custom *.sjs file specifically for the needs of Bug 1453814 + +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +const FRAME = ` + <!DOCTYPE html> + <html> + <head> + <title>Bug 1453814 - Do not allow same-site cookies for cross origin redirect</title> + </head> + <body> + <script type="application/javascript"> + let cookie = document.cookie; + // now reset the cookie for the next test + document.cookie = "myKey=;" + "expires=Thu, 01 Jan 1970 00:00:00 GMT"; + window.parent.postMessage({result: cookie}, 'http://mochi.test:8888'); + </script> + </body> + </html>`; + +const SAME_ORIGIN = "http://mochi.test:8888/"; +const CROSS_ORIGIN = "http://example.com/"; +const PATH = + "tests/dom/security/test/general/file_same_site_cookies_redirect.sjs"; + +const FRAME_META_REFRESH_SAME = + ` + <html><head> + <meta http-equiv="refresh" content="0; + url='` + + SAME_ORIGIN + + PATH + + `?loadFrame'"> + </head></html>`; + +const FRAME_META_REFRESH_CROSS = + ` + <html><head> + <meta http-equiv="refresh" content="0; + url='` + + CROSS_ORIGIN + + PATH + + `?loadFrame'"> + </head></html>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.queryString === "setSameSiteCookie") { + response.setHeader( + "Set-Cookie", + "myKey=strictSameSiteCookie; samesite=strict", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + if (request.queryString === "sameToSameRedirect") { + let URL = SAME_ORIGIN + PATH + "?loadFrame"; + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", URL, false); + return; + } + + if (request.queryString === "sameToCrossRedirect") { + let URL = CROSS_ORIGIN + PATH + "?loadFrame"; + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", URL, false); + return; + } + + if (request.queryString === "crossToSameRedirect") { + let URL = SAME_ORIGIN + PATH + "?loadFrame"; + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", URL, false); + return; + } + + if (request.queryString === "sameToCrossRedirectMeta") { + response.write(FRAME_META_REFRESH_CROSS); + return; + } + + if (request.queryString === "crossToSameRedirectMeta") { + response.write(FRAME_META_REFRESH_SAME); + return; + } + + if (request.queryString === "loadFrame") { + response.write(FRAME); + return; + } + + // we should never get here, but just in case return something unexpected + response.write("D'oh"); +} diff --git a/dom/security/test/general/file_same_site_cookies_subrequest.sjs b/dom/security/test/general/file_same_site_cookies_subrequest.sjs new file mode 100644 index 0000000000..fdc81344ef --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_subrequest.sjs @@ -0,0 +1,82 @@ +// Custom *.sjs file specifically for the needs of Bug 1286861 + +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +const FRAME = ` + <!DOCTYPE html> + <html> + <head> + <title>Bug 1286861 - Add support for same site cookies</title> + </head> + <body> + <img src = "http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_subrequest.sjs?checkCookie"> + </body> + </html>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.queryString.includes("setStrictSameSiteCookie")) { + response.setHeader( + "Set-Cookie", + "myKey=strictSameSiteCookie; samesite=strict", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + if (request.queryString.includes("setLaxSameSiteCookie")) { + response.setHeader( + "Set-Cookie", + "myKey=laxSameSiteCookie; samesite=lax", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + // save the object state of the initial request, which returns + // async once the server has processed the img request. + if (request.queryString.includes("queryresult")) { + response.processAsync(); + setObjectState("queryResult", response); + return; + } + + if (request.queryString.includes("loadFrame")) { + response.write(FRAME); + return; + } + + if (request.queryString.includes("checkCookie")) { + var cookie = "unitialized"; + if (request.hasHeader("Cookie")) { + cookie = request.getHeader("Cookie"); + } else { + cookie = "myKey=noCookie"; + } + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + + // return the result + getObjectState("queryResult", function (queryResponse) { + if (!queryResponse) { + return; + } + queryResponse.write(cookie); + queryResponse.finish(); + }); + return; + } + + // we should never get here, but just in case return something unexpected + response.write("D'oh"); +} diff --git a/dom/security/test/general/file_same_site_cookies_toplevel_nav.sjs b/dom/security/test/general/file_same_site_cookies_toplevel_nav.sjs new file mode 100644 index 0000000000..45b515a28b --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_toplevel_nav.sjs @@ -0,0 +1,96 @@ +// Custom *.sjs file specifically for the needs of Bug 1286861 + +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +const FRAME = ` + <!DOCTYPE html> + <html> + <head> + <title>Bug 1286861 - Add support for same site cookies</title> + </head> + <body> + <script type="application/javascript"> + let myWin = window.open("http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_toplevel_nav.sjs?loadWin"); + </script> + </body> + </html>`; + +const WIN = ` + <!DOCTYPE html> + <html> + <body> + just a dummy window + <script> + window.addEventListener("load",()=>{ + window.close(); + }); + </script> + </body> + </html>`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.queryString.includes("setStrictSameSiteCookie")) { + response.setHeader( + "Set-Cookie", + "myKey=strictSameSiteCookie; samesite=strict", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + if (request.queryString.includes("setLaxSameSiteCookie")) { + response.setHeader( + "Set-Cookie", + "myKey=laxSameSiteCookie; samesite=lax", + true + ); + response.setHeader("Content-Type", "image/png"); + response.write(IMG_BYTES); + return; + } + + // save the object state of the initial request, which returns + // async once the server has processed the img request. + if (request.queryString.includes("queryresult")) { + response.processAsync(); + setObjectState("queryResult", response); + return; + } + + if (request.queryString.includes("loadFrame")) { + response.write(FRAME); + return; + } + + if (request.queryString.includes("loadWin")) { + var cookie = "unitialized"; + if (request.hasHeader("Cookie")) { + cookie = request.getHeader("Cookie"); + } else { + cookie = "myKey=noCookie"; + } + response.write(WIN); + + // return the result + getObjectState("queryResult", function (queryResponse) { + if (!queryResponse) { + return; + } + queryResponse.write(cookie); + queryResponse.finish(); + }); + return; + } + + // we should never get here, but just in case return something unexpected + response.write("D'oh"); +} diff --git a/dom/security/test/general/file_same_site_cookies_toplevel_set_cookie.sjs b/dom/security/test/general/file_same_site_cookies_toplevel_set_cookie.sjs new file mode 100644 index 0000000000..34dfe40e23 --- /dev/null +++ b/dom/security/test/general/file_same_site_cookies_toplevel_set_cookie.sjs @@ -0,0 +1,68 @@ +// Custom *.sjs file specifically for the needs of Bug 1454242 + +const WIN = ` + <html> + <body> + <script type="application/javascript"> + let newWin = window.open("http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_toplevel_set_cookie.sjs?loadWinAndSetCookie"); + newWin.onload = function() { + newWin.close(); + } + </script> + </body> + </html>`; + +const DUMMY_WIN = ` + <html> + <body> + just a dummy window that sets a same-site=lax cookie + <script type="application/javascript"> + window.opener.opener.postMessage({value: 'testSetupComplete'}, '*'); + </script> + </body> + </html>`; + +const FRAME = ` + <html> + <body> + <script type="application/javascript"> + let cookie = document.cookie; + // now reset the cookie for the next test + document.cookie = "myKey=;" + "expires=Thu, 01 Jan 1970 00:00:00 GMT"; + window.parent.postMessage({value: cookie}, 'http://mochi.test:8888'); + </script> + </body> + </html>`; + +const SAME_ORIGIN = "http://mochi.test:8888/"; +const CROSS_ORIGIN = "http://example.com/"; +const PATH = + "tests/dom/security/test/general/file_same_site_cookies_redirect.sjs"; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.queryString === "loadWin") { + response.write(WIN); + return; + } + + if (request.queryString === "loadWinAndSetCookie") { + response.setHeader( + "Set-Cookie", + "myKey=laxSameSiteCookie; samesite=lax", + true + ); + response.write(DUMMY_WIN); + return; + } + + if (request.queryString === "checkCookie") { + response.write(FRAME); + return; + } + + // we should never get here, but just in case return something unexpected + response.write("D'oh"); +} diff --git a/dom/security/test/general/file_script.js b/dom/security/test/general/file_script.js new file mode 100644 index 0000000000..c339e45d5d --- /dev/null +++ b/dom/security/test/general/file_script.js @@ -0,0 +1 @@ +window.counter++; diff --git a/dom/security/test/general/file_toplevel_data_meta_redirect.html b/dom/security/test/general/file_toplevel_data_meta_redirect.html new file mode 100644 index 0000000000..e94a61ed48 --- /dev/null +++ b/dom/security/test/general/file_toplevel_data_meta_redirect.html @@ -0,0 +1,10 @@ +<html> +<body> +<head> + <meta http-equiv="refresh" + content="0; url='data:text/html,<body>toplevel meta redirect to data: URI should be blocked</body>'"> +</head> +<body> +Meta Redirect to data: URI +</body> +</html> diff --git a/dom/security/test/general/file_toplevel_data_navigations.sjs b/dom/security/test/general/file_toplevel_data_navigations.sjs new file mode 100644 index 0000000000..57c4b527dd --- /dev/null +++ b/dom/security/test/general/file_toplevel_data_navigations.sjs @@ -0,0 +1,13 @@ +// Custom *.sjs file specifically for the needs of Bug: +// Bug 1394554 - Block toplevel data: URI navigations after redirect + +var DATA_URI = + "data:text/html,<body>toplevel data: URI navigations after redirect should be blocked</body>"; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", DATA_URI, false); +} diff --git a/dom/security/test/general/file_view_bg_image_data_navigation.html b/dom/security/test/general/file_view_bg_image_data_navigation.html new file mode 100644 index 0000000000..d9aa6ca8b6 --- /dev/null +++ b/dom/security/test/general/file_view_bg_image_data_navigation.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1658244: Test navigation for right-click view-bg-image on data:image/svg</title> +<style> +body { + background: fixed #222 url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjUwMCIgd2lkdGg9IjUwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGZpbHRlciBpZD0iYSI+PGZlVHVyYnVsZW5jZSBiYXNlRnJlcXVlbmN5PSIuOSIgbnVtT2N0YXZlcz0iMTAiIHN0aXRjaFRpbGVzPSJzdGl0Y2giIHR5cGU9ImZyYWN0YWxOb2lzZSIvPjwvZmlsdGVyPjxwYXRoIGQ9Im0wIDBoNTAwdjUwMGgtNTAweiIgZmlsbD0iIzExMSIvPjxwYXRoIGQ9Im0wIDBoNTAwdjUwMGgtNTAweiIgZmlsdGVyPSJ1cmwoI2EpIiBvcGFjaXR5PSIuMiIvPjwvc3ZnPgo="); + color: #ccc; +} +</style> +</head> +<body id="testbody"> + This page has an inline SVG image as a background. +</body> +</html> diff --git a/dom/security/test/general/file_view_image_data_navigation.html b/dom/security/test/general/file_view_image_data_navigation.html new file mode 100644 index 0000000000..a3f9acfb4d --- /dev/null +++ b/dom/security/test/general/file_view_image_data_navigation.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1407891: Test navigation for right-click view-image on data:image/svg</title> +</head> +<body> + +<img id="testimage" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCwxMkwzLDcsNCw2bDQsNCw0LTQsMSwxWiIgZmlsbD0iIzZBNkE2QSIgLz4KPC9zdmc+Cg=="></img> + +</body> +</html> diff --git a/dom/security/test/general/file_xfo_error_page.sjs b/dom/security/test/general/file_xfo_error_page.sjs new file mode 100644 index 0000000000..b1fa33cbd4 --- /dev/null +++ b/dom/security/test/general/file_xfo_error_page.sjs @@ -0,0 +1,8 @@ +"use strict"; + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + response.setHeader("x-frame-options", "deny", false); + response.write("<html>xfo test loaded</html>"); +} diff --git a/dom/security/test/general/mochitest.toml b/dom/security/test/general/mochitest.toml new file mode 100644 index 0000000000..c46b5ecf57 --- /dev/null +++ b/dom/security/test/general/mochitest.toml @@ -0,0 +1,148 @@ +[DEFAULT] +support-files = [ + "file_contentpolicytype_targeted_link_iframe.sjs", + "file_nosniff_testserver.sjs", + "file_nosniff_navigation.sjs", + "file_block_script_wrong_mime_server.sjs", + "file_block_toplevel_data_navigation.html", + "file_block_toplevel_data_navigation2.html", + "file_block_toplevel_data_navigation3.html", + "file_block_toplevel_data_redirect.sjs", + "file_block_subresource_redir_to_data.sjs", + "file_same_site_cookies_subrequest.sjs", + "file_same_site_cookies_toplevel_nav.sjs", + "file_same_site_cookies_cross_origin_context.sjs", + "file_same_site_cookies_from_script.sjs", + "file_same_site_cookies_redirect.sjs", + "file_same_site_cookies_toplevel_set_cookie.sjs", + "file_same_site_cookies_blob_iframe_navigation.html", + "file_same_site_cookies_blob_iframe_inclusion.html", + "file_same_site_cookies_iframe.sjs", + "file_same_site_cookies_about.sjs", + "file_cache_splitting_server.sjs", + "file_cache_splitting_isloaded.sjs", + "file_cache_splitting_window.html", + "window_nosniff_navigation.html", +] + +["test_allow_opening_data_json.html"] + +["test_allow_opening_data_pdf.html"] +skip-if = ["os == 'android'"] # no pdf reader on Android + +["test_assert_about_page_no_csp.html"] +skip-if = ["!debug"] + +["test_block_script_wrong_mime.html"] + +["test_block_subresource_redir_to_data.html"] + +["test_block_toplevel_data_img_navigation.html"] + +["test_block_toplevel_data_navigation.html"] + +["test_bug1450853.html"] +skip-if = [ + "http3", + "http2", +] + +["test_bug1660452_http.html"] +skip-if = [ + "http3", + "http2", +] + +["test_bug1660452_https.html"] +scheme = "https" + +["test_cache_split.html"] +skip-if = [ + "http3", + "http2", +] + +["test_contentpolicytype_targeted_link_iframe.html"] +skip-if = [ + "http3", + "http2", +] + +["test_gpc.html"] +support-files = ["file_gpc_server.sjs"] + +["test_meta_referrer.html"] +support-files = [ + "file_meta_referrer_in_head.html", + "file_meta_referrer_notin_head.html", +] + +["test_nosniff.html"] + +["test_nosniff_navigation.html"] + +["test_same_site_cookies_about.html"] +fail-if = ["xorigin"] +skip-if = [ + "http3", + "http2", +] + +["test_same_site_cookies_cross_origin_context.html"] +skip-if = [ + "http3", + "http2", +] + +["test_same_site_cookies_from_script.html"] +fail-if = ["xorigin"] +skip-if = [ + "http3", + "http2", +] + +["test_same_site_cookies_iframe.html"] +fail-if = ["xorigin"] +skip-if = [ + "http3", + "http2", +] + +["test_same_site_cookies_laxByDefault.html"] +skip-if = ["debug"] +support-files = ["closeWindow.sjs"] + +["test_same_site_cookies_redirect.html"] +fail-if = ["xorigin"] +skip-if = [ + "http3", + "http2", +] + +["test_same_site_cookies_subrequest.html"] +fail-if = ["xorigin"] # Cookies set incorrectly +skip-if = [ + "http3", + "http2", +] + +["test_same_site_cookies_toplevel_nav.html"] +fail-if = ["xorigin"] +skip-if = [ + "http3", + "http2", +] + +["test_same_site_cookies_toplevel_set_cookie.html"] +fail-if = ["xorigin"] # Cookies not set +skip-if = [ + "http3", + "http2", +] + +["test_xfo_error_page.html"] +support-files = ["file_xfo_error_page.sjs"] +skip-if = [ + "http3", + "http2", +] diff --git a/dom/security/test/general/test_allow_opening_data_json.html b/dom/security/test/general/test_allow_opening_data_json.html new file mode 100644 index 0000000000..4b37931e1f --- /dev/null +++ b/dom/security/test/general/test_allow_opening_data_json.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1403814: Allow toplevel data URI navigation data:application/json</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function test_toplevel_data_json() { + const DATA_JSON = "data:application/json,{'my_json_key':'my_json_value'}"; + + let win = window.open(DATA_JSON); + let wrappedWin = SpecialPowers.wrap(win); + + // Unfortunately we can't detect whether the JSON has loaded or not using some + // event, hence we are constantly polling location.href till we see that + // the data: URI appears. Test times out on failure. + var jsonLoaded = setInterval(function() { + if (wrappedWin.document.location.href.startsWith("data:application/json")) { + clearInterval(jsonLoaded); + ok(true, "navigating to data:application/json allowed"); + wrappedWin.close(); + SimpleTest.finish(); + } + }, 200); +} + +SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]] +}, test_toplevel_data_json); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_allow_opening_data_pdf.html b/dom/security/test/general/test_allow_opening_data_pdf.html new file mode 100644 index 0000000000..007b3e8801 --- /dev/null +++ b/dom/security/test/general/test_allow_opening_data_pdf.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1398692: Allow toplevel navigation to a data:application/pdf</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function test_toplevel_data_pdf() { + // The PDF contains one page and it is a 3/72" square, the minimum allowed by the spec + const DATA_PDF = + "data:application/pdf;base64,JVBERi0xLjANCjEgMCBvYmo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PmVuZG9iaiAyIDAgb2JqPDwvVHlwZS9QYWdlcy9LaWRzWzMgMCBSXS9Db3VudCAxPj5lbmRvYmogMyAwIG9iajw8L1R5cGUvUGFnZS9NZWRpYUJveFswIDAgMyAzXT4+ZW5kb2JqDQp4cmVmDQowIDQNCjAwMDAwMDAwMDAgNjU1MzUgZg0KMDAwMDAwMDAxMCAwMDAwMCBuDQowMDAwMDAwMDUzIDAwMDAwIG4NCjAwMDAwMDAxMDIgMDAwMDAgbg0KdHJhaWxlcjw8L1NpemUgNC9Sb290IDEgMCBSPj4NCnN0YXJ0eHJlZg0KMTQ5DQolRU9G"; + + let win = window.open(DATA_PDF); + let wrappedWin = SpecialPowers.wrap(win); + + // Unfortunately we can't detect whether the PDF has loaded or not using some + // event, hence we are constantly polling location.href till we see that + // the data: URI appears. Test times out on failure. + var pdfLoaded = setInterval(function() { + if (wrappedWin.document.location.href.startsWith("data:application/pdf")) { + clearInterval(pdfLoaded); + ok(true, "navigating to data:application/pdf allowed"); + wrappedWin.close(); + SimpleTest.finish(); + } + }, 200); +} + +SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]] +}, test_toplevel_data_pdf); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_assert_about_page_no_csp.html b/dom/security/test/general/test_assert_about_page_no_csp.html new file mode 100644 index 0000000000..06be4ce460 --- /dev/null +++ b/dom/security/test/general/test_assert_about_page_no_csp.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1490977: Test Assertion if content privileged about: page has no CSP</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe id="testframe"></iframe> +<script class="testbody" type="text/javascript"> + + // Test Setup: The test overrules the allowlist of about: pages that are allowed to load + // without a CSP and makes sure to hit the assertion within AssertAboutPageHasCSP(). + + SpecialPowers.setBoolPref("dom.security.skip_about_page_csp_allowlist_and_assert", true); + + SimpleTest.waitForExplicitFinish(); + SimpleTest.expectAssertions(0, 1); + + ok(true, "sanity: prefs flipped and test runs"); + let myFrame = document.getElementById("testframe"); + myFrame.src = "about:blank"; + // booom :-) + + SpecialPowers.setBoolPref("dom.security.skip_about_page_csp_allowlist_and_assert", false); + SimpleTest.finish(); +</script> +</pre> +</body> +</html> diff --git a/dom/security/test/general/test_block_script_wrong_mime.html b/dom/security/test/general/test_block_script_wrong_mime.html new file mode 100644 index 0000000000..93a4b9d220 --- /dev/null +++ b/dom/security/test/general/test_block_script_wrong_mime.html @@ -0,0 +1,92 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1288361 - Block scripts with incorrect MIME type</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<script class="testbody" type="text/javascript"> + +const MIMETypes = [ + ["application/javascript", true], + ["text/javascript", true], + + ["audio/mpeg", false], + ["audio/", false], + ["image/jpeg", false], + ["image/", false], + ["video/mpeg", false], + ["video/", false], + ["text/csv", false], +]; + +// <script src=""> +function testScript([mime, shouldLoad]) { + return new Promise((resolve, reject) => { + let script = document.createElement("script"); + script.onload = () => { + document.body.removeChild(script); + ok(shouldLoad, `script with mime '${mime}' should load`); + resolve(); + }; + script.onerror = () => { + document.body.removeChild(script); + ok(!shouldLoad, `script with wrong mime '${mime}' should be blocked`); + resolve(); + }; + script.src = "file_block_script_wrong_mime_server.sjs?type=script&mime="+mime; + document.body.appendChild(script); + }); +} + +// new Worker() +function testWorker([mime, shouldLoad]) { + return new Promise((resolve, reject) => { + let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker&mime="+mime); + worker.onmessage = (event) => { + ok(shouldLoad, `worker with mime '${mime}' should load`) + is(event.data, "worker-loaded", "worker should send correct message"); + resolve(); + }; + worker.onerror = (error) => { + ok(!shouldLoad, `worker with wrong mime '${mime}' should be blocked`); + error.preventDefault(); + resolve(); + } + worker.postMessage("dummy"); + }); +} + +// new Worker() with importScripts() +function testWorkerImportScripts([mime, shouldLoad]) { + return new Promise((resolve, reject) => { + let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker-import&mime="+mime); + worker.onmessage = (event) => { + ok(shouldLoad, `worker/importScripts with mime '${mime}' should load`) + is(event.data, "worker-loaded", "worker should send correct message"); + resolve(); + }; + worker.onerror = (error) => { + ok(!shouldLoad, `worker/importScripts with wrong mime '${mime}' should be blocked`); + error.preventDefault(); + resolve(); + } + worker.postMessage("dummy"); + }); +} + +SimpleTest.waitForExplicitFinish(); +Promise.all(MIMETypes.map(testScript)).then(() => { + return Promise.all(MIMETypes.map(testWorker)); +}).then(() => { + return Promise.all(MIMETypes.map(testWorkerImportScripts)); +}).then(() => { + return SpecialPowers.popPrefEnv(); +}).then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_block_subresource_redir_to_data.html b/dom/security/test/general/test_block_subresource_redir_to_data.html new file mode 100644 index 0000000000..21a85515ec --- /dev/null +++ b/dom/security/test/general/test_block_subresource_redir_to_data.html @@ -0,0 +1,66 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1428793: Block insecure redirects to data: URIs</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<script id="testScriptRedirectToData"></script> +<script id="testModuleScriptRedirectToData" type="module"></script> + +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +const NUM_TESTS = 3; + +var testCounter = 0; +function checkFinish() { + testCounter++; + if (testCounter === NUM_TESTS) { + SimpleTest.finish(); + } +} + +// --- test regular scripts +let testScriptRedirectToData = document.getElementById("testScriptRedirectToData"); +testScriptRedirectToData.onerror = function() { + ok(true, "script that redirects to data: URI should not load"); + checkFinish(); +} +testScriptRedirectToData.onload = function() { + ok(false, "script that redirects to data: URI should not load"); + checkFinish(); +} +testScriptRedirectToData.src = "file_block_subresource_redir_to_data.sjs?script"; + +// --- test workers +let worker = new Worker("file_block_subresource_redir_to_data.sjs?worker"); +worker.onerror = function() { + // please note that workers need to be same origin, hence the data: URI + // redirect is blocked by worker code and not the content security manager! + ok(true, "worker script that redirects to data: URI should not load"); + checkFinish(); +} +worker.onmessage = function() { + ok(false, "worker script that redirects to data: URI should not load"); + checkFinish(); +}; +worker.postMessage("dummy"); + +// --- test script modules + let testModuleScriptRedirectToData = document.getElementById("testModuleScriptRedirectToData"); + testModuleScriptRedirectToData.onerror = function() { + ok(true, "module script that redirects to data: URI should not load"); + checkFinish(); + } + testModuleScriptRedirectToData.onload = function() { + ok(false, "module script that redirects to data: URI should not load"); + checkFinish(); + } + testModuleScriptRedirectToData.src = "file_block_subresource_redir_to_data.sjs?modulescript"; + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_block_toplevel_data_img_navigation.html b/dom/security/test/general/test_block_toplevel_data_img_navigation.html new file mode 100644 index 0000000000..07e46b1f2f --- /dev/null +++ b/dom/security/test/general/test_block_toplevel_data_img_navigation.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1396798: Do not block toplevel data: navigation to image (except svgs)</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> +SpecialPowers.setBoolPref("security.data_uri.block_toplevel_data_uri_navigations", true); +SimpleTest.registerCleanupFunction(() => { + SpecialPowers.clearUserPref("security.data_uri.block_toplevel_data_uri_navigations"); +}); + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("have to test that top level data:image loading is blocked/allowed"); + +function test_toplevel_data_image() { + const DATA_PNG = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; + let win1 = window.open(DATA_PNG); + let wrappedWin1 = SpecialPowers.wrap(win1); + setTimeout(function () { + let images = wrappedWin1.document.getElementsByTagName('img'); + is(images.length, 1, "Loading data:image/png should be allowed"); + is(images[0].src, DATA_PNG, "Sanity: img src matches"); + wrappedWin1.close(); + test_toplevel_data_image_svg(); + }, 1000); +} + +function test_toplevel_data_image_svg() { + const DATA_SVG = + "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCwxMkwzLDcsNCw2bDQsNCw0LTQsMSwxWiIgZmlsbD0iIzZBNkE2QSIgLz4KPC9zdmc+Cg=="; + let win2 = window.open(DATA_SVG); + // Unfortunately we can't detect whether the window was closed using some event, + // hence we are constantly polling till we see that win == null. + // Test times out on failure. + var win2Closed = setInterval(function() { + if (win2 == null || win2.closed) { + clearInterval(win2Closed); + ok(true, "Loading data:image/svg+xml should be blocked"); + SimpleTest.finish(); + } + }, 200); +} +// fire up the tests +test_toplevel_data_image(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_block_toplevel_data_navigation.html b/dom/security/test/general/test_block_toplevel_data_navigation.html new file mode 100644 index 0000000000..bbadacb218 --- /dev/null +++ b/dom/security/test/general/test_block_toplevel_data_navigation.html @@ -0,0 +1,134 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1331351 - Block top level window data: URI navigations</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +async function expectBlockedToplevelData() { + await SpecialPowers.spawnChrome([], async () => { + let progressListener; + let bid = await new Promise(resolve => { + let bcs = []; + progressListener = { + QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]), + onStateChange(webProgress, request, stateFlags, status) { + if (!(request instanceof Ci.nsIChannel) || !webProgress.isTopLevel || + !(stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) || + !(stateFlags & Ci.nsIWebProgressListener.STATE_STOP)) { + return; + } + + if (!["NS_ERROR_DOM_BAD_URI", "NS_ERROR_CORRUPTED_CONTENT"].includes(ChromeUtils.getXPCOMErrorName(status))) { + info(ChromeUtils.getXPCOMErrorName(status)); + isnot(request.URI.scheme, "data"); + return; + } + + // We can't check for the scheme to be "data" because in the case of a + // redirected load, we'll get a `NS_ERROR_DOM_BAD_URI` load error + // before observing the redirect, cancelling the load. Instead we just + // wait for any load to error with `NS_ERROR_DOM_BAD_URI`. + for (let bc of bcs) { + try { + bc.webProgress.removeProgressListener(progressListener); + } catch(e) { } + } + bcs = []; + Services.obs.removeObserver(observer, "browsing-context-attached"); + resolve(webProgress.browsingContext.browserId); + } + }; + + function observer(subject, topic) { + if (!bcs.includes(subject.webProgress)) { + bcs.push(subject.webProgress); + subject.webProgress.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_ALL); + } + } + Services.obs.addObserver(observer, "browsing-context-attached"); + }); + return bid; + }); +} + +async function expectBlockedURIWarning() { + await SpecialPowers.spawnChrome([], async () => { + return new Promise(resolve => { + Services.console.registerListener(function onConsoleMessage(msg) { + info("Seeing console message: " + msg.message); + if (!(msg instanceof Ci.nsIScriptError)) { + return; + } + if (msg.category != "DATA_URI_BLOCKED") { + return; + } + + Services.console.unregisterListener(onConsoleMessage); + resolve(); + }); + }); + }); +} + +async function expectBrowserDiscarded(browserId) { + await SpecialPowers.spawnChrome([browserId], async (browserId) => { + return new Promise(resolve => { + function check() { + if (!BrowsingContext.getCurrentTopByBrowserId(browserId)) { + ok(true, `BrowserID ${browserId} discarded`); + resolve(); + Services.obs.removeObserver(check, "browsing-context-discarded"); + } + } + Services.obs.addObserver(check, "browsing-context-discarded"); + check(); + }); + }); +} + +async function popupTest(uri, expectClose) { + info(`Running expect blocked test for ${uri}`); + let reqBlockedPromise = expectBlockedToplevelData(); + let warningPromise = expectBlockedURIWarning(); + let win = window.open(uri); + let browserId = await reqBlockedPromise; + await warningPromise; + if (expectClose) { + await expectBrowserDiscarded(browserId); + } + win.close(); +} + +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + + // simple data: URI click navigation should be prevented + await popupTest("file_block_toplevel_data_navigation.html", false); + + // data: URI in iframe which opens data: URI in _blank should be blocked + await popupTest("file_block_toplevel_data_navigation2.html", false); + + // navigating to a data: URI using window.location.href should be blocked + await popupTest("file_block_toplevel_data_navigation3.html", false); + + // navigating to a data: URI using window.open() should be blocked + await popupTest("data:text/html,<body>toplevel data: URI navigations should be blocked</body>", false); + + // navigating to a URI which redirects to a data: URI using window.open() should be blocked + await popupTest("file_block_toplevel_data_redirect.sjs", false); + + // navigating to a data: URI without a Content Type should be blocked + await popupTest("data:,DataURIsWithNoContentTypeShouldBeBlocked", false); +}); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_bug1277803.xhtml b/dom/security/test/general/test_bug1277803.xhtml new file mode 100644 index 0000000000..30cc82310b --- /dev/null +++ b/dom/security/test/general/test_bug1277803.xhtml @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<window title="Bug 1277803 test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="runTest();"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <body xmlns="http://www.w3.org/1999/xhtml"> + </body> + + <script type="application/javascript"><![CDATA[ + SimpleTest.requestCompleteLog(); + + const BASE_URI = "http://mochi.test:8888/chrome/dom/security/test/general/"; + const FAVICON_URI = BASE_URI + "favicon_bug1277803.ico"; + const LOADING_URI = BASE_URI + "bug1277803.html"; + let testWindow; //will be used to trigger favicon load + + let expectedPrincipal = Services.scriptSecurityManager + .createContentPrincipal(Services.io.newURI(LOADING_URI), {}); + let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); + + function runTest() { + // Register our observer to intercept favicon requests. + function observer(aSubject, aTopic, aData) { + // Make sure this is a favicon request. + let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel); + if (FAVICON_URI != httpChannel.URI.spec) { + return; + } + + // Ensure the topic is the one we set an observer for. + is(aTopic, "http-on-modify-request", "Expected observer topic"); + + // Check for the correct loadingPrincipal, triggeringPrincipal. + let triggeringPrincipal = httpChannel.loadInfo.triggeringPrincipal; + let loadingPrincipal = httpChannel.loadInfo.loadingPrincipal; + + ok(loadingPrincipal.equals(expectedPrincipal), "Should be loading with the expected principal."); + ok(triggeringPrincipal.equals(expectedPrincipal), "Should be triggered with the expected principal."); + + Services.obs.removeObserver(this, "http-on-modify-request"); + SimpleTest.finish(); + } + Services.obs.addObserver(observer, "http-on-modify-request"); + + // Now that the observer is set up, trigger a favicon load with navigation + testWindow = window.open(LOADING_URI); + } + + SimpleTest.waitForExplicitFinish(); + SimpleTest.registerCleanupFunction(function() { + if (testWindow) { + testWindow.close(); + } + }); + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" src="about:blank"/> +</window> diff --git a/dom/security/test/general/test_bug1450853.html b/dom/security/test/general/test_bug1450853.html new file mode 100644 index 0000000000..e6b61ecce0 --- /dev/null +++ b/dom/security/test/general/test_bug1450853.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1450853 +--> +<head> +<meta charset="utf-8"> +<title>Test for Cross-origin resouce status leak via MediaError</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="/tests/SimpleTest/ChromeTask.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + +<audio autoplay id="audio"></audio> + +<script type="application/javascript"> + +/** Test for Bug 1450853 **/ +var CONST_GENERIC_ERROR_MESSAGE = "Failed to open media"; + +add_task(function() { + return new Promise((resolve) => { + let audioElement = document.getElementById("audio"); + + audioElement.onerror = function() { + let err = this.error; + let message = err.message; + + info(`Got Audio Error -> ${message}`); + ok(message.includes("404"), "Same-Origin Error Message should contain status data"); + resolve(); + }; + audioElement.src = "http://mochi.test:8888/media/test.mp3"; + }); +}); + +add_task(function() { + return new Promise((resolve) => { + let audioElement = document.getElementById("audio"); + + audioElement.onerror = function() { + let err = this.error; + let message = err.message; + + info(`Got Audio Error -> ${message}`); + is(message, CONST_GENERIC_ERROR_MESSAGE, "Cross-Origin Same-Site Error Message should be generic"); + resolve(); + }; + audioElement.src = "http://mochi.test:9999/media/test.mp3"; + }); +}); + +add_task(function() { + return new Promise((resolve) => { + let audioElement = document.getElementById("audio"); + + audioElement.onerror = function() { + let err = this.error; + let message = err.message; + + info(`Got Audio Error -> ${message}`); + is(message, CONST_GENERIC_ERROR_MESSAGE, "Cross-Origin Same-Site Error Message should be generic"); + resolve(); + }; + audioElement.src = "http://sub.mochi.test:8888/media/test.mp3"; + }); +}); + +add_task(function() { + return new Promise((resolve) => { + let audioElement = document.getElementById("audio"); + + audioElement.onerror = function() { + let err = this.error; + let message = err.message; + + info(`Got Audio Error -> ${message}`); + is(message, CONST_GENERIC_ERROR_MESSAGE, "Cross-Origin Error Message should be generic"); + resolve(); + }; + audioElement.src = "https://example.com/media/test.mp3"; + }); +}); + +</script> +</head> + +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1450853">Mozilla Bug 1450853</a> + <iframe width="0" height="0"></iframe> + </body> +</html> diff --git a/dom/security/test/general/test_bug1660452_http.html b/dom/security/test/general/test_bug1660452_http.html new file mode 100644 index 0000000000..3a6512da21 --- /dev/null +++ b/dom/security/test/general/test_bug1660452_http.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Bug 1660452: NullPrincipals need to know whether they were spun off of a Secure Context</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +ok(!window.isSecureContext, "top level should not be a secure context"); + +// eslint-disable-next-line +let newWin = window.open("data:text/html,<script><"+"/script>"); +ok(!newWin.isSecureContext, "data uri window should not be a secure context"); +newWin.close(); + +window.addEventListener("message", (event) => { + ok(!event.data.isSecureContext, "data uri frames should not be a secure context"); + if(event.data.finish) { + SimpleTest.finish(); + return; + } + let f2 = document.createElement("iframe"); + // eslint-disable-next-line + f2.src = "data:text/html,<iframe src=\"data:text/html,<script>parent.parent.postMessage({isSecureContext: window.isSecureContext, finish: true}, '*');<"+"/script>\"></iframe>"; + document.body.appendChild(f2); +}); + +let f = document.createElement("iframe"); +// eslint-disable-next-line +f.src = "data:text/html,<script>parent.postMessage({isSecureContext: window.isSecureContext}, '*');<"+"/script>"; +document.body.appendChild(f); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_bug1660452_https.html b/dom/security/test/general/test_bug1660452_https.html new file mode 100644 index 0000000000..1aed356a21 --- /dev/null +++ b/dom/security/test/general/test_bug1660452_https.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Bug 1660452: NullPrincipals need to know whether they were spun off of a Secure Context</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +ok(window.isSecureContext, "top level should be a secure context"); + +// eslint-disable-next-line +let newWin = window.open("data:text/html,<script><"+"/script>"); +ok(newWin.isSecureContext, "data uri window should be a secure context"); +newWin.close(); + +window.addEventListener("message", (event) => { + ok(event.data.isSecureContext, "data uri frames should be a secure context"); + if(event.data.finish) { + SimpleTest.finish(); + return; + } + let f2 = document.createElement("iframe"); + // eslint-disable-next-line + f2.src = "data:text/html,<iframe src=\"data:text/html,<script>parent.parent.postMessage({isSecureContext: window.isSecureContext, finish: true}, '*');<"+"/script>\"></iframe>"; + document.body.appendChild(f2); +}); + +let f = document.createElement("iframe"); +// eslint-disable-next-line +f.src = "data:text/html,<script>parent.postMessage({isSecureContext: window.isSecureContext}, '*');<"+"/script>"; +document.body.appendChild(f); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_cache_split.html b/dom/security/test/general/test_cache_split.html new file mode 100644 index 0000000000..f0fc056bce --- /dev/null +++ b/dom/security/test/general/test_cache_split.html @@ -0,0 +1,153 @@ +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>Bug 1454721 - Add same-site cookie test for about:blank and about:srcdoc</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/ChromeTask.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + <img id="cookieImage"> + <script class="testbody" type="text/javascript"> + SimpleTest.requestLongerTimeout(2); + + const CROSS_ORIGIN = "http://mochi.test:8888/"; + const SAME_ORIGIN= "https://example.com/"; + const PATH = "file_cache_splitting_server.sjs"; + + async function getCount() { + return fetch(`${PATH}?state`).then(r => r.text()); + } + async function resetCount() { + return fetch(`${PATH}?flush`).then(r => r.text()); + } + async function ensureLoaded() { + // This Fetch is geting the Response "1", once file_cache_splitting_isloaded + // gets a request without a query String issued from the cache_splitting_window.html + info("Waiting for Pageload"); + let result = await fetch("file_cache_splitting_isloaded.sjs?wait").then(r => r.text); + info("Page has been Loaded"); + return result; + } + + + async function openAndLoadWindow(origin) { + let isLoaded = ensureLoaded(); + let url = `${origin}tests/dom/security/test/general/file_cache_splitting_window.html`; + let w = window.open(url); + // let ew = SpecialPowers.wrap(w); + await isLoaded; + return w; + } + + async function checkStep(step = [SAME_ORIGIN, 1], name) { + info(`Doing Step ${JSON.stringify(step)}`); + let url = step[0]; + let should_count = step[1]; + let w = await openAndLoadWindow(url); + let count = await getCount(); + ok( + count == should_count, + `${name} req to: ${ + url == SAME_ORIGIN ? "Same Origin" : "Cross Origin" + } expected ${should_count} request to Server, got ${count}` + ); + w.close() + } + async function clearCache(){ + info("Clearing Cache"); + SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache(); + await ChromeTask.spawn(null,(()=>{ + Services.cache2.clear(); + })); + } + async function runTest(test) { + info(`Starting Job with - ${test.steps.length} - Requests`); + await resetCount(); + let { prefs, steps, name } = test; + await SpecialPowers.pushPrefEnv(prefs); + for (let step of steps) { + await checkStep(step, name); + } + await clearCache(); + }; + + + add_task( + async () => + runTest({ + name: `Isolated Cache`, + steps: [[SAME_ORIGIN, 1], [SAME_ORIGIN, 1], [CROSS_ORIGIN, 2]], + prefs: { + set: [ + ["privacy.partition.network_state", true] + ], + }, + }) + ); + // Negative Test: The CROSS_ORIGIN should be able to + // acess the cache of SAME_ORIGIN + add_task( + async () => + runTest({ + name: `Non Isolated Cache`, + steps: [[SAME_ORIGIN, 1], [SAME_ORIGIN, 1], [CROSS_ORIGIN, 1]], + prefs: { + set: [ + ["privacy.partition.network_state", false] + ], + }, + }) + ); + // Test that FPI does not affect Cache Isolation + add_task( + async () => + runTest({ + name: `FPI interaction`, + steps: [[SAME_ORIGIN, 1], [SAME_ORIGIN, 1], [CROSS_ORIGIN, 2]], + prefs: { + set: [ + ["privacy.firstparty.isolate", true], + ["privacy.partition.network_state", false], + ], + }, + }) + ); + // Test that cookieBehavior does not affect Cache Isolation + for (let i = 0; i < SpecialPowers.Ci.nsICookieService.BEHAVIOR_LAST ; i++) { + add_task( + async () => + runTest({ + name: `cookieBehavior interaction ${i}`, + steps: [[SAME_ORIGIN, 1], [SAME_ORIGIN, 1], [CROSS_ORIGIN, 2]], + prefs: { + set: [ + ["privacy.firstparty.isolate", false], + ["network.cookie.cookieBehavior", i], + ["privacy.partition.network_state", true], + ], + }, + }) + ); + } + add_task( + async () => + runTest({ + name: `FPI interaction - 2`, + steps: [[SAME_ORIGIN, 1], [SAME_ORIGIN, 1], [CROSS_ORIGIN, 2]], + prefs: { + set: [ + ["privacy.firstparty.isolate", true], + ["privacy.partition.network_state", false], + ], + }, + }) + ); + + </script> +</body> + +</html> diff --git a/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html new file mode 100644 index 0000000000..24ec5dbdd9 --- /dev/null +++ b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html @@ -0,0 +1,103 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1255240 - Test content policy types within content policies for targeted links in iframes</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe style="width:100%;" id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* Description of the test: + * Let's load a link into a targeted iframe and make sure the content policy + * type used for content policy checks is of TYPE_SUBDOCUMENT. + */ + +function createChromeScript() { + /* eslint-env mozilla/chrome-script */ + const POLICYNAME = "@mozilla.org/testpolicy;1"; + const POLICYID = Components.ID("{6cc95ef3-40e1-4d59-87f0-86f100373227}"); + const EXPECTED_URL = + "http://mochi.test:8888/tests/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs?innerframe"; + + var policy = { + // nsISupports implementation + QueryInterface: ChromeUtils.generateQI([ + "nsIFactory", + "nsIContentPolicy", + ]), + + // nsIFactory implementation + createInstance(iid) { + return this.QueryInterface(iid); + }, + + // nsIContentPolicy implementation + shouldLoad(contentLocation, loadInfo) { + if (contentLocation.asciiSpec === EXPECTED_URL) { + sendAsyncMessage("loadBlocked", { policyType: loadInfo.externalContentPolicyType}); + Services.catMan.deleteCategoryEntry( + "content-policy", + POLICYNAME, + false + ); + componentManager.unregisterFactory(POLICYID, policy); + return Ci.nsIContentPolicy.REJECT_REQUEST; + } + return Ci.nsIContentPolicy.ACCEPT; + }, + + shouldProcess(contentLocation, loadInfo) { + return Ci.nsIContentPolicy.ACCEPT; + } + }; + + // Register content policy + var componentManager = Components.manager.QueryInterface( + Ci.nsIComponentRegistrar + ); + + componentManager.registerFactory( + POLICYID, + "Test content policy", + POLICYNAME, + policy + ); + Services.catMan.addCategoryEntry( + "content-policy", + POLICYNAME, + POLICYNAME, + false, + true + ); + + // Adding a new category dispatches an event to update + // caches, so we need to also dispatch an event to make + // sure we don't start the load until after that happens. + Services.tm.dispatchToMainThread(() => { + sendAsyncMessage("setupComplete"); + }); +} + +add_task(async function() { + let chromeScript = SpecialPowers.loadChromeScript(createChromeScript); + await chromeScript.promiseOneMessage("setupComplete"); + + var testframe = document.getElementById("testframe"); + testframe.src = + "file_contentpolicytype_targeted_link_iframe.sjs?testframe"; + + let result = await chromeScript.promiseOneMessage("loadBlocked"); + + is(result.policyType, SpecialPowers.Ci.nsIContentPolicy.TYPE_SUBDOCUMENT, + "content policy type should TYPESUBDOCUMENT"); + + chromeScript.destroy(); +}); +</script> +</body> +</html> diff --git a/dom/security/test/general/test_gpc.html b/dom/security/test/general/test_gpc.html new file mode 100644 index 0000000000..506629554d --- /dev/null +++ b/dom/security/test/general/test_gpc.html @@ -0,0 +1,51 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Global Privacy Control headers</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="application/javascript"> + +add_task(async function testGlobalPrivacyControlDisabled() { + await SpecialPowers.pushPrefEnv({ set: [ + ["privacy.globalprivacycontrol.enabled", false], + ["privacy.globalprivacycontrol.functionality.enabled", true], + ]}) + .then(() => fetch("file_gpc_server.sjs")) + .then((response) => response.text()) + .then((response) => { + is(response, "false", "GPC disabled so header unsent"); + is(navigator.globalPrivacyControl, false, "GPC disabled so navigator property is 0"); + + let worker = new Worker(window.URL.createObjectURL(new Blob(["postMessage(navigator.globalPrivacyControl);"]))); + return new Promise((resolve) => { worker.onmessage = (e) => { resolve(e.data) } }); + }) + .then((response) => { + is(response, false, "GPC disabled so worker's navigator property is 0"); + }); +}); + +add_task(async function testGlobalPrivacyControlEnabled() { + await SpecialPowers.pushPrefEnv({ set: [ + ["privacy.globalprivacycontrol.enabled", true], + ["privacy.globalprivacycontrol.functionality.enabled", true], + ]}) + .then(() => fetch("file_gpc_server.sjs")) + .then((response) => response.text()) + .then((response) => { + is(response, "true", "GPC enabled so header sent and received"); + is(navigator.globalPrivacyControl, true, "GPC enabled so navigator property is 1"); + + let worker = new Worker(window.URL.createObjectURL(new Blob(["postMessage(navigator.globalPrivacyControl);"]))); + return new Promise((resolve) => { worker.onmessage = (e) => { resolve(e.data) } }); + }) + .then((response) => { + is(response, true, "GPC enabled so worker's navigator property is 1"); + }); +}); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_innerhtml_sanitizer.html b/dom/security/test/general/test_innerhtml_sanitizer.html new file mode 100644 index 0000000000..4a4e4efed1 --- /dev/null +++ b/dom/security/test/general/test_innerhtml_sanitizer.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset=utf-8> + <title>Test for Bug 1667113</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1667113">Mozilla Bug 1667113</a> +<div></div> +<script> +SimpleTest.waitForExplicitFinish(); + +// Please note that 'fakeServer' does not exist because the test relies +// on "csp-on-violate-policy" , and "specialpowers-http-notify-request" +// which fire if either the request is blocked or fires. The test does +// not rely on the result of the load. + +function fail() { + ok(false, "Should not call this") +} + +function examiner() { + SpecialPowers.addObserver(this, "csp-on-violate-policy"); + SpecialPowers.addObserver(this, "specialpowers-http-notify-request"); +} +examiner.prototype = { + observe(subject, topic, data) { + if (topic === "csp-on-violate-policy") { + let asciiSpec = SpecialPowers.getPrivilegedProps( + SpecialPowers.do_QueryInterface(subject, "nsIURI"), + "asciiSpec"); + if (asciiSpec.includes("fakeServer")) { + ok (false, "Should not attempt fetch, not even blocked by CSP."); + } + } + + if (topic === "specialpowers-http-notify-request") { + if (data.includes("fakeServer")) { + ok (false, "Should not try fetch"); + } + } + }, + remove() { + SpecialPowers.removeObserver(this, "csp-on-violate-policy"); + SpecialPowers.removeObserver(this, "specialpowers-http-notify-request"); + } +} + +window.examiner = new examiner(); + +let div = document.getElementsByTagName("div")[0]; +div.innerHTML = "<svg><style><title><audio src=fakeServer onerror=fail() onload=fail()>"; + +let svg = div.firstChild; +is(svg.nodeName, "svg", "Node name should be svg"); + +let style = svg.firstChild; +if (style) { + is(style.firstChild, null, "Style should not have child nodes."); +} else { + ok(false, "Should have gotten a node."); +} + + +SimpleTest.executeSoon(function() { + window.examiner.remove(); + SimpleTest.finish(); +}); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_innerhtml_sanitizer.xhtml b/dom/security/test/general/test_innerhtml_sanitizer.xhtml new file mode 100644 index 0000000000..4d938bc23b --- /dev/null +++ b/dom/security/test/general/test_innerhtml_sanitizer.xhtml @@ -0,0 +1,73 @@ +<!DOCTYPE HTML> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Test for Bug 1667113</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1667113">Mozilla Bug 1667113</a> +<div></div> +<script><![CDATA[ +SimpleTest.waitForExplicitFinish(); + +// Please note that 'fakeServer' does not exist because the test relies +// on "csp-on-violate-policy" , and "specialpowers-http-notify-request" +// which fire if either the request is blocked or fires. The test does +// not rely on the result of the load. + +function fail() { + ok(false, "Should not call this") +} + +function examiner() { + SpecialPowers.addObserver(this, "csp-on-violate-policy"); + SpecialPowers.addObserver(this, "specialpowers-http-notify-request"); +} +examiner.prototype = { + observe(subject, topic, data) { + if (topic === "csp-on-violate-policy") { + let asciiSpec = SpecialPowers.getPrivilegedProps( + SpecialPowers.do_QueryInterface(subject, "nsIURI"), + "asciiSpec"); + if (asciiSpec.includes("fakeServer")) { + ok (false, "Should not attempt fetch, not even blocked by CSP."); + } + } + + if (topic === "specialpowers-http-notify-request") { + if (data.includes("fakeServer")) { + ok (false, "Should not try fetch"); + } + } + }, + remove() { + SpecialPowers.removeObserver(this, "csp-on-violate-policy"); + SpecialPowers.removeObserver(this, "specialpowers-http-notify-request"); + } +} + +window.examiner = new examiner(); + +let div = document.getElementsByTagName("div")[0]; +div.innerHTML = "<svg xmlns='http://www.w3.org/2000/svg'><style><title><audio xmlns='http://www.w3.org/1999/xhtml' src='fakeServer' onerror='fail()' onload='fail()'></audio></title></style></svg>"; + +let svg = div.firstChild; +is(svg.nodeName, "svg", "Node name should be svg"); + +let style = svg.firstChild; +if (style) { + is(style.firstChild, null, "Style should not have child nodes."); +} else { + ok(false, "Should have gotten a node."); +} + + +SimpleTest.executeSoon(function() { + window.examiner.remove(); + SimpleTest.finish(); +}); + +]]></script> +</body> +</html> diff --git a/dom/security/test/general/test_meta_referrer.html b/dom/security/test/general/test_meta_referrer.html new file mode 100644 index 0000000000..f5e8b649f4 --- /dev/null +++ b/dom/security/test/general/test_meta_referrer.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1704473 - Remove head requirement for meta name=referrer</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<iframe id="frame_meta_in_head"></iframe> +<iframe id="frame_meta_notin_head"></iframe> + +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +let testCounter = 0; +function checkTestsDone() { + testCounter++; + if(testCounter == 2) { + SimpleTest.finish(); + } +} +var script = SpecialPowers.loadChromeScript(() => { + /* eslint-env mozilla/chrome-script */ + let counter = 0; + Services.obs.addObserver(function onExamResp(subject, topic, data) { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + if (!channel.URI.spec.startsWith("https://example.com") || counter >= 2) { + return; + } + + let refererHeaderSet = false; + try { + channel.getRequestHeader("referer"); + refererHeaderSet = true; + } catch (e) { + refererHeaderSet = false; + } + ok(!refererHeaderSet, "the referer header should not be set"); + counter++; + sendAsyncMessage("checked-referer-header"); + }, "http-on-stop-request"); +}); + +script.addMessageListener("checked-referer-header", checkTestsDone); + +let frame1 = document.getElementById("frame_meta_in_head"); +frame1.src = "/tests/dom/security/test/general/file_meta_referrer_in_head.html"; +let frame2 = document.getElementById("frame_meta_notin_head"); +frame2.src = "/tests/dom/security/test/general/file_meta_referrer_notin_head.html"; + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_nosniff.html b/dom/security/test/general/test_nosniff.html new file mode 100644 index 0000000000..a22386aea0 --- /dev/null +++ b/dom/security/test/general/test_nosniff.html @@ -0,0 +1,88 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 471020 - Add X-Content-Type-Options: nosniff support to Firefox</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <!-- add the two css tests --> + <link rel="stylesheet" id="cssCorrectType"> + <link rel="stylesheet" id="cssWrongType"> +</head> +<body> + +<!-- add the two script tests --> +<script id="scriptCorrectType"></script> +<script id="scriptWrongType"></script> + +<script class="testbody" type="text/javascript"> +/* Description of the test: + * We load 2 css files, 2 script files and 2 image files, where + * the sever either responds with the right mime type or + * the wrong mime type for each test. + */ + +SimpleTest.waitForExplicitFinish(); +const NUM_TESTS = 4; + +var testCounter = 0; +function checkFinish() { + testCounter++; + if (testCounter === NUM_TESTS) { + SimpleTest.finish(); + } +} + + // 1) Test CSS with correct mime type + var cssCorrectType = document.getElementById("cssCorrectType"); + cssCorrectType.onload = function() { + ok(true, "style nosniff correct type should load"); + checkFinish(); + } + cssCorrectType.onerror = function() { + ok(false, "style nosniff correct type should load"); + checkFinish(); + } + cssCorrectType.href = "file_nosniff_testserver.sjs?cssCorrectType"; + + // 2) Test CSS with wrong mime type + var cssWrongType = document.getElementById("cssWrongType"); + cssWrongType.onload = function() { + ok(false, "style nosniff wrong type should not load"); + checkFinish(); + } + cssWrongType.onerror = function() { + ok(true, "style nosniff wrong type should not load"); + checkFinish(); + } + cssWrongType.href = "file_nosniff_testserver.sjs?cssWrongType"; + + // 3) Test SCRIPT with correct mime type + var scriptCorrectType = document.getElementById("scriptCorrectType"); + scriptCorrectType.onload = function() { + ok(true, "script nosniff correct type should load"); + checkFinish(); + } + scriptCorrectType.onerror = function() { + ok(false, "script nosniff correct type should load"); + checkFinish(); + } + scriptCorrectType.src = "file_nosniff_testserver.sjs?scriptCorrectType"; + + // 4) Test SCRIPT with wrong mime type + var scriptWrongType = document.getElementById("scriptWrongType"); + scriptWrongType.onload = function() { + ok(false, "script nosniff wrong type should not load"); + checkFinish(); + } + scriptWrongType.onerror = function() { + ok(true, "script nosniff wrong type should not load"); + checkFinish(); + } + scriptWrongType.src = "file_nosniff_testserver.sjs?scriptWrongType"; + + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_nosniff_navigation.html b/dom/security/test/general/test_nosniff_navigation.html new file mode 100644 index 0000000000..6710f4f5b9 --- /dev/null +++ b/dom/security/test/general/test_nosniff_navigation.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> + +<head> + <title>Bug 1428473 Support X-Content-Type-Options: nosniff when navigating</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + + <!-- add the two script tests --> + <script id="scriptCorrectType"></script> + <script id="scriptWrongType"></script> + + <script class="testbody" type="text/javascript"> + /* Description of the test: + * We're testing if Firefox respects the nosniff Header for Top-Level + * Navigations. + * If Firefox cant Display the Page, it will prompt a download + * and the URL of the Page will be about:blank. + * So we will try to open different content send with + * no-mime, mismatched-mime and garbage-mime types. + * + */ + + SimpleTest.waitForExplicitFinish(); + + window.addEventListener("load", async () => { + window.open("window_nosniff_navigation.html"); + }); + </script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_about.html b/dom/security/test/general/test_same_site_cookies_about.html new file mode 100644 index 0000000000..faf2caab9a --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_about.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1454721 - Add same-site cookie test for about:blank and about:srcdoc</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<img id="cookieImage"> +<iframe id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load an image from http://mochi.test which sets a same site cookie + * 2) We then load the following iframes: + * (a) cross-origin iframe + * (b) same-origin iframe + * which both load a: + * * nested about:srcdoc frame and nested about:blank frame + * * navigate about:srcdoc frame and navigate about:blank frame + * 3) We evaluate that the same-site cookie is available in the same-origin case. + */ + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN = "http://mochi.test:8888/" +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/file_same_site_cookies_about.sjs"; + +let curTest = 0; + +var tests = [ + // NAVIGATION TESTS + { + description: "nested same origin iframe about:srcdoc navigation [mochi.test -> mochi.test -> about:srcdoc -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + "?loadsrcdocframeNav", + result: "myKey=mySameSiteAboutCookie", // cookie should be set for baseline test + }, + { + description: "nested cross origin iframe about:srcdoc navigation [mochi.test -> example.com -> about:srcdoc -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + "?loadsrcdocframeNav", + result: "", // no same-site cookie should be available + }, + { + description: "nested same origin iframe about:blank navigation [mochi.test -> mochi.test -> about:blank -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + "?loadblankframeNav", + result: "myKey=mySameSiteAboutCookie", // cookie should be set for baseline test + }, + { + description: "nested cross origin iframe about:blank navigation [mochi.test -> example.com -> about:blank -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + "?loadblankframeNav", + result: "", // no same-site cookie should be available + }, + // INCLUSION TESTS + { + description: "nested same origin iframe about:srcdoc inclusion [mochi.test -> mochi.test -> about:srcdoc -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + "?loadsrcdocframeInc", + result: "myKey=mySameSiteAboutCookie", // cookie should be set for baseline test + }, + { + description: "nested cross origin iframe about:srcdoc inclusion [mochi.test -> example.com -> about:srcdoc -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + "?loadsrcdocframeInc", + result: "", // no same-site cookie should be available + }, + { + description: "nested same origin iframe about:blank inclusion [mochi.test -> mochi.test -> about:blank -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + "?loadblankframeInc", + result: "myKey=mySameSiteAboutCookie", // cookie should be set for baseline test + }, + { + description: "nested cross origin iframe about:blank inclusion [mochi.test -> example.com -> about:blank -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + "?loadblankframeInc", + result: "", // no same-site cookie should be available + }, +]; + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + is(event.data.result, tests[curTest].result, tests[curTest].description); + curTest += 1; + + // lets see if we ran all the tests + if (curTest == tests.length) { + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); + return; + } + // otherwise it's time to run the next test + setCookieAndInitTest(); +} + +function setupQueryResultAndRunTest() { + let testframe = document.getElementById("testframe"); + testframe.src = tests[curTest].frameSRC + curTest; +} + +function setCookieAndInitTest() { + var cookieImage = document.getElementById("cookieImage"); + cookieImage.onload = function() { + ok(true, "trying to set cookie for test (" + tests[curTest].description + ")"); + setupQueryResultAndRunTest(); + } + cookieImage.onerror = function() { + ok(false, "could not load image for test (" + tests[curTest].description + ")"); + } + cookieImage.src = SAME_ORIGIN + PATH + "?setSameSiteCookie" + curTest; +} + +// fire up the test +setCookieAndInitTest(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_cross_origin_context.html b/dom/security/test/general/test_same_site_cookies_cross_origin_context.html new file mode 100644 index 0000000000..9294a3d030 --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_cross_origin_context.html @@ -0,0 +1,93 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1452496 - Do not allow same-site cookies in cross site context</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<img id="cookieImage"> +<iframe id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load an image from http://example.com which tries to + * a) a same site cookie + * b) a regular cookie + * in the context of http://mochi.test + * 2) We load an iframe from http://example.com and check if the cookie + * is available. + * 3) We observe that: + * (a) same site cookie has been discarded in a cross origin context. + * (b) the regular cookie is available. + */ + +SimpleTest.waitForExplicitFinish(); + +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/file_same_site_cookies_cross_origin_context.sjs"; + +let curTest = 0; + +var tests = [ + { + description: "regular cookie in cross origin context", + imgSRC: CROSS_ORIGIN + PATH + "?setRegularCookie", + frameSRC: CROSS_ORIGIN + PATH + "?loadFrame", + result: "myKey=regularCookie", + }, + { + description: "same-site cookie in cross origin context", + imgSRC: CROSS_ORIGIN + PATH + "?setSameSiteCookie", + frameSRC: CROSS_ORIGIN + PATH + "?loadFrame", + result: "", // no cookie should be set + }, +]; + + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + is(event.data.result, tests[curTest].result, tests[curTest].description); + curTest += 1; + + // lets see if we ran all the tests + if (curTest == tests.length) { + window.removeEventListener("message", receiveMessage); + SpecialPowers.clearUserPref("network.cookie.sameSite.laxByDefault"); + SimpleTest.finish(); + return; + } + // otherwise it's time to run the next test + setCookieAndInitTest(); +} + +function setupQueryResultAndRunTest() { + let testframe = document.getElementById("testframe"); + testframe.src = tests[curTest].frameSRC + curTest; +} + +function setCookieAndInitTest() { + var cookieImage = document.getElementById("cookieImage"); + cookieImage.onload = function() { + ok(true, "trying to set cookie for test (" + tests[curTest].description + ")"); + setupQueryResultAndRunTest(); + } + cookieImage.onerror = function() { + ok(false, "could not load image for test (" + tests[curTest].description + ")"); + } + cookieImage.src = tests[curTest].imgSRC + curTest; +} + +// fire up the test +SpecialPowers.pushPrefEnv({ + "set": [ + // Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default" + ["network.cookie.sameSite.laxByDefault", false], + ] +}, setCookieAndInitTest); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_from_script.html b/dom/security/test/general/test_same_site_cookies_from_script.html new file mode 100644 index 0000000000..74c38b6249 --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_from_script.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1452496 - Do not allow same-site cookies in cross site context</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<iframe id="setCookieFrame"></iframe> +<iframe id="getCookieFrame"></iframe> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load an iframe which tries to set a same site cookie using an + * inline script in top-level context of http://mochi.test. + * 2) We load an iframe from http://example.com and check if the cookie + * is available. + * 3) We observe that: + * (a) same site cookie is available in same origin context. + * (a) same site cookie has been discarded in a cross origin context. + */ + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN = "http://mochi.test:8888/"; +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/file_same_site_cookies_from_script.sjs"; + +let curTest = 0; + +var tests = [ + { + description: "same-site cookie inline script within same-site context", + setCookieSrc: SAME_ORIGIN + PATH + "?setSameSiteCookieUsingInlineScript", + getCookieSrc: SAME_ORIGIN + PATH + "?getCookieFrame", + result: "myKey=sameSiteCookieInlineScript", + }, + { + description: "same-site cookie inline script within cross-site context", + setCookieSrc: CROSS_ORIGIN + PATH + "?setSameSiteCookieUsingInlineScript", + getCookieSrc: CROSS_ORIGIN + PATH + "?getCookieFrame", + result: "", // same-site cookie should be discarded in cross site context + }, +]; + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + is(event.data.result, tests[curTest].result, tests[curTest].description); + curTest += 1; + + // lets see if we ran all the tests + if (curTest == tests.length) { + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); + return; + } + // otherwise it's time to run the next test + setCookieAndInitTest(); +} + +function setupQueryResultAndRunTest() { + let getCookieFrame = document.getElementById("getCookieFrame"); + getCookieFrame.src = tests[curTest].getCookieSrc + curTest; +} + +function setCookieAndInitTest() { + var setCookieFrame = document.getElementById("setCookieFrame"); + setCookieFrame.onload = function() { + ok(true, "trying to set cookie for test (" + tests[curTest].description + ")"); + setupQueryResultAndRunTest(); + } + setCookieFrame.onerror = function() { + ok(false, "could not load image for test (" + tests[curTest].description + ")"); + } + setCookieFrame.src = tests[curTest].setCookieSrc + curTest; +} + +// fire up the test +setCookieAndInitTest(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_iframe.html b/dom/security/test/general/test_same_site_cookies_iframe.html new file mode 100644 index 0000000000..45d5d5830a --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_iframe.html @@ -0,0 +1,168 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1454027 - Update SameSite cookie handling inside iframes</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<img id="cookieImage"> +<iframe id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load an image from http://mochi.test which sets a same site cookie + * 2) We then load the following iframes: + * (a) cross-origin iframe + * (b) sandboxed iframe + * (c) data: URI iframe + * (d) same origin iframe which loads blob: URI iframe (to simulate same origin blobs) + * (e) cross origin iframe which loads blob: URI iframe (to simulate cross origin blobs) + * which all: + * * navigate the iframe to http://mochi.test + * * include another iframe from http://mochi.test + * 3) We observe that none of the nested iframes have access to the same-site cookie. + */ + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN = "http://mochi.test:8888/" +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/"; +const SERVER_FILE = "file_same_site_cookies_iframe.sjs"; + +const NESTED_DATA_IFRAME_NAVIGATION = ` + data:text/html, + <html> + <body> + <a id="testlink" href="http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_iframe.sjs"></a> + <script type="application/javascript"> + let link = document.getElementById("testlink"); + link.click(); + <\/script> + </body> + </html>`; + +const NESTED_DATA_IFRAME_INCLUSION = ` + data:text/html, + <html> + <body> + <script type="application/javascript"> + window.addEventListener("message", receiveMessage); + function receiveMessage(event) { + window.removeEventListener("message", receiveMessage); + window.parent.postMessage({result: event.data.result}, '*'); + } + <\/script> + <iframe src="http://mochi.test:8888/tests/dom/security/test/general/file_same_site_cookies_iframe.sjs"></iframe> + </body> + </html>`; + +let curTest = 0; + +var tests = [ + // NAVIGATION TESTS + { + description: "nested same origin iframe navigation [mochi.test -> mochi.test -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + SERVER_FILE + "?nestedIframeNavigation", + result: "myKey=mySameSiteIframeTestCookie", // cookie should be set for baseline test + }, + { + description: "nested cross origin iframe navigation [mochi.test -> example.com -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + SERVER_FILE + "?nestedIframeNavigation", + result: "", // no cookie should be set + }, + { + description: "nested sandboxed iframe navigation [mochi.test -> sandbox -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + SERVER_FILE + "?nestedSandboxIframeNavigation", + result: "", // no cookie should be set + }, + { + description: "nested data iframe navigation [mochi.test -> data: -> mochi.test]", + frameSRC: NESTED_DATA_IFRAME_NAVIGATION, + result: "", // no cookie should be set + }, + { + description: "nested same site blob iframe navigation [mochi.test -> mochi.test -> blob: -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + "file_same_site_cookies_blob_iframe_navigation.html", + result: "myKey=mySameSiteIframeTestCookie", // cookie should be set, blobs inherit security context + }, + { + description: "nested cross site blob iframe navigation [mochi.test -> example.com -> blob: -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + "file_same_site_cookies_blob_iframe_navigation.html", + result: "", // no cookie should be set + }, + // INCLUSION TESTS + { + description: "nested same origin iframe inclusion [mochi.test -> mochi.test -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + SERVER_FILE + "?nestedIframeInclusion", + result: "myKey=mySameSiteIframeTestCookie", // cookie should be set for baseline test + }, + { + description: "nested cross origin iframe inclusion [mochi.test -> example.com -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + SERVER_FILE + "?nestedIframeInclusion", + result: "", // no cookie should be set + }, + { + description: "nested sandboxed iframe inclusion [mochi.test -> sandbox -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + SERVER_FILE + "?nestedSandboxIframeInclusion", + result: "", // no cookie should be set + }, + { + description: "nested data iframe inclusion [mochi.test -> data: -> mochi.test]", + frameSRC: NESTED_DATA_IFRAME_INCLUSION, + result: "", // no cookie should be set + }, + { + description: "nested same site blob iframe inclusion [mochi.test -> mochi.test -> blob: -> mochi.test]", + frameSRC: SAME_ORIGIN + PATH + "file_same_site_cookies_blob_iframe_inclusion.html", + result: "myKey=mySameSiteIframeTestCookie", // cookie should be set, blobs inherit security context + }, + { + description: "same-site cookie, nested cross site blob iframe inclusion [mochi.test -> example.com -> blob: -> mochi.test]", + frameSRC: CROSS_ORIGIN + PATH + "file_same_site_cookies_blob_iframe_inclusion.html", + result: "", // no cookie should be set + }, +]; + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + is(event.data.result, tests[curTest].result, tests[curTest].description); + curTest += 1; + + // // lets see if we ran all the tests + if (curTest == tests.length) { + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); + return; + } + // otherwise it's time to run the next test + setCookieAndInitTest(); +} + +function setupQueryResultAndRunTest() { + let testframe = document.getElementById("testframe"); + testframe.src = tests[curTest].frameSRC; +} + +function setCookieAndInitTest() { + var cookieImage = document.getElementById("cookieImage"); + cookieImage.onload = function() { + ok(true, "trying to set cookie for test (" + tests[curTest].description + ")"); + setupQueryResultAndRunTest(); + } + cookieImage.onerror = function() { + ok(false, "could not load image for test (" + tests[curTest].description + ")"); + } + // appending math.random to avoid any unexpected caching behavior + cookieImage.src = SAME_ORIGIN + PATH + SERVER_FILE + "?setSameSiteCookie" + Math.random(); +} + +// fire up the test +setCookieAndInitTest(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_laxByDefault.html b/dom/security/test/general/test_same_site_cookies_laxByDefault.html new file mode 100644 index 0000000000..9fd0d0b704 --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_laxByDefault.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1551798 - SameSite=lax by default</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/closeWindow.sjs"; + +async function realTest(noneRequiresSecure) { + let types = ["unset", "lax", "none"]; + for (let i = 0; i < types.length; ++i) { + info("Loading a new top-level page (" + types[i] + ")"); + await new Promise(resolve => { + window.addEventListener("message", _ => { + resolve(); + }, { once: true }); + window.open(CROSS_ORIGIN + PATH + "?" + types[i]); + }); + } + + info("Check cookies"); + let chromeScript = SpecialPowers.loadChromeScript(() => { + /* eslint-env mozilla/chrome-script */ + const {sendAsyncMessage} = this; + let cookies = { test: null, test2: null, test3: null }; + + for (let cookie of Services.cookies.cookies) { + if (cookie.host != "example.com") continue; + + if (cookie.name == "test" && cookie.value == "wow") { + cookies.test = cookie.sameSite == Ci.nsICookie.SAMESITE_LAX ? 'lax' : 'none'; + } + + if (cookie.name == "test2" && cookie.value == "wow2") { + cookies.test2 = cookie.sameSite == Ci.nsICookie.SAMESITE_LAX ? 'lax' : 'none'; + } + + if (cookie.name == "test3" && cookie.value == "wow3") { + cookies.test3 = cookie.sameSite == Ci.nsICookie.SAMESITE_LAX ? 'lax' : 'none'; + } + } + + Services.cookies.removeAll(); + sendAsyncMessage('result', cookies); + }); + + let cookies = await new Promise(resolve => { + chromeScript.addMessageListener('result', cookies => { + chromeScript.destroy(); + resolve(cookies); + }); + }); + + is(cookies.test, "lax", "Cookie set without samesite is lax by default"); + if (noneRequiresSecure) { + is(cookies.test2, null, "Cookie set with samesite none, but not secure"); + } else { + is(cookies.test2, "none", "Cookie set with samesite none"); + } + is(cookies.test3, "lax", "Cookie set with samesite lax"); +} + +SpecialPowers.pushPrefEnv({"set": [ + ["network.cookie.sameSite.laxByDefault", true], + ["network.cookie.sameSite.noneRequiresSecure", false], +]}).then(_ => { + return realTest(false); +}).then(_ => { + return SpecialPowers.pushPrefEnv({"set": [ + ["network.cookie.sameSite.laxByDefault", true], + ["network.cookie.sameSite.noneRequiresSecure", true]]}); +}).then(_ => { + return realTest(true); +}).then(SimpleTest.finish); + +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_redirect.html b/dom/security/test/general/test_same_site_cookies_redirect.html new file mode 100644 index 0000000000..59f98b2263 --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_redirect.html @@ -0,0 +1,101 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1453814 - Do not allow same-site cookies for cross origin redirect</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<img id="cookieImage"> +<iframe id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load an image from http://mochi.test which set a same site cookie + * 2) We then load an iframe that redirects + * (a) from same-origin to cross-origin + * (b) from cross-origin to same-origin + * 3) We observe that in both cases same-site cookies should not be send + */ + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN = location.origin + "/"; +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/file_same_site_cookies_redirect.sjs"; + +let curTest = 0; + +var tests = [ + { + description: "baseline: same-site cookie, redirect same-site to same-site", + imgSRC: SAME_ORIGIN + PATH + "?setSameSiteCookie", + frameSRC: SAME_ORIGIN + PATH + "?sameToSameRedirect", + result: "myKey=strictSameSiteCookie", + }, + { + description: "same-site cookie, redirect same-site to cross-site", + imgSRC: SAME_ORIGIN + PATH + "?setSameSiteCookie", + frameSRC: SAME_ORIGIN + PATH + "?sameToCrossRedirect", + result: "", // no cookie should be set + }, + { + description: "same-site cookie, redirect cross-site to same-site", + imgSRC: SAME_ORIGIN + PATH + "?setSameSiteCookie", + frameSRC: CROSS_ORIGIN + PATH + "?crossToSameRedirect", + result: "", // no cookie should be set + }, + { + description: "same-site cookie, meta redirect same-site to cross-site", + imgSRC: SAME_ORIGIN + PATH + "?setSameSiteCookie", + frameSRC: SAME_ORIGIN + PATH + "?sameToCrossRedirectMeta", + result: "", // no cookie should be set + }, + { + description: "same-site cookie, meta redirect cross-site to same-site", + imgSRC: SAME_ORIGIN + PATH + "?setSameSiteCookie", + frameSRC: CROSS_ORIGIN + PATH + "?crossToSameRedirectMeta", + result: "", // no cookie should be set + }, +]; + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + is(event.data.result, tests[curTest].result, tests[curTest].description); + curTest += 1; + + // // lets see if we ran all the tests + if (curTest == tests.length) { + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); + return; + } + // otherwise it's time to run the next test + setCookieAndInitTest(); +} + +function setupQueryResultAndRunTest() { + let testframe = document.getElementById("testframe"); + testframe.src = tests[curTest].frameSRC; +} + +function setCookieAndInitTest() { + var cookieImage = document.getElementById("cookieImage"); + cookieImage.onload = function() { + ok(true, "trying to set cookie for test (" + tests[curTest].description + ")"); + setupQueryResultAndRunTest(); + } + cookieImage.onerror = function() { + ok(false, "could not load image for test (" + tests[curTest].description + ")"); + } + cookieImage.src = tests[curTest].imgSRC; +} + +// fire up the test +setCookieAndInitTest(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_subrequest.html b/dom/security/test/general/test_same_site_cookies_subrequest.html new file mode 100644 index 0000000000..304dbafa9a --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_subrequest.html @@ -0,0 +1,113 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1286861 - Test same site cookies on subrequests</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<img id="cookieImage"> +<iframe id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load an image from http://mochi.test which sets a same site cookie + * 2) We load an iframe from: + * * http://mochi.test which loads another image from http://mochi.test + * * http://example.com which loads another image from http://mochi.test + * 3) We observe that the same site cookie is sent in the same origin case, + * but not in the cross origin case. + * + * In detail: + * We perform an XHR request to the *.sjs file which is processed async on + * the server and waits till the image request has been processed by the server. + * Once the image requets was processed, the server responds to the initial + * XHR request with the expecuted result (the cookie value). + */ + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN = "http://mochi.test:8888/"; +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/file_same_site_cookies_subrequest.sjs"; + +let curTest = 0; + +var tests = [ + { + description: "same origin site using cookie policy 'samesite=strict'", + imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie", + frameSRC: SAME_ORIGIN + PATH + "?loadFrame", + result: "myKey=strictSameSiteCookie", + }, + { + description: "cross origin site using cookie policy 'samesite=strict'", + imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie", + frameSRC: CROSS_ORIGIN + PATH + "?loadFrame", + result: "myKey=noCookie", + }, + { + description: "same origin site using cookie policy 'samesite=lax'", + imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie", + frameSRC: SAME_ORIGIN + PATH + "?loadFrame", + result: "myKey=laxSameSiteCookie", + }, + { + description: "cross origin site using cookie policy 'samesite=lax'", + imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie", + frameSRC: CROSS_ORIGIN + PATH + "?loadFrame", + result: "myKey=noCookie", + }, +]; + +function checkResult(aCookieVal) { + is(aCookieVal, tests[curTest].result, tests[curTest].description); + curTest += 1; + + // lets see if we ran all the tests + if (curTest == tests.length) { + SimpleTest.finish(); + return; + } + // otherwise it's time to run the next test + setCookieAndInitTest(); +} + +function setupQueryResultAndRunTest() { + var myXHR = new XMLHttpRequest(); + myXHR.open("GET", "file_same_site_cookies_subrequest.sjs?queryresult" + curTest); + myXHR.onload = function(e) { + checkResult(myXHR.responseText); + } + myXHR.onerror = function(e) { + ok(false, "could not query results from server (" + e.message + ")"); + } + myXHR.send(); + + // give it some time and load the test frame + SimpleTest.executeSoon(function() { + let testframe = document.getElementById("testframe"); + testframe.src = tests[curTest].frameSRC + curTest; + }); +} + +function setCookieAndInitTest() { + var cookieImage = document.getElementById("cookieImage"); + cookieImage.onload = function() { + ok(true, "set cookie for test (" + tests[curTest].description + ")"); + setupQueryResultAndRunTest(); + } + cookieImage.onerror = function() { + ok(false, "could not set cookie for test (" + tests[curTest].description + ")"); + } + cookieImage.src = tests[curTest].imgSRC + curTest; +} + +// fire up the test +setCookieAndInitTest(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_toplevel_nav.html b/dom/security/test/general/test_same_site_cookies_toplevel_nav.html new file mode 100644 index 0000000000..aba825916b --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_toplevel_nav.html @@ -0,0 +1,117 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1286861 - Test same site cookies on top-level navigations</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<img id="cookieImage"> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load an image from http://mochi.test which sets a same site cookie + * 2) We open a new window to + * * a same origin location + * * a cross origin location + * 3) We observe that the same site cookie is sent in the same origin case, + * but not in the cross origin case, unless the policy = 'lax', which should + * send the cookie in a top-level navigation case. + * + * In detail: + * We perform an XHR request to the *.sjs file which is processed async on + * the server and waits till the image request has been processed by the server. + * Once the image requets was processed, the server responds to the initial + * XHR request with the expecuted result (the cookie value). + */ + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN = "http://mochi.test:8888/"; +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/file_same_site_cookies_toplevel_nav.sjs"; + +let curTest = 0; + +let currentWindow; +var tests = [ + { + description: "same origin navigation using cookie policy 'samesite=strict'", + imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie", + frameSRC: SAME_ORIGIN + PATH + "?loadFrame", + result: "myKey=strictSameSiteCookie", + }, + { + description: "cross origin navigation using cookie policy 'samesite=strict'", + imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie", + frameSRC: CROSS_ORIGIN + PATH + "?loadFrame", + result: "myKey=noCookie", + }, + { + description: "same origin navigation using cookie policy 'samesite=lax'", + imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie", + frameSRC: SAME_ORIGIN + PATH + "?loadFrame", + result: "myKey=laxSameSiteCookie", + }, + { + description: "cross origin navigation using cookie policy 'samesite=lax'", + imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie", + frameSRC: CROSS_ORIGIN + PATH + "?loadFrame", + result: "myKey=laxSameSiteCookie", + }, +]; + +function checkResult(aCookieVal) { + if(currentWindow){ + currentWindow.close(); + currentWindow= null; + } + is(aCookieVal, tests[curTest].result, tests[curTest].description); + curTest += 1; + + // lets see if we ran all the tests + if (curTest == tests.length) { + SimpleTest.finish(); + return; + } + // otherwise it's time to run the next test + setCookieAndInitTest(); +} + +function setupQueryResultAndRunTest() { + var myXHR = new XMLHttpRequest(); + myXHR.open("GET", "file_same_site_cookies_toplevel_nav.sjs?queryresult" + curTest); + myXHR.onload = function(e) { + checkResult( myXHR.responseText); + } + myXHR.onerror = function(e) { + ok(false, "could not query results from server (" + e.message + ")"); + } + myXHR.send(); + + // give it some time and load the test window + SimpleTest.executeSoon(function() { + currentWindow = window.open(tests[curTest].frameSRC + curTest); + }); +} + +function setCookieAndInitTest() { + var cookieImage = document.getElementById("cookieImage"); + cookieImage.onload = function() { + ok(true, "set cookie for test (" + tests[curTest].description + ")"); + setupQueryResultAndRunTest(); + } + cookieImage.onerror = function() { + ok(false, "could not set cookie for test (" + tests[curTest].description + ")"); + } + cookieImage.src = tests[curTest].imgSRC + curTest; +} + +// fire up the test +setCookieAndInitTest(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_same_site_cookies_toplevel_set_cookie.html b/dom/security/test/general/test_same_site_cookies_toplevel_set_cookie.html new file mode 100644 index 0000000000..cae2a6174e --- /dev/null +++ b/dom/security/test/general/test_same_site_cookies_toplevel_set_cookie.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1454242: Setting samesite cookie should not rely on CookieCommons::IsSameSiteForeign</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<img id="cookieImage"> +<iframe id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* + * Description of the test: + * 1) We load a window from example.com which loads a window from mochi.test + * which then sets a same-site cookie for mochi.test. + * 2) We load an iframe from mochi.test. + * 3) We observe that the cookie within (1) was allowed to be set and + * is available for mochi.test. + */ + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN = "http://mochi.test:8888/" +const CROSS_ORIGIN = "http://example.com/"; +const PATH = "tests/dom/security/test/general/file_same_site_cookies_toplevel_set_cookie.sjs"; + +let testWin = null; + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + // once the second window (which sets the cookie) loaded, we get a notification + // that the test setup is correct and we can now try to query the same-site cookie + if (event.data.value === "testSetupComplete") { + ok(true, "cookie setup worked"); + let testframe = document.getElementById("testframe"); + testframe.src = SAME_ORIGIN + PATH + "?checkCookie"; + return; + } + + // thie second message is the cookie value from verifying the + // cookie has been set correctly. + is(event.data.value, "myKey=laxSameSiteCookie", + "setting same-site cookie on cross origin top-level page"); + + window.removeEventListener("message", receiveMessage); + testWin.close(); + SimpleTest.finish(); +} + +// fire up the test +testWin = window.open(CROSS_ORIGIN + PATH + "?loadWin"); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_xfo_error_page.html b/dom/security/test/general/test_xfo_error_page.html new file mode 100644 index 0000000000..218413b4f9 --- /dev/null +++ b/dom/security/test/general/test_xfo_error_page.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1626249: Ensure correct display of neterror page for XFO</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe style="width:100%;" id="xfo_testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +const XFO_ERROR_PAGE_MSG = "This page has an X-Frame-Options policy that prevents it from being loaded in this context"; + +let xfo_testframe = document.getElementById("xfo_testframe"); + +xfo_testframe.onload = function() { + let wrappedXFOFrame = SpecialPowers.wrap(xfo_testframe.contentWindow); + let frameContentXFO = wrappedXFOFrame.document.body.innerHTML; + ok(frameContentXFO.includes(XFO_ERROR_PAGE_MSG), "xfo error page correct"); + SimpleTest.finish(); +} + +xfo_testframe.onerror = function() { + ok(false, "sanity: should not fire onerror for xfo_testframe"); + SimpleTest.finish(); +} + +xfo_testframe.src = "file_xfo_error_page.sjs"; + +</script> +</body> +</html> diff --git a/dom/security/test/general/window_nosniff_navigation.html b/dom/security/test/general/window_nosniff_navigation.html new file mode 100644 index 0000000000..1287e451b1 --- /dev/null +++ b/dom/security/test/general/window_nosniff_navigation.html @@ -0,0 +1,96 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1428473 Support X-Content-Type-Options: nosniff when navigating</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <style> + iframe{ + border: 1px solid orange; + } + </style> + + <!-- Using Content-Type: */* --> + <iframe class="no-mime" src="file_nosniff_navigation.sjs?mime=*%2F*&content=xml"></iframe> + <iframe class="no-mime" src="file_nosniff_navigation.sjs?mime=*%2F*&content=html"></iframe> + <iframe class="no-mime" src="file_nosniff_navigation.sjs?mime=*%2F*&content=css" ></iframe> + <iframe class="no-mime" src="file_nosniff_navigation.sjs?mime=*%2F*&content=json"></iframe> + <iframe class="no-mime" src="file_nosniff_navigation.sjs?mime=*%2F*&content=img"></iframe> + <iframe class="no-mime" src="file_nosniff_navigation.sjs?mime=*%2F*&content=pdf"></iframe> + <iframe class="no-mime" src="file_nosniff_navigation.sjs?mime=*%2F*"></iframe> + <hr> + <!-- Using Content-Type: image/png --> + <iframe class="mismatch-mime" src="file_nosniff_navigation.sjs?mime=image%2Fpng&content=xml"></iframe> + <iframe class="mismatch-mime" src="file_nosniff_navigation.sjs?mime=image%2Fpng&content=html"></iframe> + <iframe class="mismatch-mime" src="file_nosniff_navigation.sjs?mime=image%2Fpng&content=css"></iframe> + <iframe class="mismatch-mime" src="file_nosniff_navigation.sjs?mime=image%2Fpng&content=json"></iframe> + <iframe class="mismatch-mime" src="file_nosniff_navigation.sjs?mime=image%2Fpng&content=img"></iframe> + <iframe class="mismatch-mime" src="file_nosniff_navigation.sjs?mime=image%2Fpng&content=pdf"></iframe> + <iframe class="mismatch-mime" src="file_nosniff_navigation.sjs?mime=image%2Fpng"></iframe> + <hr> + <!-- Using Content-Type: garbage/garbage --> + <iframe class="garbage-mime" src="file_nosniff_navigation.sjs?mime=garbage%2Fgarbage&content=xml"> </iframe> + <iframe class="garbage-mime" src="file_nosniff_navigation.sjs?mime=garbage%2Fgarbage&content=html"></iframe> + <iframe class="garbage-mime" src="file_nosniff_navigation.sjs?mime=garbage%2Fgarbage&content=css" ></iframe> + <iframe class="garbage-mime" src="file_nosniff_navigation.sjs?mime=garbage%2Fgarbage&content=json"></iframe> + <iframe class="garbage-mime" src="file_nosniff_navigation.sjs?mime=garbage%2Fgarbage&content=img"></iframe> + <iframe class="garbage-mime" src="file_nosniff_navigation.sjs?mime=garbage%2Fgarbage&content=pdf"></iframe> + <iframe class="garbage-mime" src="file_nosniff_navigation.sjs?mime=garbage%2Fgarbage"></iframe> +</head> + +<body> + +<!-- add the two script tests --> +<script id="scriptCorrectType"></script> +<script id="scriptWrongType"></script> + +<script class="testbody" type="text/javascript"> +/* Description of the test: + * We're testing if Firefox respects the nosniff Header for Top-Level + * Navigations. + * If Firefox cant Display the Page, it will prompt a download + * and the URL of the Page will be about:blank. + * So we will try to open different content send with + * no-mime, mismatched-mime and garbage-mime types. + * + */ + +SimpleTest.waitForExplicitFinish(); + +window.addEventListener("load", ()=>{ + let noMimeFrames = Array.from(document.querySelectorAll(".no-mime")); + noMimeFrames.forEach(frame => { + let doc = frame.contentWindow.document; + // In case of no Provided Content Type, not rendering or assuming text/plain is valid + let result = doc.URL == "about:blank" || doc.contentType == "text/plain"; + let sniffTarget = (new URL(frame.src)).searchParams.get("content"); + window.opener.ok(result, `${sniffTarget} without MIME - was not sniffed`); + }); + + let mismatchedMimes = Array.from(document.querySelectorAll(".mismatch-mime")); + mismatchedMimes.forEach(frame => { + // In case the Server mismatches the Mime Type (sends content X as image/png) + // assert that we do not sniff and correct this. + let result = frame.contentWindow.document.contentType == "image/png"; + let sniffTarget = (new URL(frame.src)).searchParams.get("content"); + window.opener.ok(result, `${sniffTarget} send as image/png - was not Sniffed`); + }); + + let badMimeFrames = Array.from(document.querySelectorAll(".garbage-mime")); + badMimeFrames.forEach(frame => { + // In the case we got a bogous mime, assert that we dont sniff. + // We must not default here to text/plain + // as the Server at least provided a mime type. + let result = frame.contentWindow.document.URL == "about:blank"; + let sniffTarget = (new URL(frame.src)).searchParams.get("content"); + window.opener.ok(result, `${sniffTarget} send as garbage/garbage - was not Sniffed`); + }); + + window.opener.SimpleTest.finish(); + this.close(); +}); +</script> +</body> + +</html>
\ No newline at end of file |