diff options
Diffstat (limited to '')
8 files changed, 418 insertions, 0 deletions
diff --git a/dom/credentialmanagement/tests/browser/browser.toml b/dom/credentialmanagement/tests/browser/browser.toml new file mode 100644 index 0000000000..be685270ac --- /dev/null +++ b/dom/credentialmanagement/tests/browser/browser.toml @@ -0,0 +1,3 @@ +[DEFAULT] + +["browser_active_document.js"] diff --git a/dom/credentialmanagement/tests/browser/browser_active_document.js b/dom/credentialmanagement/tests/browser/browser_active_document.js new file mode 100644 index 0000000000..eced461630 --- /dev/null +++ b/dom/credentialmanagement/tests/browser/browser_active_document.js @@ -0,0 +1,139 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const TEST_URL = "https://example.com/"; + +function arrivingHereIsBad(aResult) { + ok(false, "Bad result! Received a: " + aResult); +} + +function expectNotAllowedError(aResult) { + let expected = "NotAllowedError"; + is(aResult.slice(0, expected.length), expected, `Expecting a ${expected}`); +} + +function promiseMakeCredential(tab) { + return ContentTask.spawn(tab.linkedBrowser, null, async function () { + const cose_alg_ECDSA_w_SHA256 = -7; + + let publicKey = { + rp: { id: content.document.domain, name: "none", icon: "none" }, + user: { + id: new Uint8Array(), + name: "none", + icon: "none", + displayName: "none", + }, + challenge: content.crypto.getRandomValues(new Uint8Array(16)), + timeout: 5000, // the minimum timeout is actually 15 seconds + pubKeyCredParams: [{ type: "public-key", alg: cose_alg_ECDSA_w_SHA256 }], + }; + + return content.navigator.credentials.create({ publicKey }); + }); +} + +function promiseGetAssertion(tab) { + return ContentTask.spawn(tab.linkedBrowser, null, async function () { + let newCredential = { + type: "public-key", + id: content.crypto.getRandomValues(new Uint8Array(16)), + transports: ["usb"], + }; + + let publicKey = { + challenge: content.crypto.getRandomValues(new Uint8Array(16)), + timeout: 5000, // the minimum timeout is actually 15 seconds + rpId: content.document.domain, + allowCredentials: [newCredential], + }; + + return content.navigator.credentials.get({ publicKey }); + }); +} + +add_task(async function test_setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.webauth.webauthn", true], + ["security.webauth.webauthn_enable_softtoken", true], + ["security.webauth.webauthn_enable_usbtoken", false], + ], + }); +}); + +add_task(async function test_background_tab() { + // Open two tabs, the last one will selected. + let tab_bg = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + let tab_fg = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + // Requests from background tabs must fail. + await promiseMakeCredential(tab_bg) + .then(arrivingHereIsBad) + .catch(expectNotAllowedError); + + // Requests from background tabs must fail. + await promiseGetAssertion(tab_bg) + .then(arrivingHereIsBad) + .catch(expectNotAllowedError); + + // Close tabs. + await BrowserTestUtils.removeTab(tab_bg); + await BrowserTestUtils.removeTab(tab_fg); +}); + +add_task(async function test_background_window() { + // Open a tab, then a new window. + let tab_bg = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + let win = await BrowserTestUtils.openNewBrowserWindow(); + + // Wait until the new window is really focused. + await new Promise(resolve => SimpleTest.waitForFocus(resolve, win)); + + // Requests from selected tabs not in the active window must fail. + await promiseMakeCredential(tab_bg) + .then(arrivingHereIsBad) + .catch(expectNotAllowedError); + + // Requests from selected tabs not in the active window must fail. + await promiseGetAssertion(tab_bg) + .then(arrivingHereIsBad) + .catch(expectNotAllowedError); + + // Close tab and window. + await BrowserTestUtils.closeWindow(win); + await BrowserTestUtils.removeTab(tab_bg); +}); + +add_task(async function test_minimized() { + // Minimizing windows doesn't supported in headless mode. + if (Services.env.get("MOZ_HEADLESS")) { + return; + } + + // Open a window with a tab. + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + // Minimize the window. + window.minimize(); + await TestUtils.waitForCondition(() => !tab.linkedBrowser.docShellIsActive); + + // Requests from minimized windows must fail. + await promiseMakeCredential(tab) + .then(arrivingHereIsBad) + .catch(expectNotAllowedError); + + // Requests from minimized windows must fail. + await promiseGetAssertion(tab) + .then(arrivingHereIsBad) + .catch(expectNotAllowedError); + + // Restore the window. + await new Promise(resolve => SimpleTest.waitForFocus(resolve, window)); + + // Close tab. + await BrowserTestUtils.removeTab(tab); +}); diff --git a/dom/credentialmanagement/tests/crashtests/bug1691963.html b/dom/credentialmanagement/tests/crashtests/bug1691963.html new file mode 100644 index 0000000000..f7ef34622f --- /dev/null +++ b/dom/credentialmanagement/tests/crashtests/bug1691963.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <script> + document.addEventListener("DOMContentLoaded", () => { + const frame = document.createElement("frame"); + document.body.appendChild(frame); + const { credentials } = frame.contentWindow.navigator; + + let i = 0; + setInterval(async () => { + if (i++ > 3) { + document.documentElement.removeAttribute("class"); + } + try { + await credentials.get({ + publicKey: { + challenge: new Uint8Array(128), + allowCredentials: [{ type: 'public-key', id: new TextEncoder().encode('FOOBAR'), }], + }, + }); + } catch (e) {} + frame.remove(); + }, 1000); + }, { once: true }); + </script> +</head> +</html> diff --git a/dom/credentialmanagement/tests/crashtests/crashtests.list b/dom/credentialmanagement/tests/crashtests/crashtests.list new file mode 100644 index 0000000000..dcd014d6ec --- /dev/null +++ b/dom/credentialmanagement/tests/crashtests/crashtests.list @@ -0,0 +1 @@ +load bug1691963.html diff --git a/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html b/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html new file mode 100644 index 0000000000..e7dbd40b34 --- /dev/null +++ b/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<html> +<head> + <title>Embedded Frame for Credential Management: Prohibit use in cross-origin iframes</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <meta charset=utf-8> +</head> +<body> + +<script class="testbody" type="text/javascript"> +"use strict"; + +const cose_alg_ECDSA_w_SHA256 = -7; +var _parentOrigin = "https://example.com/"; + +function log(msg) { + console.log(msg); + let logBox = document.getElementById("log"); + if (logBox) { + logBox.textContent += "\n" + msg; + } +} + +function local_finished() { + parent.postMessage({"done": true}, _parentOrigin); + log("Done."); +} + +function local_ok(expression, message) { + let body = {"test": expression, "status": expression, "msg": message}; + parent.postMessage(body, _parentOrigin); + log(expression + ": " + message); +} + +function testSameOrigin() { + log("Same origin: " + document.domain); + + navigator.credentials.create({publicKey: makeCredentialOptions}) + .then(function sameOriginCreateThen(aResult) { + local_ok(aResult != undefined, "Create worked " + aResult); + }) + .catch(function sameOriginCatch(aResult) { + local_ok(false, "Should not have failed " + aResult); + }) + .then(function sameOriginPreventSilentAccess() { + return navigator.credentials.preventSilentAccess(); + }) + .then(function sameOriginPreventSilentAccessThen(aResult) { + local_ok(aResult == undefined, "PreventSilentAccess worked " + aResult); + }) + .catch(function sameOriginPreventSilentAccessCatch(aResult) { + local_ok(false, "Should not have failed " + aResult); + }) + .then(function() { + local_finished(); + }); +} + +function testCrossOrigin() { + log("Cross-origin: " + document.domain); + + navigator.credentials.create({publicKey: makeCredentialOptions}) + .then(function crossOriginThen(aBad) { + local_ok(false, "Should not have succeeded " + aBad); + }) + .catch(function crossOriginCatch(aResult) { + local_ok(aResult.toString().startsWith("NotAllowedError"), + "Expecting a NotAllowedError, received " + aResult); + }) + .then(function crossOriginPreventSilentAccess() { + return navigator.credentials.preventSilentAccess(); + }) + .then(function crossOriginPreventSilentAccessThen(aResult) { + local_ok(aResult == undefined, "PreventSilentAccess worked " + aResult); + }) + .catch(function crossOriginPreventSilentAccessCatch(aResult) { + local_ok(false, "Should not have failed " + aResult); + }) + .then(function() { + local_finished(); + }); +} + +let rp = {id: document.domain, name: "none", icon: "none"}; +let user = { + id: crypto.getRandomValues(new Uint8Array(16)), + name: "none", icon: "none", displayName: "none", +}; +let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256}; +let makeCredentialOptions = { + rp, user, challenge: new Uint8Array(), pubKeyCredParams: [param], +}; + +if (document.domain == "example.com") { + testSameOrigin(); +} else { + testCrossOrigin(); +} + +</script> + +<div id="log"></div> + +</body> +</html> diff --git a/dom/credentialmanagement/tests/mochitest/mochitest.toml b/dom/credentialmanagement/tests/mochitest/mochitest.toml new file mode 100644 index 0000000000..d8d142d9d8 --- /dev/null +++ b/dom/credentialmanagement/tests/mochitest/mochitest.toml @@ -0,0 +1,14 @@ +[DEFAULT] +support-files = ["frame_credman_iframes.html"] +scheme = "https" + +["test_credman_empty_option.html"] + +["test_credman_iframes.html"] +skip-if = [ + "xorigin", # Application time out + "win10_2009", # Bug 1718296 + "win11_2009", # Bug 1718296 + "http3", + "http2", +] diff --git a/dom/credentialmanagement/tests/mochitest/test_credman_empty_option.html b/dom/credentialmanagement/tests/mochitest/test_credman_empty_option.html new file mode 100644 index 0000000000..4e582a9f8e --- /dev/null +++ b/dom/credentialmanagement/tests/mochitest/test_credman_empty_option.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<head> + <title>Credential Management: Handle requests with empty options</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <meta charset=utf-8> +</head> +<body> +<h1>Credential Management: Handle requests with empty options</h1> + +<script class="testbody" type="text/javascript"> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true], + ["dom.security.credentialmanagement.enabled", true] + ]}, +async function() { + info("testing create({}).") + try { + await navigator.credentials.create({}); + ok(false, "Credential creation with no options should be an error."); + } + catch (err) { + is(err.name, "NotSupportedError", "Credential creation with no options is a NotSupportedError"); + } + info("testing get({}).") + try { + await navigator.credentials.get({}); + ok(false, "Credential get with no options should be an error."); + } + catch (err) { + is(err.name, "NotSupportedError", "Credential get with no options is a NotSupportedError"); + } + SimpleTest.finish(); +}); +</script> +</body> +</html> diff --git a/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html b/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html new file mode 100644 index 0000000000..b77a868392 --- /dev/null +++ b/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<head> + <title>Credential Management: Prohibit use in cross-origin iframes</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <meta charset=utf-8> +</head> +<body> +<h1>Credential Management: Prohibit use in cross-origin iframes</h1> +<ul> + <li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1407789">Mozilla Bug 1407789</a></li> +</ul> + +<div id="framediv"> + <h2>Same Origin Test</h2> + <iframe id="frame_top"></iframe> + + <h2>Cross-Origin Test</h2> + <iframe id="frame_bottom"></iframe> +</div> + +<script class="testbody" type="text/javascript"> +"use strict"; + +var _countCompletes = 0; +var _expectedCompletes = 2; // 2 iframes + +var _done = new Promise((resolve) => { + function handleEventMessage(event) { + if ("test" in event.data) { + let summary = event.data.test + ": " + event.data.msg; + ok(event.data.status, summary); + } else if ("done" in event.data) { + _countCompletes += 1; + if (_countCompletes == _expectedCompletes) { + console.log("Test compeleted. Finished."); + resolve(); + } + } else { + ok(false, "Unexpected message in the test harness: " + event.data); + } + } + + window.addEventListener("message", handleEventMessage); +}); + +async function addVirtualAuthenticator() { + let id = await SpecialPowers.spawnChrome([], () => { + let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService( + Ci.nsIWebAuthnService + ); + return webauthnService.addVirtualAuthenticator( + "ctap2", + "internal", + true, + true, + true, + true + ); + }); + + SimpleTest.registerCleanupFunction(async () => { + await SpecialPowers.spawnChrome([id], (authenticatorId) => { + let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService( + Ci.nsIWebAuthnService + ); + webauthnService.removeVirtualAuthenticator(authenticatorId); + }); + }); +} + +add_task(async () => { + await SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true], + ["security.webauth.webauthn_enable_softtoken", true], + ["security.webauth.webauthn_enable_usbtoken", false]]}); + await addVirtualAuthenticator(); +}); + +add_task(async () => { + document.getElementById("frame_top").src = "https://example.com/tests/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html"; + + document.getElementById("frame_bottom").src = "https://test1.example.com/tests/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html"; + + await _done; +}); +</script> +</body> +</html> |