diff options
Diffstat (limited to 'dom/security/featurepolicy/test')
7 files changed, 650 insertions, 0 deletions
diff --git a/dom/security/featurepolicy/test/gtest/TestFeaturePolicyParser.cpp b/dom/security/featurepolicy/test/gtest/TestFeaturePolicyParser.cpp new file mode 100644 index 0000000000..3e58971c9b --- /dev/null +++ b/dom/security/featurepolicy/test/gtest/TestFeaturePolicyParser.cpp @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "gtest/gtest.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/dom/Feature.h" +#include "mozilla/dom/FeaturePolicyParser.h" +#include "nsNetUtil.h" +#include "nsTArray.h" + +using namespace mozilla; +using namespace mozilla::dom; + +#define URL_SELF "https://example.com"_ns +#define URL_EXAMPLE_COM "http://example.com"_ns +#define URL_EXAMPLE_NET "http://example.net"_ns + +void CheckParser(const nsAString& aInput, bool aExpectedResults, + uint32_t aExpectedFeatures, + nsTArray<Feature>& aParsedFeatures) { + nsCOMPtr<nsIPrincipal> principal = + mozilla::BasePrincipal::CreateContentPrincipal(URL_SELF); + nsTArray<Feature> parsedFeatures; + ASSERT_TRUE(FeaturePolicyParser::ParseString(aInput, nullptr, principal, + principal, parsedFeatures) == + aExpectedResults); + ASSERT_TRUE(parsedFeatures.Length() == aExpectedFeatures); + + aParsedFeatures = std::move(parsedFeatures); +} + +TEST(FeaturePolicyParser, Basic) +{ + nsCOMPtr<nsIPrincipal> selfPrincipal = + mozilla::BasePrincipal::CreateContentPrincipal(URL_SELF); + nsCOMPtr<nsIPrincipal> exampleComPrincipal = + mozilla::BasePrincipal::CreateContentPrincipal(URL_EXAMPLE_COM); + nsCOMPtr<nsIPrincipal> exampleNetPrincipal = + mozilla::BasePrincipal::CreateContentPrincipal(URL_EXAMPLE_NET); + + nsTArray<Feature> parsedFeatures; + + // Empty string is a valid policy. + CheckParser(u""_ns, true, 0, parsedFeatures); + + // Empty string with spaces is still valid. + CheckParser(u" "_ns, true, 0, parsedFeatures); + + // Non-Existing features with no allowed values + CheckParser(u"non-existing-feature"_ns, true, 0, parsedFeatures); + CheckParser(u"non-existing-feature;another-feature"_ns, true, 0, + parsedFeatures); + + // Existing feature with no allowed values + CheckParser(u"camera"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + + // Some spaces. + CheckParser(u" camera "_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + + // A random ; + CheckParser(u"camera;"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + + // Another random ; + CheckParser(u";camera;"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + + // 2 features + CheckParser(u"camera;microphone"_ns, true, 2, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + ASSERT_TRUE(parsedFeatures[1].Name().Equals(u"microphone"_ns)); + ASSERT_TRUE(parsedFeatures[1].HasAllowList()); + + // 2 features with spaces + CheckParser(u" camera ; microphone "_ns, true, 2, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + ASSERT_TRUE(parsedFeatures[1].Name().Equals(u"microphone"_ns)); + ASSERT_TRUE(parsedFeatures[1].HasAllowList()); + + // 3 features, but only 2 exist. + CheckParser(u"camera;microphone;foobar"_ns, true, 2, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + ASSERT_TRUE(parsedFeatures[1].Name().Equals(u"microphone"_ns)); + ASSERT_TRUE(parsedFeatures[1].HasAllowList()); + + // Multiple spaces around the value + CheckParser(u"camera 'self'"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal)); + + // Multiple spaces around the value + CheckParser(u"camera 'self' "_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal)); + + // No final ' + CheckParser(u"camera 'self"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].HasAllowList()); + ASSERT_TRUE(!parsedFeatures[0].AllowListContains(selfPrincipal)); + + // Lowercase/Uppercase + CheckParser(u"camera 'selF'"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal)); + + // Lowercase/Uppercase + CheckParser(u"camera * 'self' none' a.com 123"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowsAll()); + + // After a 'none' we don't continue the parsing. + CheckParser(u"camera 'none' a.com b.org c.net d.co.uk"_ns, true, 1, + parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowsNone()); + + // After a * we don't continue the parsing. + CheckParser(u"camera * a.com b.org c.net d.co.uk"_ns, true, 1, + parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowsAll()); + + // 'self' + CheckParser(u"camera 'self'"_ns, true, 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal)); + + // A couple of URLs + CheckParser(u"camera http://example.com http://example.net"_ns, true, 1, + parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(!parsedFeatures[0].AllowListContains(selfPrincipal)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleComPrincipal)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleNetPrincipal)); + + // A couple of URLs + self + CheckParser(u"camera http://example.com 'self' http://example.net"_ns, true, + 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleComPrincipal)); + ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleNetPrincipal)); + + // A couple of URLs but then * + CheckParser(u"camera http://example.com 'self' http://example.net *"_ns, true, + 1, parsedFeatures); + ASSERT_TRUE(parsedFeatures[0].Name().Equals(u"camera"_ns)); + ASSERT_TRUE(parsedFeatures[0].AllowsAll()); +} diff --git a/dom/security/featurepolicy/test/gtest/moz.build b/dom/security/featurepolicy/test/gtest/moz.build new file mode 100644 index 0000000000..e307810ff2 --- /dev/null +++ b/dom/security/featurepolicy/test/gtest/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +UNIFIED_SOURCES = [ + "TestFeaturePolicyParser.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" diff --git a/dom/security/featurepolicy/test/mochitest/empty.html b/dom/security/featurepolicy/test/mochitest/empty.html new file mode 100644 index 0000000000..64355e7d19 --- /dev/null +++ b/dom/security/featurepolicy/test/mochitest/empty.html @@ -0,0 +1 @@ +Nothing here diff --git a/dom/security/featurepolicy/test/mochitest/mochitest.ini b/dom/security/featurepolicy/test/mochitest/mochitest.ini new file mode 100644 index 0000000000..3c1850cc2e --- /dev/null +++ b/dom/security/featurepolicy/test/mochitest/mochitest.ini @@ -0,0 +1,11 @@ +[DEFAULT] +prefs = + dom.security.featurePolicy.header.enabled=true + dom.security.featurePolicy.webidl.enabled=true +support-files = + empty.html + test_parser.html^headers^ + +[test_parser.html] +fail-if = xorigin +[test_featureList.html] diff --git a/dom/security/featurepolicy/test/mochitest/test_featureList.html b/dom/security/featurepolicy/test/mochitest/test_featureList.html new file mode 100644 index 0000000000..377c5fccb4 --- /dev/null +++ b/dom/security/featurepolicy/test/mochitest/test_featureList.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test feature policy - list</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe src="empty.html" id="ifr"></iframe> +<script type="text/javascript"> + +let supportedFeatures = [ + "autoplay", + "camera", + "encrypted-media", + "fullscreen", + "gamepad", + "geolocation", + "microphone", + "midi", + "payment", + "display-capture", + "document-domain", + "speaker-selection", + "vr", + "web-share", +]; + +function checkFeatures(features) { + features.forEach(feature => { + ok(supportedFeatures.includes(feature), "Feature: " + feature); + }); +} + +ok("featurePolicy" in document, "We have document.featurePolicy"); +checkFeatures(document.featurePolicy.features()); + +let ifr = document.getElementById("ifr"); +ok("featurePolicy" in ifr, "We have HTMLIFrameElement.featurePolicy"); +checkFeatures(ifr.featurePolicy.features()); + +</script> +</body> +</html> diff --git a/dom/security/featurepolicy/test/mochitest/test_parser.html b/dom/security/featurepolicy/test/mochitest/test_parser.html new file mode 100644 index 0000000000..a8322f6e7d --- /dev/null +++ b/dom/security/featurepolicy/test/mochitest/test_parser.html @@ -0,0 +1,418 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test feature policy - parsing</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe src="empty.html" id="ifr"></iframe> +<iframe src="https://example.org/tests/dom/security/featurePolicy/test/mochitest/empty.html" id="cross_ifr"></iframe> +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +const CROSS_ORIGIN = "https://example.org"; + +function test_document() { + info("Checking document.featurePolicy"); + ok("featurePolicy" in document, "We have document.featurePolicy"); + + ok(!document.featurePolicy.allowsFeature("foobar"), "Random feature"); + ok(!document.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + ok(document.featurePolicy.allowsFeature("camera"), "Camera is allowed for self"); + ok(document.featurePolicy.allowsFeature("camera", "https://foo.bar"), "Camera is always allowed"); + let allowed = document.featurePolicy.getAllowlistForFeature("camera"); + is(allowed.length, 1, "Only 1 entry in allowlist for camera"); + is(allowed[0], "*", "allowlist is *"); + + ok(document.featurePolicy.allowsFeature("geolocation"), "Geolocation is allowed for self"); + ok(document.featurePolicy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for self"); + ok(!document.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = document.featurePolicy.getAllowlistForFeature("geolocation"); + is(allowed.length, 1, "Only 1 entry in allowlist for geolocation"); + is(allowed[0], location.origin, "allowlist is self"); + + ok(!document.featurePolicy.allowsFeature("microphone"), "Microphone is disabled for self"); + ok(!document.featurePolicy.allowsFeature("microphone", location.origin), "Microphone is disabled for self"); + ok(!document.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + ok(document.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is allowed for example.com"); + ok(document.featurePolicy.allowsFeature("microphone", "https://example.org"), "Microphone is allowed for example.org"); + allowed = document.featurePolicy.getAllowlistForFeature("microphone"); + is(allowed.length, 0, "No allowlist for microphone"); + + ok(!document.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + ok(!document.featurePolicy.allowsFeature("vr", location.origin), "Vibrate is disabled for self"); + ok(!document.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = document.featurePolicy.getAllowlistForFeature("vr"); + is(allowed.length, 0, "No allowlist for vr"); + + allowed = document.featurePolicy.allowedFeatures(); + // microphone is disabled for this origin, vr is disabled everywhere. + let camera = false; + let geolocation = false; + allowed.forEach(a => { + if (a == "camera") camera = true; + if (a == "geolocation") geolocation = true; + }); + + ok(camera, "Camera is always allowed"); + ok(geolocation, "Geolocation is allowed only for self"); + + next(); +} + +function test_iframe_without_allow() { + info("Checking HTMLIFrameElement.featurePolicy"); + let ifr = document.getElementById("ifr"); + ok("featurePolicy" in ifr, "HTMLIFrameElement.featurePolicy exists"); + + ok(!ifr.featurePolicy.allowsFeature("foobar"), "Random feature"); + ok(!ifr.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + ok(ifr.featurePolicy.allowsFeature("camera"), "Camera is allowed for self"); + ok(ifr.featurePolicy.allowsFeature("camera", location.origin), "Camera is allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("camera", "https://foo.bar"), "Camera is not allowed for a random URL"); + let allowed = ifr.featurePolicy.getAllowlistForFeature("camera"); + is(allowed.length, 1, "Only 1 entry in allowlist for camera"); + is(allowed[0], location.origin, "allowlist is 'self'"); + + ok(ifr.featurePolicy.allowsFeature("geolocation"), "Geolocation is allowed for self"); + ok(ifr.featurePolicy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = ifr.featurePolicy.getAllowlistForFeature("geolocation"); + is(allowed.length, 1, "Only 1 entry in allowlist for geolocation"); + is(allowed[0], location.origin, "allowlist is '*'"); + + ok(!ifr.featurePolicy.allowsFeature("microphone"), "Microphone is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("microphone", location.origin), "Microphone is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is disabled for example.com"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://example.org"), "Microphone is disabled for example.org"); + allowed = ifr.featurePolicy.getAllowlistForFeature("microphone"); + is(allowed.length, 0, "No allowlist for microphone"); + + ok(!ifr.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", location.origin), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = ifr.featurePolicy.getAllowlistForFeature("vr"); + is(allowed.length, 0, "No allowlist for vr"); + + ok(ifr.featurePolicy.allowedFeatures().includes("camera"), "Camera is allowed"); + ok(ifr.featurePolicy.allowedFeatures().includes("geolocation"), "Geolocation is allowed"); + // microphone is disabled for this origin + ok(!ifr.featurePolicy.allowedFeatures().includes("microphone"), "microphone is not allowed"); + // vr is disabled everywhere. + ok(!ifr.featurePolicy.allowedFeatures().includes("vr"), "VR is not allowed"); + + next(); +} + +function test_iframe_with_allow() { + info("Checking HTMLIFrameElement.featurePolicy"); + let ifr = document.getElementById("ifr"); + ok("featurePolicy" in ifr, "HTMLIFrameElement.featurePolicy exists"); + + ifr.setAttribute("allow", "camera 'none'"); + + ok(!ifr.featurePolicy.allowsFeature("foobar"), "Random feature"); + ok(!ifr.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + ok(!ifr.featurePolicy.allowsFeature("camera"), "Camera is not allowed"); + let allowed = ifr.featurePolicy.getAllowlistForFeature("camera"); + is(allowed.length, 0, "Camera has an empty allowlist"); + + ok(ifr.featurePolicy.allowsFeature("geolocation"), "Geolocation is allowed for self"); + ok(ifr.featurePolicy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = ifr.featurePolicy.getAllowlistForFeature("geolocation"); + is(allowed.length, 1, "Only 1 entry in allowlist for geolocation"); + is(allowed[0], location.origin, "allowlist is '*'"); + + ok(!ifr.featurePolicy.allowsFeature("microphone"), "Microphone is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("microphone", location.origin), "Microphone is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is disabled for example.com"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://example.org"), "Microphone is disabled for example.org"); + allowed = ifr.featurePolicy.getAllowlistForFeature("microphone"); + is(allowed.length, 0, "No allowlist for microphone"); + + ok(!ifr.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", location.origin), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = ifr.featurePolicy.getAllowlistForFeature("vr"); + is(allowed.length, 0, "No allowlist for vr"); + + ok(ifr.featurePolicy.allowedFeatures().includes("geolocation"), "Geolocation is allowed only for self"); + + next(); +} + +function test_iframe_contentDocument() { + info("Checking iframe document.featurePolicy"); + + let ifr = document.createElement("iframe"); + ifr.setAttribute("src", "empty.html"); + ifr.onload = function() { + ok("featurePolicy" in ifr.contentDocument, "We have ifr.contentDocument.featurePolicy"); + + ok(!ifr.contentDocument.featurePolicy.allowsFeature("foobar"), "Random feature"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + ok(ifr.contentDocument.featurePolicy.allowsFeature("camera"), "Camera is allowed for self"); + ok(ifr.contentDocument.featurePolicy.allowsFeature("camera", location.origin), "Camera is allowed for self"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("camera", "https://foo.bar"), "Camera is allowed for self"); + let allowed = ifr.contentDocument.featurePolicy.getAllowlistForFeature("camera"); + is(allowed.length, 1, "Only 1 entry in allowlist for camera"); + is(allowed[0], location.origin, "allowlist is 'self'"); + + ok(ifr.featurePolicy.allowsFeature("geolocation"), "Geolocation is allowed for self"); + ok(ifr.featurePolicy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = ifr.featurePolicy.getAllowlistForFeature("geolocation"); + is(allowed.length, 1, "Only 1 entry in allowlist for geolocation"); + is(allowed[0], location.origin, "allowlist is '*'"); + + ok(!ifr.contentDocument.featurePolicy.allowsFeature("microphone"), "Microphone is disabled for self"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("microphone", location.origin), "Microphone is disabled for self"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is allowed for example.com"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("microphone", "https://example.org"), "Microphone is allowed for example.org"); + allowed = ifr.contentDocument.featurePolicy.getAllowlistForFeature("microphone"); + is(allowed.length, 0, "No allowlist for microphone"); + + ok(!ifr.contentDocument.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("vr", location.origin), "Vibrate is disabled for self"); + ok(!ifr.contentDocument.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = ifr.contentDocument.featurePolicy.getAllowlistForFeature("vr"); + is(allowed.length, 0, "No allowlist for vr"); + + ok(ifr.contentDocument.featurePolicy.allowedFeatures().includes("camera"), "Camera is allowed"); + ok(ifr.contentDocument.featurePolicy.allowedFeatures().includes("geolocation"), "Geolocation is allowed"); + // microphone is disabled for this origin + ok(!ifr.contentDocument.featurePolicy.allowedFeatures().includes("microphone"), "Microphone is not allowed"); + // vr is disabled everywhere. + ok(!ifr.contentDocument.featurePolicy.allowedFeatures().includes("vr"), "VR is not allowed"); + + next(); + }; + document.body.appendChild(ifr); +} + +function test_cross_iframe_without_allow() { + info("Checking cross HTMLIFrameElement.featurePolicy no allow"); + let ifr = document.getElementById("cross_ifr"); + ok("featurePolicy" in ifr, "HTMLIFrameElement.featurePolicy exists"); + + ok(!ifr.featurePolicy.allowsFeature("foobar"), "Random feature"); + ok(!ifr.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + ok(ifr.featurePolicy.allowsFeature("camera"), "Camera is allowed for self"); + ok(ifr.featurePolicy.allowsFeature("camera", CROSS_ORIGIN), "Camera is allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("camera", "https://foo.bar"), "Camera is not allowed for a random URL"); + let allowed = ifr.featurePolicy.getAllowlistForFeature("camera"); + is(allowed.length, 1, "Only 1 entry in allowlist for camera"); + is(allowed[0], CROSS_ORIGIN, "allowlist is 'self'"); + + ok(!ifr.featurePolicy.allowsFeature("geolocation"), "Geolocation is not allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("geolocation", CROSS_ORIGIN), + "Geolocation is not allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = ifr.featurePolicy.getAllowlistForFeature("geolocation"); + is(allowed.length, 0, "No allowlist for geolocation"); + + ok(ifr.featurePolicy.allowsFeature("microphone"), "Microphone is enabled for self"); + ok(ifr.featurePolicy.allowsFeature("microphone", CROSS_ORIGIN), "Microphone is enabled for self"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is disabled for example.com"); + allowed = ifr.featurePolicy.getAllowlistForFeature("microphone"); + is(allowed.length, 1, "Only 1 entry in allowlist for microphone"); + is(allowed[0], CROSS_ORIGIN, "allowlist is self"); + + ok(!ifr.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", CROSS_ORIGIN), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = ifr.featurePolicy.getAllowlistForFeature("vr"); + is(allowed.length, 0, "No allowlist for vr"); + + ok(ifr.featurePolicy.allowedFeatures().includes("camera"), "Camera is allowed"); + ok(!ifr.featurePolicy.allowedFeatures().includes("geolocation"), "Geolocation is not allowed"); + // microphone is enabled for this origin + ok(ifr.featurePolicy.allowedFeatures().includes("microphone"), "microphone is allowed"); + // vr is disabled everywhere. + ok(!ifr.featurePolicy.allowedFeatures().includes("vr"), "VR is not allowed"); + + next(); +} + +function test_cross_iframe_with_allow() { + info("Checking cross HTMLIFrameElement.featurePolicy with allow"); + let ifr = document.getElementById("cross_ifr"); + ok("featurePolicy" in ifr, "HTMLIFrameElement.featurePolicy exists"); + + ifr.setAttribute("allow", "geolocation; camera 'none'"); + + ok(!ifr.featurePolicy.allowsFeature("foobar"), "Random feature"); + ok(!ifr.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + ok(!ifr.featurePolicy.allowsFeature("camera"), "Camera is not allowed"); + let allowed = ifr.featurePolicy.getAllowlistForFeature("camera"); + is(allowed.length, 0, "Camera has an empty allowlist"); + + ok(ifr.featurePolicy.allowsFeature("geolocation"), "Geolocation is allowed for self"); + ok(ifr.featurePolicy.allowsFeature("geolocation", CROSS_ORIGIN), "Geolocation is allowed for self"); + ok(!ifr.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = ifr.featurePolicy.getAllowlistForFeature("geolocation"); + is(allowed.length, 1, "Only 1 entry in allowlist for geolocation"); + is(allowed[0], CROSS_ORIGIN, "allowlist is '*'"); + + ok(ifr.featurePolicy.allowsFeature("microphone"), "Microphone is enabled for self"); + ok(ifr.featurePolicy.allowsFeature("microphone", CROSS_ORIGIN), "Microphone is enabled for self"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + ok(!ifr.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is disabled for example.com"); + allowed = ifr.featurePolicy.getAllowlistForFeature("microphone"); + is(allowed.length, 1, "Only 1 entry in allowlist for microphone"); + is(allowed[0], CROSS_ORIGIN, "allowlist is self"); + + ok(!ifr.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", CROSS_ORIGIN), "Vibrate is disabled for self"); + ok(!ifr.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = ifr.featurePolicy.getAllowlistForFeature("vr"); + is(allowed.length, 0, "No allowlist for vr"); + + ok(ifr.featurePolicy.allowedFeatures().includes("geolocation"), "Geolocation is allowed only for self"); + // microphone is enabled for this origin + ok(ifr.featurePolicy.allowedFeatures().includes("microphone"), "microphone is allowed"); + + next(); +} + +function test_cross_iframe_contentDocument_no_allow() { + info("Checking cross iframe document.featurePolicy no allow"); + + let ifr = document.createElement("iframe"); + ifr.setAttribute("src", "https://example.org/tests/dom/security/featurePolicy/test/mochitest/empty.html"); + ifr.onload = async function() { + await SpecialPowers.spawn(ifr, [], () => { + Assert.ok("featurePolicy" in this.content.document, "We have this.content.document.featurePolicy"); + + Assert.ok(!this.content.document.featurePolicy.allowsFeature("foobar"), "Random feature"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + Assert.ok(this.content.document.featurePolicy.allowsFeature("camera"), "Camera is allowed for self"); + Assert.ok(this.content.document.featurePolicy.allowsFeature("camera", "https://example.org"), "Camera is allowed for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("camera", "https://foo.bar"), "Camera is not allowed for a random URL"); + let allowed = this.content.document.featurePolicy.getAllowlistForFeature("camera"); + Assert.equal(allowed.length, 1, "Only 1 entry in allowlist for camera"); + Assert.equal(allowed[0], "https://example.org", "allowlist is 'self'"); + + Assert.ok(!this.content.document.featurePolicy.allowsFeature("geolocation"), "Geolocation is not allowed for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("geolocation", "https://example.org"), + "Geolocation is not allowed for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = this.content.document.featurePolicy.getAllowlistForFeature("geolocation"); + Assert.equal(allowed.length, 0, "No allowlist for geolocation"); + + Assert.ok(this.content.document.featurePolicy.allowsFeature("microphone"), "Microphone is enabled for self"); + Assert.ok(this.content.document.featurePolicy.allowsFeature("microphone", "https://example.org"), "Microphone is enabled for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is disabled for example.com"); + allowed = this.content.document.featurePolicy.getAllowlistForFeature("microphone"); + Assert.equal(allowed.length, 1, "Only 1 entry in allowlist for microphone"); + Assert.equal(allowed[0], "https://example.org", "allowlist is self"); + + Assert.ok(!this.content.document.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("vr", "https://example.org"), "Vibrate is disabled for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = this.content.document.featurePolicy.getAllowlistForFeature("vr"); + Assert.equal(allowed.length, 0, "No allowlist for vr"); + + Assert.ok(this.content.document.featurePolicy.allowedFeatures().includes("camera"), "Camera is allowed"); + Assert.ok(!this.content.document.featurePolicy.allowedFeatures().includes("geolocation"), "Geolocation is not allowed"); + // microphone is enabled for this origin + Assert.ok(this.content.document.featurePolicy.allowedFeatures().includes("microphone"), "microphone is allowed"); + // vr is disabled everywhere. + Assert.ok(!this.content.document.featurePolicy.allowedFeatures().includes("vr"), "VR is not allowed"); + }); + + next(); + }; + document.body.appendChild(ifr); +} + +function test_cross_iframe_contentDocument_allow() { + info("Checking cross iframe document.featurePolicy with allow"); + + let ifr = document.createElement("iframe"); + ifr.setAttribute("src", "https://example.org/tests/dom/security/featurePolicy/test/mochitest/empty.html"); + ifr.setAttribute("allow", "geolocation; camera 'none'"); + ifr.onload = async function() { + await SpecialPowers.spawn(ifr, [], () => { + Assert.ok("featurePolicy" in this.content.document, "We have this.content.document.featurePolicy"); + + Assert.ok(!this.content.document.featurePolicy.allowsFeature("foobar"), "Random feature"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("foobar", "https://www.something.net"), "Random feature"); + + Assert.ok(!this.content.document.featurePolicy.allowsFeature("camera"), "Camera is not allowed"); + let allowed = this.content.document.featurePolicy.getAllowlistForFeature("camera"); + Assert.equal(allowed.length, 0, "Camera has an empty allowlist"); + + Assert.ok(this.content.document.featurePolicy.allowsFeature("geolocation"), "Geolocation is allowed for self"); + Assert.ok(this.content.document.featurePolicy.allowsFeature("geolocation", "https://example.org"), "Geolocation is allowed for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("geolocation", "https://foo.bar"), "Geolocation is not allowed for any random URL"); + allowed = this.content.document.featurePolicy.getAllowlistForFeature("geolocation"); + Assert.equal(allowed.length, 1, "Only 1 entry in allowlist for geolocation"); + Assert.equal(allowed[0], "https://example.org", "allowlist is '*'"); + + Assert.ok(this.content.document.featurePolicy.allowsFeature("microphone"), "Microphone is enabled for self"); + Assert.ok(this.content.document.featurePolicy.allowsFeature("microphone", "https://example.org"), "Microphone is enabled for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("microphone", "https://foo.bar"), "Microphone is disabled for foo.bar"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("microphone", "https://example.com"), "Microphone is disabled for example.com"); + allowed = this.content.document.featurePolicy.getAllowlistForFeature("microphone"); + Assert.equal(allowed.length, 1, "Only 1 entry in allowlist for microphone"); + Assert.equal(allowed[0], "https://example.org", "allowlist is self"); + + Assert.ok(!this.content.document.featurePolicy.allowsFeature("vr"), "Vibrate is disabled for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("vr", "https://example.org"), "Vibrate is disabled for self"); + Assert.ok(!this.content.document.featurePolicy.allowsFeature("vr", "https://foo.bar"), "Vibrate is disabled for foo.bar"); + allowed = this.content.document.featurePolicy.getAllowlistForFeature("vr"); + Assert.equal(allowed.length, 0, "No allowlist for vr"); + + Assert.ok(this.content.document.featurePolicy.allowedFeatures().includes("geolocation"), "Geolocation is allowed only for self"); + // microphone is enabled for this origin + Assert.ok(this.content.document.featurePolicy.allowedFeatures().includes("microphone"), "microphone is allowed"); + }); + + next(); + }; + document.body.appendChild(ifr); +} + + +var tests = [ + test_document, + test_iframe_without_allow, + test_iframe_with_allow, + test_iframe_contentDocument, + test_cross_iframe_without_allow, + test_cross_iframe_with_allow, + test_cross_iframe_contentDocument_no_allow, + test_cross_iframe_contentDocument_allow +]; + +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +next(); + +</script> +</body> +</html> diff --git a/dom/security/featurepolicy/test/mochitest/test_parser.html^headers^ b/dom/security/featurepolicy/test/mochitest/test_parser.html^headers^ new file mode 100644 index 0000000000..949de013d3 --- /dev/null +++ b/dom/security/featurepolicy/test/mochitest/test_parser.html^headers^ @@ -0,0 +1 @@ +Feature-Policy: camera *; geolocation 'self'; microphone https://example.com https://example.org; vr 'none' |