diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /caps/tests | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'caps/tests')
30 files changed, 2266 insertions, 0 deletions
diff --git a/caps/tests/gtest/TestOriginAttributes.cpp b/caps/tests/gtest/TestOriginAttributes.cpp new file mode 100644 index 0000000000..589798487b --- /dev/null +++ b/caps/tests/gtest/TestOriginAttributes.cpp @@ -0,0 +1,128 @@ +/* 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/NullPrincipal.h" +#include "mozilla/Preferences.h" +#include "nsNetUtil.h" + +using mozilla::OriginAttributes; +using mozilla::Preferences; + +#define FPI_PREF "privacy.firstparty.isolate" +#define SITE_PREF "privacy.firstparty.isolate.use_site" + +#define TEST_FPD(_spec, _expected) \ + TestFPD(nsLiteralString(_spec), nsLiteralString(_expected)) + +namespace mozilla { + +static void TestSuffix(const OriginAttributes& attrs) { + nsAutoCString suffix; + attrs.CreateSuffix(suffix); + + OriginAttributes attrsFromSuffix; + bool success = attrsFromSuffix.PopulateFromSuffix(suffix); + EXPECT_TRUE(success); + + EXPECT_EQ(attrs, attrsFromSuffix); +} + +static void TestFPD(const nsAString& spec, const nsAString& expected) { + OriginAttributes attrs; + nsCOMPtr<nsIURI> url; + ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK); + attrs.SetFirstPartyDomain(true, url); + EXPECT_TRUE(attrs.mFirstPartyDomain.Equals(expected)); + + TestSuffix(attrs); +} + +TEST(OriginAttributes, Suffix_default) +{ + OriginAttributes attrs; + TestSuffix(attrs); +} + +TEST(OriginAttributes, Suffix_inIsolatedMozBrowser) +{ + OriginAttributes attrs(true); + TestSuffix(attrs); +} + +TEST(OriginAttributes, FirstPartyDomain_default) +{ + bool oldFpiPref = Preferences::GetBool(FPI_PREF); + Preferences::SetBool(FPI_PREF, true); + bool oldSitePref = Preferences::GetBool(SITE_PREF); + Preferences::SetBool(SITE_PREF, false); + + TEST_FPD(u"http://www.example.com", u"example.com"); + TEST_FPD(u"http://www.example.com:80", u"example.com"); + TEST_FPD(u"http://www.example.com:8080", u"example.com"); + TEST_FPD(u"http://s3.amazonaws.com", u"s3.amazonaws.com"); + TEST_FPD(u"http://com", u"com"); + TEST_FPD(u"http://com.", u"com."); + TEST_FPD(u"http://com:8080", u"com"); + TEST_FPD(u"http://.com", u""); + TEST_FPD(u"http://..com", u""); + TEST_FPD(u"http://127.0.0.1", u"127.0.0.1"); + TEST_FPD(u"http://[::1]", u"[::1]"); + TEST_FPD(u"about:config", + u"about.ef2a7dd5-93bc-417f-a698-142c3116864f.mozilla"); + TEST_FPD(u"moz-extension://f5b6ca10-5bd4-4ed6-9baf-820dc5152bc1", u""); + + Preferences::SetBool(FPI_PREF, oldFpiPref); + Preferences::SetBool(SITE_PREF, oldSitePref); +} + +TEST(OriginAttributes, FirstPartyDomain_site) +{ + bool oldFpiPref = Preferences::GetBool(FPI_PREF); + Preferences::SetBool(FPI_PREF, true); + bool oldSitePref = Preferences::GetBool(SITE_PREF); + Preferences::SetBool(SITE_PREF, true); + + TEST_FPD(u"http://www.example.com", u"(http,example.com)"); + TEST_FPD(u"http://www.example.com:80", u"(http,example.com)"); + TEST_FPD(u"http://www.example.com:8080", u"(http,example.com)"); + TEST_FPD(u"http://s3.amazonaws.com", u"(http,s3.amazonaws.com)"); + TEST_FPD(u"http://com", u"(http,com)"); + TEST_FPD(u"http://com.", u"(http,com.)"); + TEST_FPD(u"http://com:8080", u"(http,com,8080)"); + TEST_FPD(u"http://.com", u"(http,.com)"); + TEST_FPD(u"http://..com", u"(http,..com)"); + TEST_FPD(u"http://127.0.0.1", u"(http,127.0.0.1)"); + TEST_FPD(u"http://[::1]", u"(http,[::1])"); + TEST_FPD(u"about:config", + u"(about,about.ef2a7dd5-93bc-417f-a698-142c3116864f.mozilla)"); + TEST_FPD(u"moz-extension://f5b6ca10-5bd4-4ed6-9baf-820dc5152bc1", u""); + + Preferences::SetBool(FPI_PREF, oldFpiPref); + Preferences::SetBool(SITE_PREF, oldSitePref); +} + +TEST(OriginAttributes, NullPrincipal) +{ + bool oldFpiPref = Preferences::GetBool(FPI_PREF); + Preferences::SetBool(FPI_PREF, true); + bool oldSitePref = Preferences::GetBool(SITE_PREF); + Preferences::SetBool(SITE_PREF, true); + + constexpr auto spec = + u"moz-nullprincipal:{9bebdabb-828a-4284-8b00-432a968c6e42}"_ns; + constexpr auto expected = u"9bebdabb-828a-4284-8b00-432a968c6e42.mozilla"_ns; + + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), spec); + + RefPtr<NullPrincipal> prin = new NullPrincipal(); + prin->Init(OriginAttributes(), true, uri); + EXPECT_TRUE(prin->OriginAttributesRef().mFirstPartyDomain.Equals(expected)); + + Preferences::SetBool(FPI_PREF, oldFpiPref); + Preferences::SetBool(SITE_PREF, oldSitePref); +} + +} // namespace mozilla diff --git a/caps/tests/gtest/TestPrincipalAttributes.cpp b/caps/tests/gtest/TestPrincipalAttributes.cpp new file mode 100644 index 0000000000..3824cd8934 --- /dev/null +++ b/caps/tests/gtest/TestPrincipalAttributes.cpp @@ -0,0 +1,39 @@ +/* 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 "nsScriptSecurityManager.h" + +using namespace mozilla; + +class PrincipalAttributesParam { + public: + nsAutoCString spec; + bool expectIsIpAddress; +}; + +class PrincipalAttributesTest + : public ::testing::TestWithParam<PrincipalAttributesParam> {}; + +TEST_P(PrincipalAttributesTest, PrincipalAttributesTest) { + nsCOMPtr<nsIScriptSecurityManager> ssm = + nsScriptSecurityManager::GetScriptSecurityManager(); + + nsAutoCString spec(GetParam().spec); + nsCOMPtr<nsIPrincipal> principal; + nsresult rv = + ssm->CreateContentPrincipalFromOrigin(spec, getter_AddRefs(principal)); + ASSERT_EQ(rv, NS_OK); + + ASSERT_EQ(principal->GetIsIpAddress(), GetParam().expectIsIpAddress); +} + +static const PrincipalAttributesParam kAttributes[] = { + {nsAutoCString("https://mozilla.com"), false}, + {nsAutoCString("https://127.0.0.1"), true}, + {nsAutoCString("https://[::1]"), true}, +}; + +INSTANTIATE_TEST_CASE_P(TestPrincipalAttributes, PrincipalAttributesTest, + ::testing::ValuesIn(kAttributes)); diff --git a/caps/tests/gtest/TestPrincipalSerialization.cpp b/caps/tests/gtest/TestPrincipalSerialization.cpp new file mode 100644 index 0000000000..e3abbdeebf --- /dev/null +++ b/caps/tests/gtest/TestPrincipalSerialization.cpp @@ -0,0 +1,215 @@ +/* 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/ContentPrincipal.h" +#include "mozilla/NullPrincipal.h" +#include "mozilla/SystemPrincipal.h" +#include "mozilla/ExpandedPrincipal.h" + +using mozilla::BasePrincipal; +using mozilla::ContentPrincipal; +using mozilla::NullPrincipal; +using mozilla::SystemPrincipal; + +// None of these tests work in debug due to assert guards +#ifndef MOZ_DEBUG + +// calling toJson() twice with the same string arg +// (ensure that we truncate correctly where needed) +TEST(PrincipalSerialization, ReusedJSONArgument) +{ + nsCOMPtr<nsIScriptSecurityManager> ssm = + nsScriptSecurityManager::GetScriptSecurityManager(); + + nsAutoCString spec("https://mozilla.com"); + nsCOMPtr<nsIPrincipal> principal; + nsresult rv = + ssm->CreateContentPrincipalFromOrigin(spec, getter_AddRefs(principal)); + ASSERT_EQ(rv, NS_OK); + + nsAutoCString JSON; + rv = BasePrincipal::Cast(principal)->ToJSON(JSON); + ASSERT_EQ(rv, NS_OK); + ASSERT_TRUE(JSON.EqualsLiteral("{\"1\":{\"0\":\"https://mozilla.com/\"}}")); + + nsAutoCString spec2("https://example.com"); + nsCOMPtr<nsIPrincipal> principal2; + rv = ssm->CreateContentPrincipalFromOrigin(spec2, getter_AddRefs(principal2)); + ASSERT_EQ(rv, NS_OK); + + // Reuse JSON without truncation to check the code is doing this + rv = BasePrincipal::Cast(principal2)->ToJSON(JSON); + ASSERT_EQ(rv, NS_OK); + ASSERT_TRUE(JSON.EqualsLiteral("{\"1\":{\"0\":\"https://example.com/\"}}")); +} + +// Assure that calling FromProperties() with an empty array list always returns +// a nullptr The exception here is SystemPrincipal which doesn't have fields but +// it also doesn't implement FromProperties These are overly cautious checks +// that we don't try to create a principal in reality FromProperties is only +// called with a populated array. +TEST(PrincipalSerialization, FromPropertiesEmpty) +{ + nsTArray<ContentPrincipal::KeyVal> resContent; + nsCOMPtr<nsIPrincipal> contentPrincipal = + ContentPrincipal::FromProperties(resContent); + ASSERT_EQ(nullptr, contentPrincipal); + + nsTArray<ExpandedPrincipal::KeyVal> resExpanded; + nsCOMPtr<nsIPrincipal> expandedPrincipal = + ExpandedPrincipal::FromProperties(resExpanded); + ASSERT_EQ(nullptr, expandedPrincipal); + + nsTArray<NullPrincipal::KeyVal> resNull; + nsCOMPtr<nsIPrincipal> nullprincipal = NullPrincipal::FromProperties(resNull); + ASSERT_EQ(nullptr, nullprincipal); +} + +// Double check that if we have two valid principals in a serialized JSON that +// nullptr is returned +TEST(PrincipalSerialization, TwoKeys) +{ + // Sanity check that this returns a system principal + nsCOMPtr<nsIPrincipal> systemPrincipal = + BasePrincipal::FromJSON("{\"3\":{}}"_ns); + ASSERT_EQ(BasePrincipal::Cast(systemPrincipal)->Kind(), + BasePrincipal::eSystemPrincipal); + + // Sanity check that this returns a content principal + nsCOMPtr<nsIPrincipal> contentPrincipal = + BasePrincipal::FromJSON("{\"1\":{\"0\":\"https://mozilla.com\"}}"_ns); + ASSERT_EQ(BasePrincipal::Cast(contentPrincipal)->Kind(), + BasePrincipal::eContentPrincipal); + + // Check both combined don't return a principal + nsCOMPtr<nsIPrincipal> combinedPrincipal = BasePrincipal::FromJSON( + "{\"1\":{\"0\":\"https://mozilla.com\"},\"3\":{}}"_ns); + ASSERT_EQ(nullptr, combinedPrincipal); +} + +#endif // ifndef MOZ_DEBUG + +TEST(PrincipalSerialization, ExpandedPrincipal) +{ + // Check basic Expandedprincipal works without OA + nsCOMPtr<nsIScriptSecurityManager> ssm = + nsScriptSecurityManager::GetScriptSecurityManager(); + + uint32_t length = 2; + nsTArray<nsCOMPtr<nsIPrincipal> > allowedDomains(length); + allowedDomains.SetLength(length); + + nsAutoCString spec("https://mozilla.com"); + nsCOMPtr<nsIPrincipal> principal; + nsresult rv = + ssm->CreateContentPrincipalFromOrigin(spec, getter_AddRefs(principal)); + ASSERT_EQ(rv, NS_OK); + ASSERT_EQ(BasePrincipal::Cast(principal)->Kind(), + BasePrincipal::eContentPrincipal); + allowedDomains[0] = principal; + + nsAutoCString spec2("https://mozilla.org"); + nsCOMPtr<nsIPrincipal> principal2; + rv = ssm->CreateContentPrincipalFromOrigin(spec2, getter_AddRefs(principal2)); + ASSERT_EQ(rv, NS_OK); + ASSERT_EQ(BasePrincipal::Cast(principal2)->Kind(), + BasePrincipal::eContentPrincipal); + allowedDomains[1] = principal2; + + OriginAttributes attrs; + RefPtr<ExpandedPrincipal> result = + ExpandedPrincipal::Create(allowedDomains, attrs); + ASSERT_EQ(BasePrincipal::Cast(result)->Kind(), + BasePrincipal::eExpandedPrincipal); + + nsAutoCString JSON; + rv = BasePrincipal::Cast(result)->ToJSON(JSON); + ASSERT_EQ(rv, NS_OK); + ASSERT_STREQ( + JSON.get(), + "{\"2\":{\"0\":\"eyIxIjp7IjAiOiJodHRwczovL21vemlsbGEuY29tLyJ9fQ==," + "eyIxIjp7IjAiOiJodHRwczovL21vemlsbGEub3JnLyJ9fQ==\"}}"); + + nsCOMPtr<nsIPrincipal> returnedPrincipal = BasePrincipal::FromJSON(JSON); + auto outPrincipal = BasePrincipal::Cast(returnedPrincipal); + ASSERT_EQ(outPrincipal->Kind(), BasePrincipal::eExpandedPrincipal); + + ASSERT_TRUE(outPrincipal->FastSubsumesIgnoringFPD(principal)); + ASSERT_TRUE(outPrincipal->FastSubsumesIgnoringFPD(principal2)); + + nsAutoCString specDev("https://mozilla.dev"); + nsCOMPtr<nsIPrincipal> principalDev; + rv = ssm->CreateContentPrincipalFromOrigin(specDev, + getter_AddRefs(principalDev)); + ASSERT_EQ(rv, NS_OK); + ASSERT_EQ(BasePrincipal::Cast(principalDev)->Kind(), + BasePrincipal::eContentPrincipal); + + ASSERT_FALSE(outPrincipal->FastSubsumesIgnoringFPD(principalDev)); +} + +TEST(PrincipalSerialization, ExpandedPrincipalOA) +{ + // Check Expandedprincipal works with top level OA + nsCOMPtr<nsIScriptSecurityManager> ssm = + nsScriptSecurityManager::GetScriptSecurityManager(); + + uint32_t length = 2; + nsTArray<nsCOMPtr<nsIPrincipal> > allowedDomains(length); + allowedDomains.SetLength(length); + + nsAutoCString spec("https://mozilla.com"); + nsCOMPtr<nsIPrincipal> principal; + nsresult rv = + ssm->CreateContentPrincipalFromOrigin(spec, getter_AddRefs(principal)); + ASSERT_EQ(rv, NS_OK); + ASSERT_EQ(BasePrincipal::Cast(principal)->Kind(), + BasePrincipal::eContentPrincipal); + allowedDomains[0] = principal; + + nsAutoCString spec2("https://mozilla.org"); + nsCOMPtr<nsIPrincipal> principal2; + rv = ssm->CreateContentPrincipalFromOrigin(spec2, getter_AddRefs(principal2)); + ASSERT_EQ(rv, NS_OK); + ASSERT_EQ(BasePrincipal::Cast(principal2)->Kind(), + BasePrincipal::eContentPrincipal); + allowedDomains[1] = principal2; + + OriginAttributes attrs; + nsAutoCString suffix("^userContextId=1"); + bool ok = attrs.PopulateFromSuffix(suffix); + ASSERT_TRUE(ok); + + RefPtr<ExpandedPrincipal> result = + ExpandedPrincipal::Create(allowedDomains, attrs); + ASSERT_EQ(BasePrincipal::Cast(result)->Kind(), + BasePrincipal::eExpandedPrincipal); + + nsAutoCString JSON; + rv = BasePrincipal::Cast(result)->ToJSON(JSON); + ASSERT_EQ(rv, NS_OK); + ASSERT_STREQ( + JSON.get(), + "{\"2\":{\"0\":\"eyIxIjp7IjAiOiJodHRwczovL21vemlsbGEuY29tLyJ9fQ==," + "eyIxIjp7IjAiOiJodHRwczovL21vemlsbGEub3JnLyJ9fQ==\",\"1\":\"^" + "userContextId=1\"}}"); + + nsCOMPtr<nsIPrincipal> returnedPrincipal = BasePrincipal::FromJSON(JSON); + auto outPrincipal = BasePrincipal::Cast(returnedPrincipal); + ASSERT_EQ(outPrincipal->Kind(), BasePrincipal::eExpandedPrincipal); + + ASSERT_TRUE(outPrincipal->FastSubsumesIgnoringFPD(principal)); + ASSERT_TRUE(outPrincipal->FastSubsumesIgnoringFPD(principal2)); + + nsAutoCString specDev("https://mozilla.dev"); + nsCOMPtr<nsIPrincipal> principalDev; + rv = ssm->CreateContentPrincipalFromOrigin(specDev, + getter_AddRefs(principalDev)); + ASSERT_EQ(rv, NS_OK); + ASSERT_EQ(BasePrincipal::Cast(principalDev)->Kind(), + BasePrincipal::eContentPrincipal); + + ASSERT_FALSE(outPrincipal->FastSubsumesIgnoringFPD(principalDev)); +} diff --git a/caps/tests/gtest/moz.build b/caps/tests/gtest/moz.build new file mode 100644 index 0000000000..1ce8663119 --- /dev/null +++ b/caps/tests/gtest/moz.build @@ -0,0 +1,15 @@ +# -*- 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 += [ + "TestOriginAttributes.cpp", + "TestPrincipalAttributes.cpp", + "TestPrincipalSerialization.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" diff --git a/caps/tests/mochitest/.eslintrc.js b/caps/tests/mochitest/.eslintrc.js new file mode 100644 index 0000000000..735f687ed1 --- /dev/null +++ b/caps/tests/mochitest/.eslintrc.js @@ -0,0 +1,5 @@ +"use strict"; + +module.exports = { + extends: ["plugin:mozilla/mochitest-test", "plugin:mozilla/browser-test"], +}; diff --git a/caps/tests/mochitest/browser.ini b/caps/tests/mochitest/browser.ini new file mode 100644 index 0000000000..a1c76eb57b --- /dev/null +++ b/caps/tests/mochitest/browser.ini @@ -0,0 +1,2 @@ +[browser_checkloaduri.js] +[browser_aboutOrigin.js] diff --git a/caps/tests/mochitest/browser_aboutOrigin.js b/caps/tests/mochitest/browser_aboutOrigin.js new file mode 100644 index 0000000000..fc2e2d8f53 --- /dev/null +++ b/caps/tests/mochitest/browser_aboutOrigin.js @@ -0,0 +1,12 @@ +"use strict"; + +let tests = ["about:robots?foo", "about:robots#foo", "about:robots?foo#bar"]; +tests.forEach(async test => { + add_task(async () => { + await BrowserTestUtils.withNewTab(test, async browser => { + await SpecialPowers.spawn(browser, [], () => { + is(content.document.nodePrincipal.origin, "about:robots"); + }); + }); + }); +}); diff --git a/caps/tests/mochitest/browser_checkloaduri.js b/caps/tests/mochitest/browser_checkloaduri.js new file mode 100644 index 0000000000..4607c80228 --- /dev/null +++ b/caps/tests/mochitest/browser_checkloaduri.js @@ -0,0 +1,380 @@ +"use strict"; + +let ssm = Services.scriptSecurityManager; +// This will show a directory listing, but we never actually load these so that's OK. +const kDummyPage = getRootDirectory(gTestPath); + +const kAboutPagesRegistered = Promise.all([ + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-chrome-privs", + kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-chrome-privs2", + kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-unknown-linkable", + kDummyPage, + Ci.nsIAboutModule.MAKE_LINKABLE | Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-unknown-linkable2", + kDummyPage, + Ci.nsIAboutModule.MAKE_LINKABLE | Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-unknown-unlinkable", + kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-unknown-unlinkable2", + kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-content-unlinkable", + kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | + Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-content-unlinkable2", + kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | + Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-content-linkable", + kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | + Ci.nsIAboutModule.MAKE_LINKABLE | + Ci.nsIAboutModule.ALLOW_SCRIPT + ), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, + "test-content-linkable2", + kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | + Ci.nsIAboutModule.MAKE_LINKABLE | + Ci.nsIAboutModule.ALLOW_SCRIPT + ), +]); + +const URLs = new Map([ + [ + "http://www.example.com", + [ + // For each of these entries, the booleans represent whether the parent URI can: + // - load them + // - load them without principal inheritance + // - whether the URI can be created at all (some protocol handlers will + // refuse to create certain variants) + ["http://www.example2.com", true, true, true], + ["https://www.example2.com", true, true, true], + ["moz-icon:file:///foo/bar/baz.exe", false, false, true], + ["moz-icon://.exe", false, false, true], + ["chrome://foo/content/bar.xul", false, false, true], + ["view-source:http://www.example2.com", false, false, true], + ["view-source:https://www.example2.com", false, false, true], + ["data:text/html,Hi", true, false, true], + ["view-source:data:text/html,Hi", false, false, true], + ["javascript:alert('hi')", true, false, true], + ["moz://a", false, false, true], + ["about:test-chrome-privs", false, false, true], + ["about:test-unknown-unlinkable", false, false, true], + ["about:test-content-unlinkable", false, false, true], + ["about:test-content-linkable", true, true, true], + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], + ], + ], + [ + "view-source:http://www.example.com", + [ + ["http://www.example2.com", true, true, true], + ["https://www.example2.com", true, true, true], + ["moz-icon:file:///foo/bar/baz.exe", false, false, true], + ["moz-icon://.exe", false, false, true], + ["chrome://foo/content/bar.xul", false, false, true], + ["view-source:http://www.example2.com", true, true, true], + ["view-source:https://www.example2.com", true, true, true], + ["data:text/html,Hi", true, false, true], + ["view-source:data:text/html,Hi", true, false, true], + ["javascript:alert('hi')", true, false, true], + ["moz://a", false, false, true], + ["about:test-chrome-privs", false, false, true], + ["about:test-unknown-unlinkable", false, false, true], + ["about:test-content-unlinkable", false, false, true], + ["about:test-content-linkable", true, true, true], + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], + ], + ], + // about: related tests. + [ + "about:test-chrome-privs", + [ + ["about:test-chrome-privs", true, true, true], + ["about:test-chrome-privs2", true, true, true], + ["about:test-chrome-privs2?foo#bar", true, true, true], + ["about:test-chrome-privs2?foo", true, true, true], + ["about:test-chrome-privs2#bar", true, true, true], + + ["about:test-unknown-unlinkable", true, true, true], + + ["about:test-content-unlinkable", true, true, true], + ["about:test-content-unlinkable?foo", true, true, true], + ["about:test-content-unlinkable?foo#bar", true, true, true], + ["about:test-content-unlinkable#bar", true, true, true], + + ["about:test-content-linkable", true, true, true], + + ["about:test-unknown-linkable", true, true, true], + ["moz-icon:file:///foo/bar/baz.exe", true, true, true], + ["moz-icon://.exe", true, true, true], + ], + ], + [ + "about:test-unknown-unlinkable", + [ + ["about:test-chrome-privs", false, false, true], + + // Can link to ourselves: + ["about:test-unknown-unlinkable", true, true, true], + // Can't link to unlinkable content if we're not sure it's privileged: + ["about:test-unknown-unlinkable2", false, false, true], + + ["about:test-content-unlinkable", true, true, true], + ["about:test-content-unlinkable2", true, true, true], + ["about:test-content-unlinkable2?foo", true, true, true], + ["about:test-content-unlinkable2?foo#bar", true, true, true], + ["about:test-content-unlinkable2#bar", true, true, true], + + ["about:test-content-linkable", true, true, true], + + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], + ], + ], + [ + "about:test-content-unlinkable", + [ + ["about:test-chrome-privs", false, false, true], + + // Can't link to unlinkable content if we're not sure it's privileged: + ["about:test-unknown-unlinkable", false, false, true], + + ["about:test-content-unlinkable", true, true, true], + ["about:test-content-unlinkable2", true, true, true], + ["about:test-content-unlinkable2?foo", true, true, true], + ["about:test-content-unlinkable2?foo#bar", true, true, true], + ["about:test-content-unlinkable2#bar", true, true, true], + + ["about:test-content-linkable", true, true, true], + ["about:test-unknown-linkable", false, false, true], + ], + ], + [ + "about:test-unknown-linkable", + [ + ["about:test-chrome-privs", false, false, true], + + // Linkable content can't link to unlinkable content. + ["about:test-unknown-unlinkable", false, false, true], + + ["about:test-content-unlinkable", false, false, true], + ["about:test-content-unlinkable2", false, false, true], + ["about:test-content-unlinkable2?foo", false, false, true], + ["about:test-content-unlinkable2?foo#bar", false, false, true], + ["about:test-content-unlinkable2#bar", false, false, true], + + // ... but it can link to other linkable content. + ["about:test-content-linkable", true, true, true], + + // Can link to ourselves: + ["about:test-unknown-linkable", true, true, true], + + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable2", false, false, true], + ], + ], + [ + "about:test-content-linkable", + [ + ["about:test-chrome-privs", false, false, true], + + // Linkable content can't link to unlinkable content. + ["about:test-unknown-unlinkable", false, false, true], + + ["about:test-content-unlinkable", false, false, true], + + // ... but it can link to itself and other linkable content. + ["about:test-content-linkable", true, true, true], + ["about:test-content-linkable2", true, true, true], + + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], + ], + ], +]); + +function testURL( + source, + target, + canLoad, + canLoadWithoutInherit, + canCreate, + flags +) { + function getPrincipalDesc(principal) { + if (principal.spec != "") { + return principal.spec; + } + if (principal.isSystemPrincipal) { + return "system principal"; + } + if (principal.isNullPrincipal) { + return "null principal"; + } + return "unknown principal"; + } + let threw = false; + let targetURI; + try { + targetURI = makeURI(target); + } catch (ex) { + ok( + !canCreate, + "Shouldn't be passing URIs that we can't create. Failed to create: " + + target + ); + return; + } + ok( + canCreate, + "Created a URI for " + + target + + " which should " + + (canCreate ? "" : "not ") + + "be possible." + ); + try { + ssm.checkLoadURIWithPrincipal(source, targetURI, flags); + } catch (ex) { + info(ex.message); + threw = true; + } + let inheritDisallowed = flags & ssm.DISALLOW_INHERIT_PRINCIPAL; + let shouldThrow = inheritDisallowed ? !canLoadWithoutInherit : !canLoad; + ok( + threw == shouldThrow, + "Should " + + (shouldThrow ? "" : "not ") + + "throw an error when loading " + + target + + " from " + + getPrincipalDesc(source) + + (inheritDisallowed ? " without" : " with") + + " principal inheritance." + ); +} + +add_task(async function() { + await kAboutPagesRegistered; + let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS; + for (let [sourceString, targetsAndExpectations] of URLs) { + let source; + if (sourceString.startsWith("about:test-chrome-privs")) { + source = ssm.getSystemPrincipal(); + } else { + source = ssm.createContentPrincipal(makeURI(sourceString), {}); + } + for (let [ + target, + canLoad, + canLoadWithoutInherit, + canCreate, + ] of targetsAndExpectations) { + testURL( + source, + target, + canLoad, + canLoadWithoutInherit, + canCreate, + baseFlags + ); + testURL( + source, + target, + canLoad, + canLoadWithoutInherit, + canCreate, + baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL + ); + } + } + + // Now test blob URIs, which we need to do in-content. + await BrowserTestUtils.withNewTab("http://www.example.com/", async function( + browser + ) { + await SpecialPowers.spawn(browser, [testURL.toString()], async function( + testURLFn + ) { + // eslint-disable-next-line no-shadow , no-eval + let testURL = eval("(" + testURLFn + ")"); + // eslint-disable-next-line no-shadow + let ssm = Services.scriptSecurityManager; + // eslint-disable-next-line no-shadow + let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS; + // eslint-disable-next-line no-unused-vars + let makeURI = ChromeUtils.import( + "resource://gre/modules/BrowserUtils.jsm", + {} + ).BrowserUtils.makeURI; + let b = new content.Blob(["I am a blob"]); + let contentBlobURI = content.URL.createObjectURL(b); + let contentPrincipal = content.document.nodePrincipal; + // Loading this blob URI from the content page should work: + testURL(contentPrincipal, contentBlobURI, true, true, true, baseFlags); + testURL( + contentPrincipal, + contentBlobURI, + true, + true, + true, + baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL + ); + + testURL( + contentPrincipal, + "view-source:" + contentBlobURI, + false, + false, + true, + baseFlags + ); + testURL( + contentPrincipal, + "view-source:" + contentBlobURI, + false, + false, + true, + baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL + ); + }); + }); +}); diff --git a/caps/tests/mochitest/chrome.ini b/caps/tests/mochitest/chrome.ini new file mode 100644 index 0000000000..776afa34e4 --- /dev/null +++ b/caps/tests/mochitest/chrome.ini @@ -0,0 +1,12 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + file_data.txt + file_disableScript.html + !/caps/tests/mochitest/file_data.txt + !/caps/tests/mochitest/file_disableScript.html + +[test_bug995943.xhtml] +skip-if = (verify && debug && (os == 'mac')) +[test_addonMayLoad.html] +[test_disableScript.xhtml] diff --git a/caps/tests/mochitest/file_bug1367586-followon.html b/caps/tests/mochitest/file_bug1367586-followon.html new file mode 100644 index 0000000000..3b648ce746 --- /dev/null +++ b/caps/tests/mochitest/file_bug1367586-followon.html @@ -0,0 +1 @@ +<body>Follow-on navigation content</body> diff --git a/caps/tests/mochitest/file_bug1367586-redirect.sjs b/caps/tests/mochitest/file_bug1367586-redirect.sjs new file mode 100644 index 0000000000..0f99404e87 --- /dev/null +++ b/caps/tests/mochitest/file_bug1367586-redirect.sjs @@ -0,0 +1,5 @@ +function handleRequest(aRequest, aResponse) { + aResponse.setStatusLine(aRequest.httpVersion, 302, "Moved"); + aResponse.setHeader("Location", "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html"); + aResponse.write("To be redirected to target"); +} diff --git a/caps/tests/mochitest/file_bug1367586-target.html b/caps/tests/mochitest/file_bug1367586-target.html new file mode 100644 index 0000000000..e2a2fde20d --- /dev/null +++ b/caps/tests/mochitest/file_bug1367586-target.html @@ -0,0 +1,6 @@ +<head><script> +window.addEventListener("pageshow", function(event) { + parent.ok(!event.persisted, "Should not load from bfcache"); +}); +</script></head> +<body>Redirect target content</body> diff --git a/caps/tests/mochitest/file_data.txt b/caps/tests/mochitest/file_data.txt new file mode 100644 index 0000000000..26d7bd8488 --- /dev/null +++ b/caps/tests/mochitest/file_data.txt @@ -0,0 +1 @@ +server data fetched over XHR diff --git a/caps/tests/mochitest/file_disableScript.html b/caps/tests/mochitest/file_disableScript.html new file mode 100644 index 0000000000..f4888cd586 --- /dev/null +++ b/caps/tests/mochitest/file_disableScript.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> +<script> +var gFiredOnload = false; +var gFiredOnclick = false; +</script> +</head> +<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;"> +</body> +</html> diff --git a/caps/tests/mochitest/mochitest.ini b/caps/tests/mochitest/mochitest.ini new file mode 100644 index 0000000000..204f637898 --- /dev/null +++ b/caps/tests/mochitest/mochitest.ini @@ -0,0 +1,16 @@ +[DEFAULT] +support-files = + file_bug1367586-followon.html + file_bug1367586-redirect.sjs + file_bug1367586-target.html + file_data.txt + file_disableScript.html + !/js/xpconnect/tests/mochitest/file_empty.html + +[test_bug246699.html] +[test_bug292789.html] +skip-if = os == 'android' +[test_bug423375.html] +[test_bug470804.html] +[test_bug1367586.html] +[test_disallowInheritPrincipal.html] diff --git a/caps/tests/mochitest/resource_test_file.html b/caps/tests/mochitest/resource_test_file.html new file mode 100644 index 0000000000..8201bd70e0 --- /dev/null +++ b/caps/tests/mochitest/resource_test_file.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<html><head><title>resource test file</title></head><body></body></html> diff --git a/caps/tests/mochitest/test_addonMayLoad.html b/caps/tests/mochitest/test_addonMayLoad.html new file mode 100644 index 0000000000..abf4596027 --- /dev/null +++ b/caps/tests/mochitest/test_addonMayLoad.html @@ -0,0 +1,95 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1180921 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1180921</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://global/skin"/> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 1180921 **/ + let module = Cu.getGlobalForObject(ChromeUtils.import("resource://gre/modules/Services.jsm", window)); + let ssm = Services.scriptSecurityManager; + + function StubPolicy(id, subdomain) { + /* globals MatchPatternSet */ + return new module.WebExtensionPolicy({ + id, + mozExtensionHostname: id, + baseURL: `file:///{id}`, + + allowedOrigins: new MatchPatternSet([`*://${subdomain}.example.org/*`]), + localizeCallback(string) {}, + }); + } + + /* globals WebExtensionPolicy */ + let policyA = StubPolicy("addona", "test1"); + let policyB = StubPolicy("addonb", "test2"); + policyA.active = true; + policyB.active = true; + + SimpleTest.waitForExplicitFinish(); + SimpleTest.registerCleanupFunction(function() { + policyA.active = false; + policyB.active = false; + }); + + function tryLoad(sb, uri) { + let p = new Promise(function(resolve, reject) { + Cu.exportFunction(resolve, sb, { defineAs: "finish" }); + Cu.exportFunction(reject, sb, { defineAs: "error" }); + sb.eval("try { (function () { " + + " var xhr = new XMLHttpRequest();" + + " xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { finish(xhr.status == 200); } };" + + " xhr.open('GET', '" + uri + "', true);" + + " xhr.send();" + + "})() } catch (e) { error(e); }"); + }); + return p; + } + + let addonA = new Cu.Sandbox(ssm.createContentPrincipal(Services.io.newURI("moz-extension://addonA/"), {}), + {wantGlobalProperties: ["XMLHttpRequest"]}); + let addonB = new Cu.Sandbox(ssm.createContentPrincipal(Services.io.newURI("moz-extension://addonB/"), {}), + {wantGlobalProperties: ["XMLHttpRequest"]}); + + function uriForDomain(d) { return d + "/tests/caps/tests/mochitest/file_data.txt"; } + + tryLoad(addonA, uriForDomain("http://test4.example.org")) + .then(function(success) { + ok(!success, "cross-origin load should fail for addon A"); + return tryLoad(addonA, uriForDomain("http://test1.example.org")); + }).then(function(success) { + ok(success, "allowlisted cross-origin load of test1 should succeed for addon A"); + return tryLoad(addonB, uriForDomain("http://test1.example.org")); + }).then(function(success) { + ok(!success, "non-allowlisted cross-origin load of test1 should fail for addon B"); + return tryLoad(addonB, uriForDomain("http://test2.example.org")); + }).then(function(success) { + ok(success, "allowlisted cross-origin load of test2 should succeed for addon B"); + return tryLoad(addonA, uriForDomain("http://test2.example.org")); + }).then(function(success) { + ok(!success, "non-allowlisted cross-origin load of test2 should fail for addon A"); + SimpleTest.finish(); + }, function(e) { + ok(false, "Rejected promise chain: " + e); + SimpleTest.finish(); + }); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1180921">Mozilla Bug 1180921</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/caps/tests/mochitest/test_bug1367586.html b/caps/tests/mochitest/test_bug1367586.html new file mode 100644 index 0000000000..d95693c94d --- /dev/null +++ b/caps/tests/mochitest/test_bug1367586.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1367586 +--> +<head> + <title>Test for Bug 1367586</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe id="load-frame"></iframe> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var frm = document.getElementById("load-frame"); +var step = 0; + +window.addEventListener("load", () => { + frm.contentWindow.location = "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-redirect.sjs"; + frm.addEventListener("load", function() { + ++step; + SimpleTest.executeSoon((function(_step, _frm) { + switch (_step) { + case 1: + is(_frm.contentWindow.location.href, "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html", + "Redirected to the expected target in step 1"); + _frm.contentWindow.location = "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-followon.html"; + break; + case 2: + is(_frm.contentWindow.location.href, "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-followon.html", + "Navigated to the expected URL in step 2"); + _frm.contentWindow.history.back(); + break; + case 3: + is(_frm.contentWindow.location.href, "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html", + "Seeing the correct URL when navigating back in step 3"); + SimpleTest.finish(); + break; + } + }).bind(window, step, frm)); + }); +}); + +</script> +</pre> +</body> +</html> diff --git a/caps/tests/mochitest/test_bug246699.html b/caps/tests/mochitest/test_bug246699.html new file mode 100644 index 0000000000..13c92e3743 --- /dev/null +++ b/caps/tests/mochitest/test_bug246699.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=246699 +--> +<head> + <title>Test for Bug 246699</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=246699">Mozilla Bug 246699</a> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe id="load-frame"></iframe> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** + ** Test for Bug 246699 + ** (should produce stack information for caps errors) + **/ +function isError(e) { + return e.constructor.name === "Error" || e.constructor.name === "TypeError"; +} + +function hasStack(e) { + return isError(e) && /inciteCaps/.test(e.stack); +} + +function inciteCaps(f) { + try { + f(); + return "operation succeeded"; + } catch (e) { + if (hasStack(e)) { + return "denied-stack"; + } + return "unexpected: " + e; + } +} + +function tryChromeLoad() { + window.frames[0].location = "chrome://global/content/mozilla.html"; +} + +function tryComponentsClasses() { + return SpecialPowers.unwrap(SpecialPowers.Cc)["@mozilla.org/dummy;1"]; +} + + +is(inciteCaps(tryChromeLoad), "denied-stack", + "should get stack for content-loading-chrome rejection"); +is(inciteCaps(tryComponentsClasses), "denied-stack", + "should get stack for SpecialPowers.Components.classes rejection"); +</script> +</pre> +</body> +</html> diff --git a/caps/tests/mochitest/test_bug292789.html b/caps/tests/mochitest/test_bug292789.html new file mode 100644 index 0000000000..855ca3bce2 --- /dev/null +++ b/caps/tests/mochitest/test_bug292789.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=292789 +--> +<head> + <title>Test for Bug 292789</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=292789">Mozilla Bug 292789</a> +<p id="display"></p> +<div id="content" style="display: none"> + <script src="chrome://global/content/treeUtils.js"></script> + <script type="application/javascript" src="chrome://mozapps/content/extensions/blocklist.js"></script> + <script id="resjs" type="application/javascript"></script> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** Test for Bug 292789 + ** + ** Selectively allow access to allowlisted chrome packages + ** even for ALLOW_CHROME mechanisms (<script>, <img> etc) + **/ + +/* import-globals-from ../../../toolkit/content/treeUtils.js */ + +SimpleTest.waitForExplicitFinish(); + +let ChromeUtils = { + import() { return {}; }, +}; + +/** <script src=""> test **/ +function testScriptSrc(aCallback) { + is(typeof gTreeUtils.sort, "function", + "content can still load <script> from chrome://global"); + is(typeof Startup, "undefined", + "content should not be able to load <script> from chrome://mozapps"); + + /** make sure the last one didn't pass because someone + ** moved the resource + **/ + var resjs = document.getElementById("resjs"); + resjs.onload = scriptOnload; + resjs.src = "resource://gre/chrome/toolkit/content/mozapps/extensions/blocklist.js"; + document.getElementById("content").appendChild(resjs); + + function scriptOnload() { + is(typeof init, "function", + "blocklist.js has not moved unexpectedly"); + + // trigger the callback + if (aCallback) + aCallback(); + } +} + +/** <img src=""> tests **/ +var img_global = "chrome://global/skin/media/error.png"; +var img_mozapps = "chrome://mozapps/skin/extensions/extensionGeneric.svg"; +var res_mozapps = "resource://gre/chrome/toolkit/skin/classic/mozapps/extensions/extensionGeneric.svg"; + +var imgTests = [[img_global, "success"], + [img_mozapps, "fail"], + [res_mozapps, "success"]]; + +var curImgTest = 0; + +function runImgTest() { + var test = imgTests[curImgTest++]; + var callback = curImgTest == imgTests.length ? finishTest : runImgTest; + loadImage(test[0], test[1], callback); +} + +function finishTest() { + SimpleTest.finish(); +} + +function fail(event) { + is("fail", event.target.expected, + "content should not be allowed to load " + event.target.src); + if (event.target.callback) + event.target.callback(); +} + +function success(event) { + is("success", event.target.expected, + "content should be able to load " + event.target.src); + if (event.target.callback) + event.target.callback(); +} + +function loadImage(uri, expect, callback) { + var img = document.createElement("img"); + img.onerror = fail; + img.onload = success; + img.expected = expect; + img.callback = callback; + img.src = uri; + // document.getElementById("content").appendChild(img); +} + +// Start off the script src test, and have it start the img tests when complete. +// Temporarily allow content to access all resource:// URIs. +SpecialPowers.pushPrefEnv({ + set: [ + ["security.all_resource_uri_content_accessible", true], + ], +}, () => testScriptSrc(runImgTest)); +</script> +</pre> +</body> +</html> diff --git a/caps/tests/mochitest/test_bug423375.html b/caps/tests/mochitest/test_bug423375.html new file mode 100644 index 0000000000..3d73b0b874 --- /dev/null +++ b/caps/tests/mochitest/test_bug423375.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=423375 +--> +<head> + <title>Test for Bug 423375</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=423375">Mozilla Bug 423375</a> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe id="load-frame"></iframe> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** + ** Test for Bug 423375 + ** (content shouldn't be able to load chrome: or resource:) + **/ +function tryLoad(url) { + try { + window.frames[0].location = url; + return "loaded"; + } catch (e) { + if (/Access.*denied/.test(String(e))) { + return "denied"; + } + return "unexpected: " + e; + } +} + +is(tryLoad("chrome://global/content/mozilla.html"), "denied", + "content should have been prevented from loading chrome: URL"); +is(tryLoad("resource://gre-resources/html.css"), "denied", + "content should have been prevented from loading resource: URL"); +</script> +</pre> +</body> +</html> diff --git a/caps/tests/mochitest/test_bug470804.html b/caps/tests/mochitest/test_bug470804.html new file mode 100644 index 0000000000..a2d6c7e002 --- /dev/null +++ b/caps/tests/mochitest/test_bug470804.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=470804 +--> +<head> + <title>Test for Bug 470804</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=470804">Mozilla Bug 470804</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 470804 + Passing a null targetURL to checkLoadURIWithPrincipal shouldn't crash + **/ + +const nsIScriptSecurityManager = SpecialPowers.Ci.nsIScriptSecurityManager; +var secMan = SpecialPowers.Services.scriptSecurityManager; +var principal = SpecialPowers.wrap(document).nodePrincipal; +isnot(principal, undefined, "Should have a principal"); +isnot(principal, null, "Should have a non-null principal"); +is(principal.isSystemPrincipal, false, + "Shouldn't have system principal here"); +try { + secMan.checkLoadURIWithPrincipal(principal, null, + nsIScriptSecurityManager.STANDARD); +} catch (e) { + // throwing is fine, it's just crashing that's bad +} +ok(true, "Survival: we should get here without crashing"); +</script> +</pre> +</body> +</html> diff --git a/caps/tests/mochitest/test_bug995943.xhtml b/caps/tests/mochitest/test_bug995943.xhtml new file mode 100644 index 0000000000..fba1596ac9 --- /dev/null +++ b/caps/tests/mochitest/test_bug995943.xhtml @@ -0,0 +1,112 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=995943 +--> +<window title="Mozilla Bug 995943" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=995943" + target="_blank">Mozilla Bug 995943</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); + function debug(msg) { info(msg); } + + /** Test for CAPS file:// URI prefs. **/ + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestCompleteLog(); + if (navigator.userAgent.includes("Mac OS X 10.10")) + SimpleTest.expectAssertions(5, 11); // See bug 1067022, 1307988 + else if (Services.appinfo.OS == "WINNT") + SimpleTest.expectAssertions(0, 1); // See bug 1067022 + else + SimpleTest.expectAssertions(0, 9); // See bug 1305241, 1145314 + + var rootdir = Services.appinfo.OS == "WINNT" ? "file:///C:" : "file:///"; + + function checkLoadFileURI(domain, shouldLoad) { + debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad); + return new Promise(function(resolve, reject) { + $('ifr').addEventListener('load', function l1() { + debug("Invoked l1 for " + domain); + $('ifr').removeEventListener('load', l1); + function l2() { + debug("Invoked l2 for " + domain); + $('ifr').removeEventListener('load', l2); + ok(shouldLoad, "Successfully loaded file:// URI for domain: " + domain); + resolve(); + } + $('ifr').addEventListener('load', l2); + try { + window[0].wrappedJSObject.location = rootdir; + debug("Successfully navigated for " + domain); + } catch (e) { + ok(!shouldLoad && /denied|insecure/.test(e), + "Prevented loading of file:// URI for domain: " + domain + " - " + e); + $('ifr').removeEventListener('load', l2); + resolve(); + } + }); + let targetURI = domain + '/tests/js/xpconnect/tests/mochitest/file_empty.html'; + debug("Navigating iframe to " + targetURI); + $('ifr').contentWindow.location = targetURI; + }); + } + + function pushPrefs(prefs) { + return SpecialPowers.pushPrefEnv({ set: prefs }); + } + + function popPrefs() { + return new Promise(function(resolve) { SpecialPowers.popPrefEnv(resolve); }); + } + + var gGoCount = 0; + function go() { + debug("Invoking go for window with id: " + window.windowGlobalChild.innerWindowId); + is(++gGoCount, 1, "Should only call go once!"); + checkLoadFileURI('http://example.com', false).then( + pushPrefs.bind(null, [['capability.policy.policynames', ' somepolicy '], + ['capability.policy.somepolicy.checkloaduri.enabled', 'AlLAcCeSs'], + ['capability.policy.somepolicy.sites', 'http://example.com']])) + .then(checkLoadFileURI.bind(null, 'http://example.com', true)) + .then(popPrefs) + .then(checkLoadFileURI.bind(null, 'http://example.com', false)) + .then( + pushPrefs.bind(null, [['capability.policy.policynames', ',somepolicy, someotherpolicy, '], + ['capability.policy.somepolicy.checkloaduri.enabled', 'allaccess'], + ['capability.policy.someotherpolicy.checkloaduri.enabled', 'nope'], + ['capability.policy.somepolicy.sites', ' http://example.org test1.example.com https://test2.example.com '], + ['capability.policy.someotherpolicy.sites', 'http://example.net ']])) + .then(checkLoadFileURI.bind(null, 'http://example.org', true)) + .then(checkLoadFileURI.bind(null, 'http://test2.example.com', false)) + .then(checkLoadFileURI.bind(null, 'https://test2.example.com', true)) + .then(checkLoadFileURI.bind(null, 'http://sub1.test2.example.com', false)) + .then(checkLoadFileURI.bind(null, 'https://sub1.test2.example.com', true)) + .then(checkLoadFileURI.bind(null, 'http://example.net', false)) + .then(checkLoadFileURI.bind(null, 'http://test1.example.com', true)) + .then(checkLoadFileURI.bind(null, 'https://test1.example.com', true)) + .then(checkLoadFileURI.bind(null, 'http://sub1.test1.example.com', true)) + .then(checkLoadFileURI.bind(null, 'https://sub1.test1.example.com', true)) + .then(pushPrefs.bind(null, [['capability.policy.someotherpolicy.checkloaduri.enabled', 'allAccess']])) + .then(checkLoadFileURI.bind(null, 'http://example.net', true)) + .then(popPrefs) + .then(popPrefs) + .then(checkLoadFileURI.bind(null, 'http://example.net', false)) + .then(SimpleTest.finish.bind(SimpleTest)); + + } + addLoadEvent(go); + + ]]> + </script> + <iframe id="ifr" type="content" /> +</window> diff --git a/caps/tests/mochitest/test_disableScript.xhtml b/caps/tests/mochitest/test_disableScript.xhtml new file mode 100644 index 0000000000..fe07f7fe6b --- /dev/null +++ b/caps/tests/mochitest/test_disableScript.xhtml @@ -0,0 +1,331 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=840488 +--> +<window title="Mozilla Bug 840488" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=840488" + target="_blank">Mozilla Bug 840488</a> + </body> + + <iframe id="root" name="root" type="content"/> + <iframe id="chromeFrame" name="chromeFrame" type="content"/> + + <!-- test code goes here --> + <script type="application/javascript"> + /* eslint-disable mozilla/no-useless-parameters, no-redeclare, no-undef */ + <![CDATA[ + + /** Test for all the different ways that script can be disabled for a given global. **/ + + SimpleTest.waitForExplicitFinish(); + const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); + const ssm = Services.scriptSecurityManager; + function makeURI(uri) { return Services.io.newURI(uri); } + const path = "/tests/caps/tests/mochitest/file_disableScript.html"; + const uri = "http://www.example.com" + path; + var rootFrame = document.getElementById('root'); + var chromeFrame = document.getElementById('chromeFrame'); + navigateFrame(rootFrame, uri + "?name=rootframe").then(function() { + navigateFrame(chromeFrame, "file_disableScript.html").then(go); + }); + + function navigateFrame(ifr, src) { + return new Promise(resolve => { + function onload() { + ifr.removeEventListener('load', onload); + resolve(); + } + ifr.addEventListener('load', onload, false); + ifr.setAttribute('src', src); + }); + } + + function navigateBack(ifr) { + return new Promise(resolve => { + + // pageshow events don't fire on the iframe element, so we need to use the + // chrome event handler for the docshell. + var browser = ifr.contentWindow.docShell.chromeEventHandler; + function onpageshow(evt) { + info("Navigated back. Persisted: " + evt.persisted); + browser.removeEventListener('pageshow', onpageshow); + resolve(); + } + browser.addEventListener('pageshow', onpageshow, false); + ifr.contentWindow.history.back(); + }); + } + + function addFrame(parentWin, name, expectOnload) { + let ifr = parentWin.document.createElement('iframe'); + parentWin.document.body.appendChild(ifr); + ifr.setAttribute('name', name); + return new Promise(resolve => { + // We need to append 'name' to avoid running afoul of recursive frame detection. + let frameURI = uri + "?name=" + name; + navigateFrame(ifr, frameURI).then(function() { + is(String(ifr.contentWindow.location), frameURI, "Successful load"); + is(!!ifr.contentWindow.wrappedJSObject.gFiredOnload, expectOnload, + "onload should only fire when scripts are enabled"); + resolve(); + }); + }); + } + + function checkScriptEnabled(win, expectEnabled) { + win.wrappedJSObject.gFiredOnclick = false; + win.document.body.dispatchEvent(new win.Event('click')); + is(win.wrappedJSObject.gFiredOnclick, expectEnabled, "Checking script-enabled for " + win.name + " (" + win.location + ")"); + } + + function setScriptEnabledForDocShell(win, enabled) { + win.docShell.allowJavascript = enabled; + } + + function testList(expectEnabled, win, list, idx) { + idx = idx || 0; + return new Promise(resolve => { + let target = list[idx] + path; + info("Testing scriptability for: " + target + ". expecting " + expectEnabled); + navigateFrame(win.frameElement, target).then(function() { + checkScriptEnabled(win, expectEnabled); + if (idx == list.length - 1) + resolve(); + else + testList(expectEnabled, win, list, idx + 1).then(function() { resolve(); }); + }); + }); + } + + function testDomainPolicy(defaultScriptability, exceptions, superExceptions, + exempt, notExempt, set, superSet, win) { + // Populate our sets. + for (var e of exceptions) + set.add(makeURI(e)); + for (var e of superExceptions) + superSet.add(makeURI(e)); + + return testList(defaultScriptability, win, notExempt).then(function() { + return testList(!defaultScriptability, win, exempt); + }); + } + + function setScriptEnabledForBrowser(enabled) { + var prefname = "javascript.enabled"; + Services.prefs.setBoolPref(prefname, enabled); + } + + function reloadFrame(frame) { + return new Promise(resolve => { + frame.addEventListener('load', function onload() { + resolve(); + frame.removeEventListener('load', onload); + }, false); + frame.contentWindow.location.reload(true); + }); + } + + function go() { + var rootWin = rootFrame.contentWindow; + var chromeWin = chromeFrame.contentWindow; + + // Test simple docshell enable/disable. + checkScriptEnabled(rootWin, true); + setScriptEnabledForDocShell(rootWin, false); + checkScriptEnabled(rootWin, false); + setScriptEnabledForDocShell(rootWin, true); + checkScriptEnabled(rootWin, true); + + // Privileged frames are immune to docshell flags. + ok(chromeWin.document.nodePrincipal.isSystemPrincipal, "Sanity check for System Principal"); + setScriptEnabledForDocShell(chromeWin, false); + checkScriptEnabled(chromeWin, true); + setScriptEnabledForDocShell(chromeWin, true); + + // Play around with the docshell tree and make sure everything works as + // we expect. + addFrame(rootWin, 'parent', true).then(function() { + checkScriptEnabled(rootWin[0], true); + return addFrame(rootWin[0], 'childA', true); + }).then(function() { + checkScriptEnabled(rootWin[0][0], true); + setScriptEnabledForDocShell(rootWin[0], false); + checkScriptEnabled(rootWin, true); + checkScriptEnabled(rootWin[0], false); + checkScriptEnabled(rootWin[0][0], false); + return addFrame(rootWin[0], 'childB', false); + }).then(function() { + checkScriptEnabled(rootWin[0][1], false); + setScriptEnabledForDocShell(rootWin[0][0], false); + setScriptEnabledForDocShell(rootWin[0], true); + checkScriptEnabled(rootWin[0], true); + checkScriptEnabled(rootWin[0][0], false); + setScriptEnabledForDocShell(rootWin[0][0], true); + + // Flags are inherited from the parent docshell at attach time. Note that + // the flag itself is inherited, regardless of whether or not scripts are + // currently allowed on the parent (which could depend on the parent's + // parent). Check that. + checkScriptEnabled(rootWin[0][1], false); + setScriptEnabledForDocShell(rootWin[0], false); + setScriptEnabledForDocShell(rootWin[0][1], true); + return addFrame(rootWin[0][1], 'grandchild', false); + }).then(function() { + checkScriptEnabled(rootWin[0], false); + checkScriptEnabled(rootWin[0][1], false); + checkScriptEnabled(rootWin[0][1][0], false); + setScriptEnabledForDocShell(rootWin[0], true); + checkScriptEnabled(rootWin[0], true); + checkScriptEnabled(rootWin[0][1], true); + checkScriptEnabled(rootWin[0][1][0], true); + + // Try navigating two frames, then munging docshell scriptability, then + // pulling the frames out of the bfcache to make sure that flags are + // properly propagated to inactive inner windows. We do this both for an + // 'own' docshell, as well as for an ancestor docshell. + return navigateFrame(rootWin[0][0].frameElement, rootWin[0][0].location + '-navigated'); + }).then(function() { return navigateFrame(rootWin[0][1][0].frameElement, rootWin[0][1][0].location + '-navigated'); }) + .then(function() { + checkScriptEnabled(rootWin[0][0], true); + checkScriptEnabled(rootWin[0][1][0], true); + setScriptEnabledForDocShell(rootWin[0][0], false); + setScriptEnabledForDocShell(rootWin[0][1], false); + checkScriptEnabled(rootWin[0][0], false); + checkScriptEnabled(rootWin[0][1][0], false); + return navigateBack(rootWin[0][0].frameElement); + }).then(function() { return navigateBack(rootWin[0][1][0].frameElement); }) + .then(function() { + checkScriptEnabled(rootWin[0][0], false); + checkScriptEnabled(rootWin[0][1][0], false); + + // Disable JS via the global pref pref. This is only guaranteed to have an effect + // for subsequent loads. + setScriptEnabledForBrowser(false); + return reloadFrame(rootFrame); + }).then(function() { + checkScriptEnabled(rootWin, false); + checkScriptEnabled(chromeWin, true); + setScriptEnabledForBrowser(true); + return reloadFrame(rootFrame); + }).then(function() { + checkScriptEnabled(rootWin, true); + + // Play around with dynamically blocking script for a given global. + // This takes effect immediately. + Cu.blockScriptForGlobal(rootWin); + Cu.blockScriptForGlobal(rootWin); + Cu.unblockScriptForGlobal(rootWin); + checkScriptEnabled(rootWin, false); + Cu.unblockScriptForGlobal(rootWin); + checkScriptEnabled(rootWin, true); + Cu.blockScriptForGlobal(rootWin); + try { + Cu.blockScriptForGlobal(chromeWin); + ok(false, "Should have thrown"); + } catch (e) { + ok(/may not be disabled/.test(e), + "Shouldn't be able to programmatically block script for system globals"); + } + return reloadFrame(rootFrame); + }).then(function() { + checkScriptEnabled(rootWin, true); + + // Test system-wide domain policy. This only takes effect for subsequently- + // loaded globals. + + // Check the basic semantics of the sets. + is(ssm.domainPolicyActive, false, "not enabled"); + window.policy = ssm.activateDomainPolicy(); + ok(policy instanceof Ci.nsIDomainPolicy, "Got a policy"); + try { + ssm.activateDomainPolicy(); + ok(false, "Should have thrown"); + } catch (e) { + ok(true, "can't have two live domain policies"); + } + var sbRef = policy.superBlocklist; + isnot(sbRef, null, "superBlocklist non-null"); + ok(!sbRef.contains(makeURI('http://www.example.com'))); + sbRef.add(makeURI('http://www.example.com/foopy')); + ok(sbRef.contains(makeURI('http://www.example.com'))); + sbRef.remove(makeURI('http://www.example.com')); + ok(!sbRef.contains(makeURI('http://www.example.com'))); + sbRef.add(makeURI('http://www.example.com/foopy/this.that/')); + ok(sbRef.contains(makeURI('http://www.example.com/baz'))); + ok(!sbRef.contains(makeURI('https://www.example.com'))); + ok(!sbRef.contains(makeURI('https://www.example.com:88'))); + ok(!sbRef.contains(makeURI('http://foo.www.example.com'))); + ok(sbRef.containsSuperDomain(makeURI('http://foo.www.example.com'))); + ok(sbRef.containsSuperDomain(makeURI('http://foo.bar.www.example.com'))); + ok(!sbRef.containsSuperDomain(makeURI('http://foo.bar.www.exxample.com'))); + ok(!sbRef.containsSuperDomain(makeURI('http://example.com'))); + ok(!sbRef.containsSuperDomain(makeURI('http://com/this.that/'))); + ok(!sbRef.containsSuperDomain(makeURI('https://foo.www.example.com'))); + ok(sbRef.contains(makeURI('http://www.example.com'))); + policy.deactivate(); + is(ssm.domainPolicyActive, false, "back to inactive"); + ok(!sbRef.contains(makeURI('http://www.example.com')), + "Disabling domain policy clears the set"); + policy = ssm.activateDomainPolicy(); + ok(policy.superBlocklist); + isnot(sbRef, policy.superBlocklist, "Mint new sets each time!"); + policy.deactivate(); + is(policy.blocklist, null, "blocklist nulled out"); + policy = ssm.activateDomainPolicy(); + isnot(policy.blocklist, null, "non-null again"); + isnot(policy.blocklist, sbRef, "freshly minted"); + policy.deactivate(); + + // + // Now, create and apply a mock-policy. We check the same policy both as + // a blocklist and as a allowlist. + // + + window.testPolicy = { + // The policy. + exceptions: ['http://test1.example.com', 'http://example.com'], + superExceptions: ['http://test2.example.org', 'https://test1.example.com'], + + // The testcases. + exempt: ['http://test1.example.com', 'http://example.com', + 'http://test2.example.org', 'http://sub1.test2.example.org', + 'https://sub1.test1.example.com'], + + notExempt: ['http://test2.example.com', 'http://sub1.test1.example.com', + 'http://www.example.com', 'https://test2.example.com', + 'https://example.com', 'http://test1.example.org'], + }; + + policy = ssm.activateDomainPolicy(); + info("Testing Blocklist-style Domain Policy"); + return testDomainPolicy(true, testPolicy.exceptions, + testPolicy.superExceptions, testPolicy.exempt, + testPolicy.notExempt, policy.blocklist, + policy.superBlocklist, rootWin); + }).then(function() { + policy.deactivate(); + policy = ssm.activateDomainPolicy(); + info("Testing Allowlist-style Domain Policy"); + setScriptEnabledForBrowser(false); + return testDomainPolicy(false, testPolicy.exceptions, + testPolicy.superExceptions, testPolicy.exempt, + testPolicy.notExempt, policy.allowlist, + policy.superAllowlist, rootWin); + }).then(function() { + setScriptEnabledForBrowser(true); + policy.deactivate(); + + SimpleTest.finish(); + }); + } + + ]]> + </script> +</window> diff --git a/caps/tests/mochitest/test_disallowInheritPrincipal.html b/caps/tests/mochitest/test_disallowInheritPrincipal.html new file mode 100644 index 0000000000..8f5a0b9353 --- /dev/null +++ b/caps/tests/mochitest/test_disallowInheritPrincipal.html @@ -0,0 +1,58 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=732413 +--> +<head> + <title>Test for Bug 732413</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=732413">Mozilla Bug 732413</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 732413 + Passing DISALLOW_INHERIT_PRINCIPAL flag should be effective even if + aPrincipal is the system principal. + **/ + +const nsIScriptSecurityManager = SpecialPowers.Ci.nsIScriptSecurityManager; +var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(nsIScriptSecurityManager); +var sysPrincipal = secMan.getSystemPrincipal(); +isnot(sysPrincipal, undefined, "Should have a principal"); +isnot(sysPrincipal, null, "Should have a non-null principal"); +is(sysPrincipal.isSystemPrincipal, true, + "Should have system principal here"); + + +var inheritingURI = SpecialPowers.Services.io.newURI("javascript:1+1"); + +// First try a normal call to checkLoadURIWithPrincipal +try { + secMan.checkLoadURIWithPrincipal(sysPrincipal, inheritingURI, + nsIScriptSecurityManager.STANDARD); + ok(true, "checkLoadURI allowed the load"); +} catch (e) { + ok(false, "checkLoadURI failed unexpectedly: " + e); +} + +// Now call checkLoadURIWithPrincipal with DISALLOW_INHERIT_PRINCIPAL +try { + secMan.checkLoadURIWithPrincipal(sysPrincipal, inheritingURI, + nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); + ok(false, "checkLoadURI allowed the load unexpectedly"); +} catch (e) { + ok(true, "checkLoadURI prevented load of principal-inheriting URI"); +} + +</script> +</pre> +</body> +</html> diff --git a/caps/tests/unit/test_ipv6_host_literal.js b/caps/tests/unit/test_ipv6_host_literal.js new file mode 100644 index 0000000000..0539e17de5 --- /dev/null +++ b/caps/tests/unit/test_ipv6_host_literal.js @@ -0,0 +1,39 @@ +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +var ssm = Services.scriptSecurityManager; +function makeURI(uri) { + return Services.io.newURI(uri); +} + +function testIPV6Host(aHost, aExpected) { + var ipv6Host = ssm.createContentPrincipal(makeURI(aHost), {}); + Assert.equal(ipv6Host.origin, aExpected); +} + +function run_test() { + testIPV6Host("http://[::1]/", "http://[::1]"); + + testIPV6Host( + "http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/", + "http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]" + ); + + testIPV6Host( + "http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443/", + "http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443" + ); + + testIPV6Host( + "http://[2001:db8:85a3::1319:8a2e:370:7348]/", + "http://[2001:db8:85a3:0:1319:8a2e:370:7348]" + ); + + testIPV6Host( + "http://[20D1:0000:3238:DFE1:63:0000:0000:FEFB]/", + "http://[20d1:0:3238:dfe1:63::fefb]" + ); + + testIPV6Host( + "http://[20D1:0:3238:DFE1:63::FEFB]/", + "http://[20d1:0:3238:dfe1:63::fefb]" + ); +} diff --git a/caps/tests/unit/test_origin.js b/caps/tests/unit/test_origin.js new file mode 100644 index 0000000000..8407020cba --- /dev/null +++ b/caps/tests/unit/test_origin.js @@ -0,0 +1,323 @@ +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +var ssm = Services.scriptSecurityManager; +function makeURI(uri) { + return Services.io.newURI(uri); +} + +function checkThrows(f) { + var threw = false; + try { + f(); + } catch (e) { + threw = true; + } + Assert.ok(threw); +} + +function checkCrossOrigin(a, b) { + Assert.ok(!a.equals(b)); + Assert.ok(!a.equalsConsideringDomain(b)); + Assert.ok(!a.subsumes(b)); + Assert.ok(!a.subsumesConsideringDomain(b)); + Assert.ok(!b.subsumes(a)); + Assert.ok(!b.subsumesConsideringDomain(a)); +} + +function checkOriginAttributes(prin, attrs, suffix) { + attrs = attrs || {}; + Assert.equal( + prin.originAttributes.inIsolatedMozBrowser, + attrs.inIsolatedMozBrowser || false + ); + Assert.equal(prin.originSuffix, suffix || ""); + Assert.equal(ChromeUtils.originAttributesToSuffix(attrs), suffix || ""); + Assert.ok( + ChromeUtils.originAttributesMatchPattern(prin.originAttributes, attrs) + ); + if (!prin.isNullPrincipal && !prin.origin.startsWith("[")) { + Assert.ok(ssm.createContentPrincipalFromOrigin(prin.origin).equals(prin)); + } else { + checkThrows(() => ssm.createContentPrincipalFromOrigin(prin.origin)); + } +} + +function checkSandboxOriginAttributes(arr, attrs, options) { + options = options || {}; + var sandbox = Cu.Sandbox(arr, options); + checkOriginAttributes( + Cu.getObjectPrincipal(sandbox), + attrs, + ChromeUtils.originAttributesToSuffix(attrs) + ); +} + +// utility function useful for debugging +function printAttrs(name, attrs) { + info( + name + + " {\n" + + "\tuserContextId: " + + attrs.userContextId + + ",\n" + + "\tinIsolatedMozBrowser: " + + attrs.inIsolatedMozBrowser + + ",\n" + + "\tprivateBrowsingId: '" + + attrs.privateBrowsingId + + "',\n" + + "\tfirstPartyDomain: '" + + attrs.firstPartyDomain + + "'\n}" + ); +} + +function checkValues(attrs, values) { + values = values || {}; + // printAttrs("attrs", attrs); + // printAttrs("values", values); + Assert.equal(attrs.userContextId, values.userContextId || 0); + Assert.equal( + attrs.inIsolatedMozBrowser, + values.inIsolatedMozBrowser || false + ); + Assert.equal(attrs.privateBrowsingId, values.privateBrowsingId || ""); + Assert.equal(attrs.firstPartyDomain, values.firstPartyDomain || ""); +} + +function run_test() { + // Attributeless origins. + Assert.equal(ssm.getSystemPrincipal().origin, "[System Principal]"); + checkOriginAttributes(ssm.getSystemPrincipal()); + var exampleOrg = ssm.createContentPrincipal( + makeURI("http://example.org"), + {} + ); + Assert.equal(exampleOrg.origin, "http://example.org"); + checkOriginAttributes(exampleOrg); + var exampleCom = ssm.createContentPrincipal( + makeURI("https://www.example.com:123"), + {} + ); + Assert.equal(exampleCom.origin, "https://www.example.com:123"); + checkOriginAttributes(exampleCom); + var nullPrin = Cu.getObjectPrincipal(new Cu.Sandbox(null)); + Assert.ok( + /^moz-nullprincipal:\{([0-9]|[a-z]|\-){36}\}$/.test(nullPrin.origin) + ); + checkOriginAttributes(nullPrin); + var ipv6Prin = ssm.createContentPrincipal( + makeURI("https://[2001:db8::ff00:42:8329]:123"), + {} + ); + Assert.equal(ipv6Prin.origin, "https://[2001:db8::ff00:42:8329]:123"); + checkOriginAttributes(ipv6Prin); + var ipv6NPPrin = ssm.createContentPrincipal( + makeURI("https://[2001:db8::ff00:42:8329]"), + {} + ); + Assert.equal(ipv6NPPrin.origin, "https://[2001:db8::ff00:42:8329]"); + checkOriginAttributes(ipv6NPPrin); + var ep = Cu.getObjectPrincipal( + Cu.Sandbox([exampleCom, nullPrin, exampleOrg]) + ); + checkOriginAttributes(ep); + checkCrossOrigin(exampleCom, exampleOrg); + checkCrossOrigin(exampleOrg, nullPrin); + + // nsEP origins should be in lexical order. + Assert.equal( + ep.origin, + `[Expanded Principal [${exampleOrg.origin}, ${exampleCom.origin}, ${nullPrin.origin}]]` + ); + + // Make sure createContentPrincipal does what the rest of gecko does. + Assert.ok( + exampleOrg.equals( + Cu.getObjectPrincipal(new Cu.Sandbox("http://example.org")) + ) + ); + + // + // Test origin attributes. + // + + // Just browser. + var exampleOrg_browser = ssm.createContentPrincipal( + makeURI("http://example.org"), + { inIsolatedMozBrowser: true } + ); + var nullPrin_browser = ssm.createNullPrincipal({ + inIsolatedMozBrowser: true, + }); + checkOriginAttributes( + exampleOrg_browser, + { inIsolatedMozBrowser: true }, + "^inBrowser=1" + ); + checkOriginAttributes( + nullPrin_browser, + { inIsolatedMozBrowser: true }, + "^inBrowser=1" + ); + Assert.equal(exampleOrg_browser.origin, "http://example.org^inBrowser=1"); + + // First party Uri + var exampleOrg_firstPartyDomain = ssm.createContentPrincipal( + makeURI("http://example.org"), + { firstPartyDomain: "example.org" } + ); + checkOriginAttributes( + exampleOrg_firstPartyDomain, + { firstPartyDomain: "example.org" }, + "^firstPartyDomain=example.org" + ); + Assert.equal( + exampleOrg_firstPartyDomain.origin, + "http://example.org^firstPartyDomain=example.org" + ); + + // Just userContext. + var exampleOrg_userContext = ssm.createContentPrincipal( + makeURI("http://example.org"), + { userContextId: 42 } + ); + checkOriginAttributes( + exampleOrg_userContext, + { userContextId: 42 }, + "^userContextId=42" + ); + Assert.equal( + exampleOrg_userContext.origin, + "http://example.org^userContextId=42" + ); + + checkSandboxOriginAttributes(null, {}); + checkSandboxOriginAttributes("http://example.org", {}); + checkSandboxOriginAttributes( + "http://example.org", + {}, + { originAttributes: {} } + ); + checkSandboxOriginAttributes(["http://example.org"], {}); + checkSandboxOriginAttributes( + ["http://example.org"], + {}, + { originAttributes: {} } + ); + + // Check that all of the above are cross-origin. + checkCrossOrigin(exampleOrg_browser, nullPrin_browser); + checkCrossOrigin(exampleOrg_firstPartyDomain, exampleOrg); + checkCrossOrigin(exampleOrg_userContext, exampleOrg); + + // Check Principal kinds. + function checkKind(prin, kind) { + Assert.equal(prin.isNullPrincipal, kind == "nullPrincipal"); + Assert.equal(prin.isContentPrincipal, kind == "contentPrincipal"); + Assert.equal(prin.isExpandedPrincipal, kind == "expandedPrincipal"); + Assert.equal(prin.isSystemPrincipal, kind == "systemPrincipal"); + } + checkKind(ssm.createNullPrincipal({}), "nullPrincipal"); + checkKind( + ssm.createContentPrincipal(makeURI("http://www.example.com"), {}), + "contentPrincipal" + ); + checkKind( + Cu.getObjectPrincipal( + Cu.Sandbox([ + ssm.createContentPrincipal(makeURI("http://www.example.com"), {}), + ]) + ), + "expandedPrincipal" + ); + checkKind(ssm.getSystemPrincipal(), "systemPrincipal"); + + // + // Test Origin Attribute Manipulation + // + + // check that we can create an empty origin attributes dict with default + // members and values. + var emptyAttrs = ChromeUtils.fillNonDefaultOriginAttributes({}); + checkValues(emptyAttrs); + + var uri = "http://example.org"; + var tests = [ + ["", {}], + ["^userContextId=3", { userContextId: 3 }], + ["^inBrowser=1", { inIsolatedMozBrowser: true }], + ["^firstPartyDomain=example.org", { firstPartyDomain: "example.org" }], + ]; + + // check that we can create an origin attributes from an origin properly + tests.forEach(t => { + let attrs = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]); + checkValues(attrs, t[1]); + Assert.equal(ChromeUtils.originAttributesToSuffix(attrs), t[0]); + }); + + // check that we can create an origin attributes from a dict properly + tests.forEach(t => { + let attrs = ChromeUtils.fillNonDefaultOriginAttributes(t[1]); + checkValues(attrs, t[1]); + Assert.equal(ChromeUtils.originAttributesToSuffix(attrs), t[0]); + }); + + // each row in the dflt_tests array has these values: + // [0] - the suffix used to create an origin attribute from + // [1] - the expected result of creating an origin attributes from [0] + // [2] - the expected result after setting userContextId to the default + // [3] - the expected result of creating a suffix from [2] + var dflt_tests = [ + ["", {}, {}, ""], + ["^userContextId=3", { userContextId: 3 }, {}, ""], + ]; + + // check that we can set the userContextId to default properly + dflt_tests.forEach(t => { + let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]); + checkValues(orig, t[1]); + let mod = orig; + mod.userContextId = 0; + checkValues(mod, t[2]); + Assert.equal(ChromeUtils.originAttributesToSuffix(mod), t[3]); + }); + + // each row in the dflt2_tests array has these values: + // [0] - the suffix used to create an origin attribute from + // [1] - the expected result of creating an origin attributes from [0] + // [2] - the expected result after setting firstPartyUri to the default + // [3] - the expected result of creating a suffix from [2] + var dflt2_tests = [ + ["", {}, {}, ""], + ["^firstPartyDomain=foo.com", { firstPartyDomain: "foo.com" }, {}, ""], + ]; + + // check that we can set the userContextId to default properly + dflt2_tests.forEach(t => { + let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]); + checkValues(orig, t[1]); + let mod = orig; + mod.firstPartyDomain = ""; + checkValues(mod, t[2]); + Assert.equal(ChromeUtils.originAttributesToSuffix(mod), t[3]); + }); + + var fileURI = makeURI("file:///foo/bar").QueryInterface(Ci.nsIFileURL); + var fileTests = [ + [true, fileURI.spec], + [false, "file://UNIVERSAL_FILE_URI_ORIGIN"], + ]; + fileTests.forEach(t => { + Services.prefs.setBoolPref("security.fileuri.strict_origin_policy", t[0]); + var filePrin = ssm.createContentPrincipal(fileURI, {}); + Assert.equal(filePrin.origin, t[1]); + }); + Services.prefs.clearUserPref("security.fileuri.strict_origin_policy"); + + var aboutBlankURI = makeURI("about:blank"); + var aboutBlankPrin = ssm.createContentPrincipal(aboutBlankURI, {}); + Assert.ok( + /^moz-nullprincipal:\{([0-9]|[a-z]|\-){36}\}$/.test(aboutBlankPrin.origin) + ); +} diff --git a/caps/tests/unit/test_site_origin.js b/caps/tests/unit/test_site_origin.js new file mode 100644 index 0000000000..6ce2d0dfad --- /dev/null +++ b/caps/tests/unit/test_site_origin.js @@ -0,0 +1,112 @@ +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const scriptSecMan = Services.scriptSecurityManager; + +function makeURI(uri) { + return Services.io.newURI(uri); +} + +// SystemPrincipal checks +let systemPrincipal = scriptSecMan.getSystemPrincipal(); +Assert.ok(systemPrincipal.isSystemPrincipal); +Assert.equal(systemPrincipal.origin, "[System Principal]"); +Assert.equal(systemPrincipal.originNoSuffix, "[System Principal]"); +Assert.equal(systemPrincipal.siteOrigin, "[System Principal]"); +Assert.equal(systemPrincipal.siteOriginNoSuffix, "[System Principal]"); + +// ContentPrincipal checks +let uri1 = Services.io.newURI("http://example.com"); +let prinicpal1 = scriptSecMan.createContentPrincipal(uri1, { + userContextId: 11, +}); +Assert.ok(prinicpal1.isContentPrincipal); +Assert.equal(prinicpal1.origin, "http://example.com^userContextId=11"); +Assert.equal(prinicpal1.originNoSuffix, "http://example.com"); +Assert.equal(prinicpal1.siteOrigin, "http://example.com^userContextId=11"); +Assert.equal(prinicpal1.siteOriginNoSuffix, "http://example.com"); + +let uri2 = Services.io.newURI("http://test1.example.com"); +let prinicpal2 = scriptSecMan.createContentPrincipal(uri2, { + userContextId: 22, +}); +Assert.ok(prinicpal2.isContentPrincipal); +Assert.equal(prinicpal2.origin, "http://test1.example.com^userContextId=22"); +Assert.equal(prinicpal2.originNoSuffix, "http://test1.example.com"); +Assert.equal(prinicpal2.siteOrigin, "http://example.com^userContextId=22"); +Assert.equal(prinicpal2.siteOriginNoSuffix, "http://example.com"); + +let uri3 = Services.io.newURI("https://test1.test2.example.com:5555"); +let prinicpal3 = scriptSecMan.createContentPrincipal(uri3, { + userContextId: 5555, +}); +Assert.ok(prinicpal3.isContentPrincipal); +Assert.equal( + prinicpal3.origin, + "https://test1.test2.example.com:5555^userContextId=5555" +); +Assert.equal(prinicpal3.originNoSuffix, "https://test1.test2.example.com:5555"); +Assert.equal(prinicpal3.siteOrigin, "https://example.com^userContextId=5555"); +Assert.equal(prinicpal3.siteOriginNoSuffix, "https://example.com"); + +let aboutURI = Services.io.newURI("about:preferences"); +let aboutPrincipal = scriptSecMan.createContentPrincipal(aboutURI, { + userContextId: 66, +}); +Assert.ok(aboutPrincipal.isContentPrincipal); +Assert.equal(aboutPrincipal.origin, "about:preferences^userContextId=66"); +Assert.equal(aboutPrincipal.originNoSuffix, "about:preferences"); +Assert.equal(aboutPrincipal.siteOrigin, "about:preferences^userContextId=66"); +Assert.equal(aboutPrincipal.siteOriginNoSuffix, "about:preferences"); + +// NullPrincipal checks +let nullPrincipal = scriptSecMan.createNullPrincipal({ userContextId: 33 }); +Assert.ok(nullPrincipal.isNullPrincipal); +Assert.ok(nullPrincipal.origin.includes("moz-nullprincipal")); +Assert.ok(nullPrincipal.origin.includes("^userContextId=33")); +Assert.ok(nullPrincipal.originNoSuffix.includes("moz-nullprincipal")); +Assert.ok(!nullPrincipal.originNoSuffix.includes("^userContextId=33")); +Assert.ok(nullPrincipal.siteOrigin.includes("moz-nullprincipal")); +Assert.ok(nullPrincipal.siteOrigin.includes("^userContextId=33")); +Assert.ok(nullPrincipal.siteOriginNoSuffix.includes("moz-nullprincipal")); +Assert.ok(!nullPrincipal.siteOriginNoSuffix.includes("^userContextId=33")); + +// ExpandedPrincipal checks +let expandedPrincipal = Cu.getObjectPrincipal(Cu.Sandbox([prinicpal2])); +Assert.ok(expandedPrincipal.isExpandedPrincipal); +Assert.equal( + expandedPrincipal.origin, + "[Expanded Principal [http://test1.example.com^userContextId=22]]^userContextId=22" +); +Assert.equal( + expandedPrincipal.originNoSuffix, + "[Expanded Principal [http://test1.example.com^userContextId=22]]" +); +Assert.equal( + expandedPrincipal.siteOrigin, + "[Expanded Principal [http://test1.example.com^userContextId=22]]^userContextId=22" +); +Assert.equal( + expandedPrincipal.siteOriginNoSuffix, + "[Expanded Principal [http://test1.example.com^userContextId=22]]" +); + +let ipv6URI = Services.io.newURI("https://[2001:db8::ff00:42:8329]:123"); +let ipv6Principal = scriptSecMan.createContentPrincipal(ipv6URI, { + userContextId: 6, +}); +Assert.ok(ipv6Principal.isContentPrincipal); +Assert.equal( + ipv6Principal.origin, + "https://[2001:db8::ff00:42:8329]:123^userContextId=6" +); +Assert.equal( + ipv6Principal.originNoSuffix, + "https://[2001:db8::ff00:42:8329]:123" +); +Assert.equal( + ipv6Principal.siteOrigin, + "https://[2001:db8::ff00:42:8329]^userContextId=6" +); +Assert.equal( + ipv6Principal.siteOriginNoSuffix, + "https://[2001:db8::ff00:42:8329]" +); diff --git a/caps/tests/unit/test_uri_escaping.js b/caps/tests/unit/test_uri_escaping.js new file mode 100644 index 0000000000..91263b058c --- /dev/null +++ b/caps/tests/unit/test_uri_escaping.js @@ -0,0 +1,29 @@ +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +var ssm = Services.scriptSecurityManager; + +function makeURI(uri) { + return Services.io.newURI(uri); +} + +function createPrincipal(aURI) { + try { + var uri = makeURI(aURI); + var principal = ssm.createContentPrincipal(uri, {}); + return principal; + } catch (e) { + return null; + } +} + +function run_test() { + Assert.equal(createPrincipal("http://test^test/foo^bar#x^y"), null); + + Assert.equal(createPrincipal("http://test^test/foo\\bar"), null); + + Assert.equal(createPrincipal("http://test:2^3/foo\\bar"), null); + + Assert.equal( + createPrincipal("http://test/foo^bar").exposableSpec, + "http://test/foo%5Ebar" + ); +} diff --git a/caps/tests/unit/xpcshell.ini b/caps/tests/unit/xpcshell.ini new file mode 100644 index 0000000000..a18acf9673 --- /dev/null +++ b/caps/tests/unit/xpcshell.ini @@ -0,0 +1,7 @@ +[DEFAULT] +head = + +[test_origin.js] +[test_uri_escaping.js] +[test_ipv6_host_literal.js] +[test_site_origin.js] |