summaryrefslogtreecommitdiffstats
path: root/dom/security/test/gtest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/security/test/gtest
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/security/test/gtest')
-rw-r--r--dom/security/test/gtest/TestCSPParser.cpp1148
-rw-r--r--dom/security/test/gtest/TestFilenameEvalParser.cpp453
-rw-r--r--dom/security/test/gtest/TestSecureContext.cpp121
-rw-r--r--dom/security/test/gtest/TestSmartCrashTrimmer.cpp44
-rw-r--r--dom/security/test/gtest/TestUnexpectedPrivilegedLoads.cpp305
-rw-r--r--dom/security/test/gtest/moz.build25
6 files changed, 2096 insertions, 0 deletions
diff --git a/dom/security/test/gtest/TestCSPParser.cpp b/dom/security/test/gtest/TestCSPParser.cpp
new file mode 100644
index 0000000000..b8a4e986b6
--- /dev/null
+++ b/dom/security/test/gtest/TestCSPParser.cpp
@@ -0,0 +1,1148 @@
+/* -*- 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 <string.h>
+#include <stdlib.h>
+
+#include "nsIContentSecurityPolicy.h"
+#include "nsNetUtil.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/nsCSPContext.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsStringFwd.h"
+
+/*
+ * Testing the parser is non trivial, especially since we can not call
+ * parser functionality directly in compiled code tests.
+ * All the tests (except the fuzzy tests at the end) follow the same schemata:
+ * a) create an nsIContentSecurityPolicy object
+ * b) set the selfURI in SetRequestContextWithPrincipal
+ * c) append one or more policies by calling AppendPolicy
+ * d) check if the policy count is correct by calling GetPolicyCount
+ * e) compare the result of the policy with the expected output
+ * using the struct PolicyTest;
+ *
+ * In general we test:
+ * a) policies that the parser should accept
+ * b) policies that the parser should reject
+ * c) policies that are randomly generated (fuzzy tests)
+ *
+ * Please note that fuzzy tests are *DISABLED* by default and shold only
+ * be run *OFFLINE* whenever code in nsCSPParser changes.
+ * To run fuzzy tests, flip RUN_OFFLINE_TESTS to 1.
+ *
+ */
+
+#define RUN_OFFLINE_TESTS 0
+
+/*
+ * Offline tests are separated in three different groups:
+ * * TestFuzzyPolicies - complete random ASCII input
+ * * TestFuzzyPoliciesIncDir - a directory name followed by random ASCII
+ * * TestFuzzyPoliciesIncDirLimASCII - a directory name followed by limited
+ * ASCII which represents more likely user input.
+ *
+ * We run each of this categories |kFuzzyRuns| times.
+ */
+
+#if RUN_OFFLINE_TESTS
+static const uint32_t kFuzzyRuns = 10000;
+#endif
+
+// For fuzzy testing we actually do not care about the output,
+// we just want to make sure that the parser can handle random
+// input, therefore we use kFuzzyExpectedPolicyCount to return early.
+static const uint32_t kFuzzyExpectedPolicyCount = 111;
+
+static const uint32_t kMaxPolicyLength = 96;
+
+struct PolicyTest {
+ char policy[kMaxPolicyLength];
+ char expectedResult[kMaxPolicyLength];
+};
+
+nsresult runTest(
+ uint32_t aExpectedPolicyCount, // this should be 0 for policies which
+ // should fail to parse
+ const char* aPolicy, const char* aExpectedResult) {
+ nsresult rv;
+
+ // we init the csp with http://www.selfuri.com
+ nsCOMPtr<nsIURI> selfURI;
+ rv = NS_NewURI(getter_AddRefs(selfURI), "http://www.selfuri.com");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> selfURIPrincipal;
+ mozilla::OriginAttributes attrs;
+ selfURIPrincipal =
+ mozilla::BasePrincipal::CreateContentPrincipal(selfURI, attrs);
+ NS_ENSURE_TRUE(selfURIPrincipal, NS_ERROR_FAILURE);
+
+ // create a CSP object
+ nsCOMPtr<nsIContentSecurityPolicy> csp =
+ do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // for testing the parser we only need to set a principal which is needed
+ // to translate the keyword 'self' into an actual URI.
+ rv =
+ csp->SetRequestContextWithPrincipal(selfURIPrincipal, selfURI, u""_ns, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // append a policy
+ nsString policyStr;
+ policyStr.AssignASCII(aPolicy);
+ rv = csp->AppendPolicy(policyStr, false, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // when executing fuzzy tests we do not care about the actual output
+ // of the parser, we just want to make sure that the parser is not crashing.
+ if (aExpectedPolicyCount == kFuzzyExpectedPolicyCount) {
+ return NS_OK;
+ }
+
+ // verify that the expected number of policies exists
+ uint32_t actualPolicyCount;
+ rv = csp->GetPolicyCount(&actualPolicyCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (actualPolicyCount != aExpectedPolicyCount) {
+ EXPECT_TRUE(false)
+ << "Actual policy count not equal to expected policy count ("
+ << actualPolicyCount << " != " << aExpectedPolicyCount
+ << ") for policy: " << aPolicy;
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // if the expected policy count is 0, we can return, because
+ // we can not compare any output anyway. Used when parsing
+ // errornous policies.
+ if (aExpectedPolicyCount == 0) {
+ return NS_OK;
+ }
+
+ // compare the parsed policy against the expected result
+ nsString parsedPolicyStr;
+ // checking policy at index 0, which is the one what we appended.
+ rv = csp->GetPolicyString(0, parsedPolicyStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!NS_ConvertUTF16toUTF8(parsedPolicyStr).EqualsASCII(aExpectedResult)) {
+ EXPECT_TRUE(false) << "Actual policy does not match expected policy ("
+ << NS_ConvertUTF16toUTF8(parsedPolicyStr).get()
+ << " != " << aExpectedResult << ")";
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+// ============================= run Tests ========================
+
+nsresult runTestSuite(const PolicyTest* aPolicies, uint32_t aPolicyCount,
+ uint32_t aExpectedPolicyCount) {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+ // Add prefs you need to set to parse CSP here, see comments for example
+ // bool examplePref = false;
+ if (prefs) {
+ // prefs->GetBoolPref("security.csp.examplePref", &examplePref);
+ // prefs->SetBoolPref("security.csp.examplePref", true);
+ }
+
+ for (uint32_t i = 0; i < aPolicyCount; i++) {
+ rv = runTest(aExpectedPolicyCount, aPolicies[i].policy,
+ aPolicies[i].expectedResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (prefs) {
+ // prefs->SetBoolPref("security.csp.examplePref", examplePref);
+ }
+
+ return NS_OK;
+}
+
+// ============================= TestDirectives ========================
+
+TEST(CSPParser, Directives)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "connect-src xn--mnchen-3ya.de",
+ "connect-src http://xn--mnchen-3ya.de"},
+ { "default-src http://www.example.com",
+ "default-src http://www.example.com" },
+ { "script-src http://www.example.com",
+ "script-src http://www.example.com" },
+ { "object-src http://www.example.com",
+ "object-src http://www.example.com" },
+ { "style-src http://www.example.com",
+ "style-src http://www.example.com" },
+ { "img-src http://www.example.com",
+ "img-src http://www.example.com" },
+ { "media-src http://www.example.com",
+ "media-src http://www.example.com" },
+ { "frame-src http://www.example.com",
+ "frame-src http://www.example.com" },
+ { "font-src http://www.example.com",
+ "font-src http://www.example.com" },
+ { "connect-src http://www.example.com",
+ "connect-src http://www.example.com" },
+ { "report-uri http://www.example.com",
+ "report-uri http://www.example.com/" },
+ { "script-src 'nonce-correctscriptnonce'",
+ "script-src 'nonce-correctscriptnonce'" },
+ { "script-src 'nonce-a'",
+ "script-src 'nonce-a'" },
+ { "script-src 'sha256-a'",
+ "script-src 'sha256-a'" },
+ { "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
+ "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
+ { "script-src 'nonce-foo' 'unsafe-inline' ",
+ "script-src 'nonce-foo' 'unsafe-inline'" },
+ { "script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' https: ",
+ "script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' https:" },
+ { "script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' 'report-sample' https: ",
+ "script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' 'report-sample' https:" },
+ { "default-src 'sha256-siVR8' 'strict-dynamic' 'unsafe-inline' https: ",
+ "default-src 'sha256-siVR8' 'strict-dynamic' 'unsafe-inline' https:" },
+ { "worker-src https://example.com",
+ "worker-src https://example.com" },
+ { "worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com",
+ "worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com" },
+ { "script-src 'unsafe-allow-redirects' http://example.com",
+ "script-src http://example.com"},
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// ============================= TestKeywords ========================
+
+TEST(CSPParser, Keywords)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "script-src 'self'",
+ "script-src 'self'" },
+ { "script-src 'unsafe-inline'",
+ "script-src 'unsafe-inline'" },
+ { "script-src 'unsafe-eval'",
+ "script-src 'unsafe-eval'" },
+ { "script-src 'unsafe-inline' 'unsafe-eval'",
+ "script-src 'unsafe-inline' 'unsafe-eval'" },
+ { "script-src 'none'",
+ "script-src 'none'" },
+ { "script-src 'wasm-unsafe-eval'",
+ "script-src 'wasm-unsafe-eval'" },
+ { "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src 'self'",
+ "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src 'self'" },
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// =================== TestIgnoreUpperLowerCasePolicies ==============
+
+TEST(CSPParser, IgnoreUpperLowerCasePolicies)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "script-src 'SELF'",
+ "script-src 'self'" },
+ { "sCriPt-src 'Unsafe-Inline'",
+ "script-src 'unsafe-inline'" },
+ { "SCRIPT-src 'unsafe-eval'",
+ "script-src 'unsafe-eval'" },
+ { "default-SRC 'unsafe-inline' 'unsafe-eval'",
+ "default-src 'unsafe-inline' 'unsafe-eval'" },
+ { "script-src 'NoNe'",
+ "script-src 'none'" },
+ { "img-sRc 'noNe'; scrIpt-src 'unsafe-EVAL' 'UNSAFE-inline'; deFAULT-src 'Self'",
+ "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src 'self'" },
+ { "default-src HTTP://www.example.com",
+ "default-src http://www.example.com" },
+ { "default-src HTTP://WWW.EXAMPLE.COM",
+ "default-src http://www.example.com" },
+ { "default-src HTTPS://*.example.COM",
+ "default-src https://*.example.com" },
+ { "script-src 'none' test.com;",
+ "script-src http://test.com" },
+ { "script-src 'NoNCE-correctscriptnonce'",
+ "script-src 'nonce-correctscriptnonce'" },
+ { "script-src 'NoncE-NONCENEEDSTOBEUPPERCASE'",
+ "script-src 'nonce-NONCENEEDSTOBEUPPERCASE'" },
+ { "script-src 'SHA256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
+ "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
+ { "upgrade-INSECURE-requests",
+ "upgrade-insecure-requests" },
+ { "sanDBox alloW-foRMs",
+ "sandbox allow-forms"},
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// ========================= TestPaths ===============================
+
+TEST(CSPParser, Paths)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "script-src http://www.example.com",
+ "script-src http://www.example.com" },
+ { "script-src http://www.example.com/",
+ "script-src http://www.example.com/" },
+ { "script-src http://www.example.com/path-1",
+ "script-src http://www.example.com/path-1" },
+ { "script-src http://www.example.com/path-1/",
+ "script-src http://www.example.com/path-1/" },
+ { "script-src http://www.example.com/path-1/path_2",
+ "script-src http://www.example.com/path-1/path_2" },
+ { "script-src http://www.example.com/path-1/path_2/",
+ "script-src http://www.example.com/path-1/path_2/" },
+ { "script-src http://www.example.com/path-1/path_2/file.js",
+ "script-src http://www.example.com/path-1/path_2/file.js" },
+ { "script-src http://www.example.com/path-1/path_2/file_1.js",
+ "script-src http://www.example.com/path-1/path_2/file_1.js" },
+ { "script-src http://www.example.com/path-1/path_2/file-2.js",
+ "script-src http://www.example.com/path-1/path_2/file-2.js" },
+ { "script-src http://www.example.com/path-1/path_2/f.js",
+ "script-src http://www.example.com/path-1/path_2/f.js" },
+ { "script-src http://www.example.com:88",
+ "script-src http://www.example.com:88" },
+ { "script-src http://www.example.com:88/",
+ "script-src http://www.example.com:88/" },
+ { "script-src http://www.example.com:88/path-1",
+ "script-src http://www.example.com:88/path-1" },
+ { "script-src http://www.example.com:88/path-1/",
+ "script-src http://www.example.com:88/path-1/" },
+ { "script-src http://www.example.com:88/path-1/path_2",
+ "script-src http://www.example.com:88/path-1/path_2" },
+ { "script-src http://www.example.com:88/path-1/path_2/",
+ "script-src http://www.example.com:88/path-1/path_2/" },
+ { "script-src http://www.example.com:88/path-1/path_2/file.js",
+ "script-src http://www.example.com:88/path-1/path_2/file.js" },
+ { "script-src http://www.example.com:*",
+ "script-src http://www.example.com:*" },
+ { "script-src http://www.example.com:*/",
+ "script-src http://www.example.com:*/" },
+ { "script-src http://www.example.com:*/path-1",
+ "script-src http://www.example.com:*/path-1" },
+ { "script-src http://www.example.com:*/path-1/",
+ "script-src http://www.example.com:*/path-1/" },
+ { "script-src http://www.example.com:*/path-1/path_2",
+ "script-src http://www.example.com:*/path-1/path_2" },
+ { "script-src http://www.example.com:*/path-1/path_2/",
+ "script-src http://www.example.com:*/path-1/path_2/" },
+ { "script-src http://www.example.com:*/path-1/path_2/file.js",
+ "script-src http://www.example.com:*/path-1/path_2/file.js" },
+ { "script-src http://www.example.com#foo",
+ "script-src http://www.example.com" },
+ { "script-src http://www.example.com?foo=bar",
+ "script-src http://www.example.com" },
+ { "script-src http://www.example.com:8888#foo",
+ "script-src http://www.example.com:8888" },
+ { "script-src http://www.example.com:8888?foo",
+ "script-src http://www.example.com:8888" },
+ { "script-src http://www.example.com/#foo",
+ "script-src http://www.example.com/" },
+ { "script-src http://www.example.com/?foo",
+ "script-src http://www.example.com/" },
+ { "script-src http://www.example.com/path-1/file.js#foo",
+ "script-src http://www.example.com/path-1/file.js" },
+ { "script-src http://www.example.com/path-1/file.js?foo",
+ "script-src http://www.example.com/path-1/file.js" },
+ { "script-src http://www.example.com/path-1/file.js?foo#bar",
+ "script-src http://www.example.com/path-1/file.js" },
+ { "report-uri http://www.example.com/",
+ "report-uri http://www.example.com/" },
+ { "report-uri http://www.example.com:8888/asdf",
+ "report-uri http://www.example.com:8888/asdf" },
+ { "report-uri http://www.example.com:8888/path_1/path_2",
+ "report-uri http://www.example.com:8888/path_1/path_2" },
+ { "report-uri http://www.example.com:8888/path_1/path_2/report.sjs&301",
+ "report-uri http://www.example.com:8888/path_1/path_2/report.sjs&301" },
+ { "report-uri /examplepath",
+ "report-uri http://www.selfuri.com/examplepath" },
+ { "connect-src http://www.example.com/foo%3Bsessionid=12%2C34",
+ "connect-src http://www.example.com/foo;sessionid=12,34" },
+ { "connect-src http://www.example.com/foo%3bsessionid=12%2c34",
+ "connect-src http://www.example.com/foo;sessionid=12,34" },
+ { "connect-src http://test.com/pathIncludingAz19-._~!$&'()*+=:@",
+ "connect-src http://test.com/pathIncludingAz19-._~!$&'()*+=:@" },
+ { "script-src http://www.example.com:88/.js",
+ "script-src http://www.example.com:88/.js" },
+ { "script-src https://foo.com/_abc/abc_/_/_a_b_c_",
+ "script-src https://foo.com/_abc/abc_/_/_a_b_c_" }
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// ======================== TestSimplePolicies =======================
+
+TEST(CSPParser, SimplePolicies)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "frame-src intent:",
+ "frame-src intent:" },
+ { "frame-src intent://host.name",
+ "frame-src intent://host.name" },
+ { "frame-src intent://my.host.link/",
+ "frame-src intent://my.host.link/" },
+ { "default-src *",
+ "default-src *" },
+ { "default-src https:",
+ "default-src https:" },
+ { "default-src https://*",
+ "default-src https://*" },
+ { "default-src *:*",
+ "default-src http://*:*" },
+ { "default-src *:80",
+ "default-src http://*:80" },
+ { "default-src http://*:80",
+ "default-src http://*:80" },
+ { "default-src javascript:",
+ "default-src javascript:" },
+ { "default-src data:",
+ "default-src data:" },
+ { "script-src 'unsafe-eval' 'unsafe-inline' http://www.example.com",
+ "script-src 'unsafe-eval' 'unsafe-inline' http://www.example.com" },
+ { "object-src 'self'",
+ "object-src 'self'" },
+ { "style-src http://www.example.com 'self'",
+ "style-src http://www.example.com 'self'" },
+ { "media-src http://www.example.com http://www.test.com",
+ "media-src http://www.example.com http://www.test.com" },
+ { "connect-src http://www.test.com example.com *.other.com;",
+ "connect-src http://www.test.com http://example.com http://*.other.com"},
+ { "connect-src example.com *.other.com",
+ "connect-src http://example.com http://*.other.com"},
+ { "style-src *.other.com example.com",
+ "style-src http://*.other.com http://example.com"},
+ { "default-src 'self'; img-src *;",
+ "default-src 'self'; img-src *" },
+ { "object-src media1.example.com media2.example.com *.cdn.example.com;",
+ "object-src http://media1.example.com http://media2.example.com http://*.cdn.example.com" },
+ { "script-src trustedscripts.example.com",
+ "script-src http://trustedscripts.example.com" },
+ { "script-src 'self' ; default-src trustedscripts.example.com",
+ "script-src 'self'; default-src http://trustedscripts.example.com" },
+ { "default-src 'none'; report-uri http://localhost:49938/test",
+ "default-src 'none'; report-uri http://localhost:49938/test" },
+ { " ; default-src abc",
+ "default-src http://abc" },
+ { " ; ; ; ; default-src abc ; ; ; ;",
+ "default-src http://abc" },
+ { "script-src 'none' 'none' 'none';",
+ "script-src 'none'" },
+ { "script-src http://www.example.com/path-1//",
+ "script-src http://www.example.com/path-1//" },
+ { "script-src http://www.example.com/path-1//path_2",
+ "script-src http://www.example.com/path-1//path_2" },
+ { "default-src 127.0.0.1",
+ "default-src http://127.0.0.1" },
+ { "default-src 127.0.0.1:*",
+ "default-src http://127.0.0.1:*" },
+ { "default-src -; ",
+ "default-src http://-" },
+ { "script-src 1",
+ "script-src http://1" },
+ { "upgrade-insecure-requests",
+ "upgrade-insecure-requests" },
+ { "upgrade-insecure-requests https:",
+ "upgrade-insecure-requests" },
+ { "sandbox allow-scripts allow-forms ",
+ "sandbox allow-scripts allow-forms" },
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// =================== TestPoliciesWithInvalidSrc ====================
+
+TEST(CSPParser, PoliciesWithInvalidSrc)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "script-src 'self'; SCRIPT-SRC http://www.example.com",
+ "script-src 'self'" },
+ { "script-src 'none' test.com; script-src example.com",
+ "script-src http://test.com" },
+ { "default-src **",
+ "default-src 'none'" },
+ { "default-src 'self",
+ "default-src 'none'" },
+ { "default-src 'unsafe-inlin' ",
+ "default-src 'none'" },
+ { "default-src */",
+ "default-src 'none'" },
+ { "default-src",
+ "default-src 'none'" },
+ { "default-src 'unsafe-inlin' ",
+ "default-src 'none'" },
+ { "default-src :88",
+ "default-src 'none'" },
+ { "script-src abc::::::88",
+ "script-src 'none'" },
+ { "script-src *.*:*",
+ "script-src 'none'" },
+ { "img-src *::88",
+ "img-src 'none'" },
+ { "object-src http://localhost:",
+ "object-src 'none'" },
+ { "script-src test..com",
+ "script-src 'none'" },
+ { "script-src sub1.sub2.example+",
+ "script-src 'none'" },
+ { "script-src http://www.example.com//",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88path-1/",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88//",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88//path-1",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88//path-1",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88.js",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:*.js",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:*.",
+ "script-src 'none'" },
+ { "script-src 'nonce-{invalid}'",
+ "script-src 'none'" },
+ { "script-src 'sha256-{invalid}'",
+ "script-src 'none'" },
+ { "script-src 'nonce-in$valid'",
+ "script-src 'none'" },
+ { "script-src 'sha256-in$valid'",
+ "script-src 'none'" },
+ { "script-src 'nonce-invalid==='",
+ "script-src 'none'" },
+ { "script-src 'sha256-invalid==='",
+ "script-src 'none'" },
+ { "script-src 'nonce-==='",
+ "script-src 'none'" },
+ { "script-src 'sha256-==='",
+ "script-src 'none'" },
+ { "script-src 'nonce-=='",
+ "script-src 'none'" },
+ { "script-src 'sha256-=='",
+ "script-src 'none'" },
+ { "script-src 'nonce-='",
+ "script-src 'none'" },
+ { "script-src 'sha256-='",
+ "script-src 'none'" },
+ { "script-src 'nonce-'",
+ "script-src 'none'" },
+ { "script-src 'sha256-'",
+ "script-src 'none'" },
+ { "connect-src http://www.example.com/foo%zz;",
+ "connect-src 'none'" },
+ { "script-src https://foo.com/%$",
+ "script-src 'none'" },
+ { "sandbox foo",
+ "sandbox"},
+ // clang-format on
+ };
+
+ // amount of tests - 1, because the latest should be ignored.
+ uint32_t policyCount = (sizeof(policies) / sizeof(PolicyTest)) - 1;
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// ============================= TestBadPolicies =======================
+
+TEST(CSPParser, BadPolicies)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "script-sr 'self", "" },
+ { "", "" },
+ { "; ; ; ; ; ; ;", "" },
+ { "defaut-src asdf", "" },
+ { "default-src: aaa", "" },
+ { "asdf http://test.com", ""},
+ { "report-uri", ""},
+ { "report-uri http://:foo", ""},
+ { "require-sri-for", ""},
+ { "require-sri-for style", ""},
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 0));
+}
+
+// ======================= TestGoodGeneratedPolicies =================
+
+TEST(CSPParser, GoodGeneratedPolicies)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "default-src 'self'; img-src *",
+ "default-src 'self'; img-src *" },
+ { "report-uri /policy",
+ "report-uri http://www.selfuri.com/policy"},
+ { "img-src *",
+ "img-src *" },
+ { "media-src foo.bar",
+ "media-src http://foo.bar" },
+ { "frame-src *.bar",
+ "frame-src http://*.bar" },
+ { "font-src com",
+ "font-src http://com" },
+ { "connect-src f00b4r.com",
+ "connect-src http://f00b4r.com" },
+ { "script-src *.a.b.c",
+ "script-src http://*.a.b.c" },
+ { "object-src *.b.c",
+ "object-src http://*.b.c" },
+ { "style-src a.b.c",
+ "style-src http://a.b.c" },
+ { "img-src a.com",
+ "img-src http://a.com" },
+ { "media-src http://abc.com",
+ "media-src http://abc.com" },
+ { "frame-src a2-c.com",
+ "frame-src http://a2-c.com" },
+ { "font-src https://a.com",
+ "font-src https://a.com" },
+ { "connect-src *.a.com",
+ "connect-src http://*.a.com" },
+ { "default-src a.com:23",
+ "default-src http://a.com:23" },
+ { "script-src https://a.com:200",
+ "script-src https://a.com:200" },
+ { "object-src data:",
+ "object-src data:" },
+ { "style-src javascript:",
+ "style-src javascript:" },
+ { "frame-src https://foobar.com:443",
+ "frame-src https://foobar.com:443" },
+ { "font-src https://a.com:443",
+ "font-src https://a.com:443" },
+ { "connect-src http://a.com:80",
+ "connect-src http://a.com:80" },
+ { "default-src http://foobar.com",
+ "default-src http://foobar.com" },
+ { "script-src https://foobar.com",
+ "script-src https://foobar.com" },
+ { "style-src 'none'",
+ "style-src 'none'" },
+ { "img-src foo.bar:21 https://ras.bar",
+ "img-src http://foo.bar:21 https://ras.bar" },
+ { "media-src http://foo.bar:21 https://ras.bar:443",
+ "media-src http://foo.bar:21 https://ras.bar:443" },
+ { "frame-src http://self.com:80",
+ "frame-src http://self.com:80" },
+ { "font-src http://self.com",
+ "font-src http://self.com" },
+ { "connect-src https://foo.com http://bar.com:88",
+ "connect-src https://foo.com http://bar.com:88" },
+ { "default-src * https://bar.com 'none'",
+ "default-src * https://bar.com" },
+ { "script-src *.foo.com",
+ "script-src http://*.foo.com" },
+ { "object-src http://b.com",
+ "object-src http://b.com" },
+ { "style-src http://bar.com:88",
+ "style-src http://bar.com:88" },
+ { "img-src https://bar.com:88",
+ "img-src https://bar.com:88" },
+ { "media-src http://bar.com:443",
+ "media-src http://bar.com:443" },
+ { "frame-src https://foo.com:88",
+ "frame-src https://foo.com:88" },
+ { "font-src http://foo.com",
+ "font-src http://foo.com" },
+ { "connect-src http://x.com:23",
+ "connect-src http://x.com:23" },
+ { "default-src http://barbaz.com",
+ "default-src http://barbaz.com" },
+ { "script-src http://somerandom.foo.com",
+ "script-src http://somerandom.foo.com" },
+ { "default-src *",
+ "default-src *" },
+ { "style-src http://bar.com:22",
+ "style-src http://bar.com:22" },
+ { "img-src https://foo.com:443",
+ "img-src https://foo.com:443" },
+ { "script-src https://foo.com; ",
+ "script-src https://foo.com" },
+ { "img-src bar.com:*",
+ "img-src http://bar.com:*" },
+ { "font-src https://foo.com:400",
+ "font-src https://foo.com:400" },
+ { "connect-src http://bar.com:400",
+ "connect-src http://bar.com:400" },
+ { "default-src http://evil.com",
+ "default-src http://evil.com" },
+ { "script-src https://evil.com:100",
+ "script-src https://evil.com:100" },
+ { "default-src bar.com; script-src https://foo.com",
+ "default-src http://bar.com; script-src https://foo.com" },
+ { "default-src 'self'; script-src 'self' https://*:*",
+ "default-src 'self'; script-src 'self' https://*:*" },
+ { "img-src http://self.com:34",
+ "img-src http://self.com:34" },
+ { "media-src http://subd.self.com:34",
+ "media-src http://subd.self.com:34" },
+ { "default-src 'none'",
+ "default-src 'none'" },
+ { "connect-src http://self",
+ "connect-src http://self" },
+ { "default-src http://foo",
+ "default-src http://foo" },
+ { "script-src http://foo:80",
+ "script-src http://foo:80" },
+ { "object-src http://bar",
+ "object-src http://bar" },
+ { "style-src http://three:80",
+ "style-src http://three:80" },
+ { "img-src https://foo:400",
+ "img-src https://foo:400" },
+ { "media-src https://self:34",
+ "media-src https://self:34" },
+ { "frame-src https://bar",
+ "frame-src https://bar" },
+ { "font-src http://three:81",
+ "font-src http://three:81" },
+ { "connect-src https://three:81",
+ "connect-src https://three:81" },
+ { "script-src http://self.com:80/foo",
+ "script-src http://self.com:80/foo" },
+ { "object-src http://self.com/foo",
+ "object-src http://self.com/foo" },
+ { "report-uri /report.py",
+ "report-uri http://www.selfuri.com/report.py"},
+ { "img-src http://foo.org:34/report.py",
+ "img-src http://foo.org:34/report.py" },
+ { "media-src foo/bar/report.py",
+ "media-src http://foo/bar/report.py" },
+ { "report-uri /",
+ "report-uri http://www.selfuri.com/"},
+ { "font-src https://self.com/report.py",
+ "font-src https://self.com/report.py" },
+ { "connect-src https://foo.com/report.py",
+ "connect-src https://foo.com/report.py" },
+ { "default-src *; report-uri http://www.reporturi.com/",
+ "default-src *; report-uri http://www.reporturi.com/" },
+ { "default-src http://first.com",
+ "default-src http://first.com" },
+ { "script-src http://second.com",
+ "script-src http://second.com" },
+ { "object-src http://third.com",
+ "object-src http://third.com" },
+ { "style-src https://foobar.com:4443",
+ "style-src https://foobar.com:4443" },
+ { "img-src http://foobar.com:4443",
+ "img-src http://foobar.com:4443" },
+ { "media-src bar.com",
+ "media-src http://bar.com" },
+ { "frame-src http://bar.com",
+ "frame-src http://bar.com" },
+ { "font-src http://self.com/",
+ "font-src http://self.com/" },
+ { "script-src 'self'",
+ "script-src 'self'" },
+ { "default-src http://self.com/foo.png",
+ "default-src http://self.com/foo.png" },
+ { "script-src http://self.com/foo.js",
+ "script-src http://self.com/foo.js" },
+ { "object-src http://bar.com/foo.js",
+ "object-src http://bar.com/foo.js" },
+ { "style-src http://FOO.COM",
+ "style-src http://foo.com" },
+ { "img-src HTTP",
+ "img-src http://http" },
+ { "media-src http",
+ "media-src http://http" },
+ { "frame-src 'SELF'",
+ "frame-src 'self'" },
+ { "DEFAULT-src 'self';",
+ "default-src 'self'" },
+ { "default-src 'self' http://FOO.COM",
+ "default-src 'self' http://foo.com" },
+ { "default-src 'self' HTTP://foo.com",
+ "default-src 'self' http://foo.com" },
+ { "default-src 'NONE'",
+ "default-src 'none'" },
+ { "script-src policy-uri ",
+ "script-src http://policy-uri" },
+ { "img-src 'self'; ",
+ "img-src 'self'" },
+ { "frame-ancestors foo-bar.com",
+ "frame-ancestors http://foo-bar.com" },
+ { "frame-ancestors http://a.com",
+ "frame-ancestors http://a.com" },
+ { "frame-ancestors 'self'",
+ "frame-ancestors 'self'" },
+ { "frame-ancestors http://self.com:88",
+ "frame-ancestors http://self.com:88" },
+ { "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com",
+ "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com" },
+ { "frame-ancestors https://self.com:34",
+ "frame-ancestors https://self.com:34" },
+ { "frame-ancestors http://sampleuser:samplepass@example.com",
+ "frame-ancestors 'none'" },
+ { "default-src 'none'; frame-ancestors 'self'",
+ "default-src 'none'; frame-ancestors 'self'" },
+ { "frame-ancestors http://self:80",
+ "frame-ancestors http://self:80" },
+ { "frame-ancestors http://self.com/bar",
+ "frame-ancestors http://self.com/bar" },
+ { "default-src 'self'; frame-ancestors 'self'",
+ "default-src 'self'; frame-ancestors 'self'" },
+ { "frame-ancestors http://bar.com/foo.png",
+ "frame-ancestors http://bar.com/foo.png" },
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// ==================== TestBadGeneratedPolicies ====================
+
+TEST(CSPParser, BadGeneratedPolicies)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "foo.*.bar", ""},
+ { "foo!bar.com", ""},
+ { "x.*.a.com", ""},
+ { "a#2-c.com", ""},
+ { "http://foo.com:bar.com:23", ""},
+ { "f!oo.bar", ""},
+ { "ht!ps://f-oo.bar", ""},
+ { "https://f-oo.bar:3f", ""},
+ { "**", ""},
+ { "*a", ""},
+ { "http://username:password@self.com/foo", ""},
+ { "http://other:pass1@self.com/foo", ""},
+ { "http://user1:pass1@self.com/foo", ""},
+ { "http://username:password@self.com/bar", ""},
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 0));
+}
+
+// ============ TestGoodGeneratedPoliciesForPathHandling =============
+
+TEST(CSPParser, GoodGeneratedPoliciesForPathHandling)
+{
+ // Once bug 808292 (Implement path-level host-source matching to CSP)
+ // lands we have to update the expected output to include the parsed path
+
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "img-src http://test1.example.com",
+ "img-src http://test1.example.com" },
+ { "img-src http://test1.example.com/",
+ "img-src http://test1.example.com/" },
+ { "img-src http://test1.example.com/path-1",
+ "img-src http://test1.example.com/path-1" },
+ { "img-src http://test1.example.com/path-1/",
+ "img-src http://test1.example.com/path-1/" },
+ { "img-src http://test1.example.com/path-1/path_2/",
+ "img-src http://test1.example.com/path-1/path_2/" },
+ { "img-src http://test1.example.com/path-1/path_2/file.js",
+ "img-src http://test1.example.com/path-1/path_2/file.js" },
+ { "img-src http://test1.example.com/path-1/path_2/file_1.js",
+ "img-src http://test1.example.com/path-1/path_2/file_1.js" },
+ { "img-src http://test1.example.com/path-1/path_2/file-2.js",
+ "img-src http://test1.example.com/path-1/path_2/file-2.js" },
+ { "img-src http://test1.example.com/path-1/path_2/f.js",
+ "img-src http://test1.example.com/path-1/path_2/f.js" },
+ { "img-src http://test1.example.com/path-1/path_2/f.oo.js",
+ "img-src http://test1.example.com/path-1/path_2/f.oo.js" },
+ { "img-src test1.example.com",
+ "img-src http://test1.example.com" },
+ { "img-src test1.example.com/",
+ "img-src http://test1.example.com/" },
+ { "img-src test1.example.com/path-1",
+ "img-src http://test1.example.com/path-1" },
+ { "img-src test1.example.com/path-1/",
+ "img-src http://test1.example.com/path-1/" },
+ { "img-src test1.example.com/path-1/path_2/",
+ "img-src http://test1.example.com/path-1/path_2/" },
+ { "img-src test1.example.com/path-1/path_2/file.js",
+ "img-src http://test1.example.com/path-1/path_2/file.js" },
+ { "img-src test1.example.com/path-1/path_2/file_1.js",
+ "img-src http://test1.example.com/path-1/path_2/file_1.js" },
+ { "img-src test1.example.com/path-1/path_2/file-2.js",
+ "img-src http://test1.example.com/path-1/path_2/file-2.js" },
+ { "img-src test1.example.com/path-1/path_2/f.js",
+ "img-src http://test1.example.com/path-1/path_2/f.js" },
+ { "img-src test1.example.com/path-1/path_2/f.oo.js",
+ "img-src http://test1.example.com/path-1/path_2/f.oo.js" },
+ { "img-src *.example.com",
+ "img-src http://*.example.com" },
+ { "img-src *.example.com/",
+ "img-src http://*.example.com/" },
+ { "img-src *.example.com/path-1",
+ "img-src http://*.example.com/path-1" },
+ { "img-src *.example.com/path-1/",
+ "img-src http://*.example.com/path-1/" },
+ { "img-src *.example.com/path-1/path_2/",
+ "img-src http://*.example.com/path-1/path_2/" },
+ { "img-src *.example.com/path-1/path_2/file.js",
+ "img-src http://*.example.com/path-1/path_2/file.js" },
+ { "img-src *.example.com/path-1/path_2/file_1.js",
+ "img-src http://*.example.com/path-1/path_2/file_1.js" },
+ { "img-src *.example.com/path-1/path_2/file-2.js",
+ "img-src http://*.example.com/path-1/path_2/file-2.js" },
+ { "img-src *.example.com/path-1/path_2/f.js",
+ "img-src http://*.example.com/path-1/path_2/f.js" },
+ { "img-src *.example.com/path-1/path_2/f.oo.js",
+ "img-src http://*.example.com/path-1/path_2/f.oo.js" },
+ { "img-src test1.example.com:80",
+ "img-src http://test1.example.com:80" },
+ { "img-src test1.example.com:80/",
+ "img-src http://test1.example.com:80/" },
+ { "img-src test1.example.com:80/path-1",
+ "img-src http://test1.example.com:80/path-1" },
+ { "img-src test1.example.com:80/path-1/",
+ "img-src http://test1.example.com:80/path-1/" },
+ { "img-src test1.example.com:80/path-1/path_2",
+ "img-src http://test1.example.com:80/path-1/path_2" },
+ { "img-src test1.example.com:80/path-1/path_2/",
+ "img-src http://test1.example.com:80/path-1/path_2/" },
+ { "img-src test1.example.com:80/path-1/path_2/file.js",
+ "img-src http://test1.example.com:80/path-1/path_2/file.js" },
+ { "img-src test1.example.com:80/path-1/path_2/f.ile.js",
+ "img-src http://test1.example.com:80/path-1/path_2/f.ile.js" },
+ { "img-src test1.example.com:*",
+ "img-src http://test1.example.com:*" },
+ { "img-src test1.example.com:*/",
+ "img-src http://test1.example.com:*/" },
+ { "img-src test1.example.com:*/path-1",
+ "img-src http://test1.example.com:*/path-1" },
+ { "img-src test1.example.com:*/path-1/",
+ "img-src http://test1.example.com:*/path-1/" },
+ { "img-src test1.example.com:*/path-1/path_2",
+ "img-src http://test1.example.com:*/path-1/path_2" },
+ { "img-src test1.example.com:*/path-1/path_2/",
+ "img-src http://test1.example.com:*/path-1/path_2/" },
+ { "img-src test1.example.com:*/path-1/path_2/file.js",
+ "img-src http://test1.example.com:*/path-1/path_2/file.js" },
+ { "img-src test1.example.com:*/path-1/path_2/f.ile.js",
+ "img-src http://test1.example.com:*/path-1/path_2/f.ile.js" },
+ { "img-src http://test1.example.com/abc//",
+ "img-src http://test1.example.com/abc//" },
+ { "img-src https://test1.example.com/abc/def//",
+ "img-src https://test1.example.com/abc/def//" },
+ { "img-src https://test1.example.com/abc/def/ghi//",
+ "img-src https://test1.example.com/abc/def/ghi//" },
+ { "img-src http://test1.example.com:80/abc//",
+ "img-src http://test1.example.com:80/abc//" },
+ { "img-src https://test1.example.com:80/abc/def//",
+ "img-src https://test1.example.com:80/abc/def//" },
+ { "img-src https://test1.example.com:80/abc/def/ghi//",
+ "img-src https://test1.example.com:80/abc/def/ghi//" },
+ { "img-src https://test1.example.com/abc////////////def/",
+ "img-src https://test1.example.com/abc////////////def/" },
+ { "img-src https://test1.example.com/abc////////////",
+ "img-src https://test1.example.com/abc////////////" },
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// ============== TestBadGeneratedPoliciesForPathHandling ============
+
+TEST(CSPParser, BadGeneratedPoliciesForPathHandling)
+{
+ static const PolicyTest policies[] = {
+ // clang-format off
+ { "img-src test1.example.com:88path-1/",
+ "img-src 'none'" },
+ { "img-src test1.example.com:80.js",
+ "img-src 'none'" },
+ { "img-src test1.example.com:*.js",
+ "img-src 'none'" },
+ { "img-src test1.example.com:*.",
+ "img-src 'none'" },
+ { "img-src http://test1.example.com//",
+ "img-src 'none'" },
+ { "img-src http://test1.example.com:80//",
+ "img-src 'none'" },
+ { "img-src http://test1.example.com:80abc",
+ "img-src 'none'" },
+ // clang-format on
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_NS_SUCCEEDED(runTestSuite(policies, policyCount, 1));
+}
+
+// ======================== TestFuzzyPolicies ========================
+
+// Use a policy, eliminate one character at a time,
+// and feed it as input to the parser.
+
+TEST(CSPParser, ShorteningPolicies)
+{
+ char pol[] =
+ "default-src http://www.sub1.sub2.example.com:88/path1/path2/ "
+ "'unsafe-inline' 'none'";
+ uint32_t len = static_cast<uint32_t>(sizeof(pol));
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
+
+ while (--len) {
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
+ memcpy(&testPol[0].policy, &pol, len * sizeof(char));
+ ASSERT_TRUE(
+ NS_SUCCEEDED(runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount)));
+ }
+}
+
+// ============================= TestFuzzyPolicies ===================
+
+// We generate kFuzzyRuns inputs by (pseudo) randomly picking from the 128
+// ASCII characters; feed them to the parser and verfy that the parser
+// handles the input gracefully.
+//
+// Please note, that by using srand(0) we get deterministic results!
+
+#if RUN_OFFLINE_TESTS
+
+TEST(CSPParser, FuzzyPolicies)
+{
+ // init srand with 0 so we get same results
+ srand(0);
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength);
+
+ for (uint32_t index = 0; index < kFuzzyRuns; index++) {
+ // randomly select the length of the next policy
+ uint32_t polLength = rand() % kMaxPolicyLength;
+ // reset memory of the policy string
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
+
+ for (uint32_t i = 0; i < polLength; i++) {
+ // fill the policy array with random ASCII chars
+ testPol[0].policy[i] = static_cast<char>(rand() % 128);
+ }
+ ASSERT_TRUE(
+ NS_SUCCEEDED(runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount)));
+ }
+}
+
+#endif
+
+// ======================= TestFuzzyPoliciesIncDir ===================
+
+// In a similar fashion as in TestFuzzyPolicies, we again (pseudo) randomly
+// generate input for the parser, but this time also include a valid directive
+// followed by the random input.
+
+#if RUN_OFFLINE_TESTS
+
+TEST(CSPParser, FuzzyPoliciesIncDir)
+{
+ // init srand with 0 so we get same results
+ srand(0);
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength);
+
+ char defaultSrc[] = "default-src ";
+ int defaultSrcLen = sizeof(defaultSrc) - 1;
+ // copy default-src into the policy array
+ memcpy(&testPol[0].policy, &defaultSrc, (defaultSrcLen * sizeof(char)));
+
+ for (uint32_t index = 0; index < kFuzzyRuns; index++) {
+ // randomly select the length of the next policy
+ uint32_t polLength = rand() % (kMaxPolicyLength - defaultSrcLen);
+ // reset memory of the policy string, but leave default-src.
+ memset((&(testPol[0].policy) + (defaultSrcLen * sizeof(char))), '\0',
+ (kMaxPolicyLength - defaultSrcLen) * sizeof(char));
+
+ // do not start at index 0 so we do not overwrite 'default-src'
+ for (uint32_t i = defaultSrcLen; i < polLength; i++) {
+ // fill the policy array with random ASCII chars
+ testPol[0].policy[i] = static_cast<char>(rand() % 128);
+ }
+ ASSERT_TRUE(
+ NS_SUCCEEDED(runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount)));
+ }
+}
+
+#endif
+
+// ====================== TestFuzzyPoliciesIncDirLimASCII ============
+
+// Same as TestFuzzyPoliciesIncDir() but using limited ASCII,
+// which represents more likely input.
+
+#if RUN_OFFLINE_TESTS
+
+TEST(CSPParser, FuzzyPoliciesIncDirLimASCII)
+{
+ char input[] =
+ "1234567890"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWZYZ"
+ "!@#^&*()-+_=";
+
+ // init srand with 0 so we get same results
+ srand(0);
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength);
+
+ char defaultSrc[] = "default-src ";
+ int defaultSrcLen = sizeof(defaultSrc) - 1;
+ // copy default-src into the policy array
+ memcpy(&testPol[0].policy, &defaultSrc, (defaultSrcLen * sizeof(char)));
+
+ for (uint32_t index = 0; index < kFuzzyRuns; index++) {
+ // randomly select the length of the next policy
+ uint32_t polLength = rand() % (kMaxPolicyLength - defaultSrcLen);
+ // reset memory of the policy string, but leave default-src.
+ memset((&(testPol[0].policy) + (defaultSrcLen * sizeof(char))), '\0',
+ (kMaxPolicyLength - defaultSrcLen) * sizeof(char));
+
+ // do not start at index 0 so we do not overwrite 'default-src'
+ for (uint32_t i = defaultSrcLen; i < polLength; i++) {
+ // fill the policy array with chars from the pre-defined input
+ uint32_t inputIndex = rand() % sizeof(input);
+ testPol[0].policy[i] = input[inputIndex];
+ }
+ ASSERT_TRUE(
+ NS_SUCCEEDED(runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount)));
+ }
+}
+#endif
diff --git a/dom/security/test/gtest/TestFilenameEvalParser.cpp b/dom/security/test/gtest/TestFilenameEvalParser.cpp
new file mode 100644
index 0000000000..60683007ca
--- /dev/null
+++ b/dom/security/test/gtest/TestFilenameEvalParser.cpp
@@ -0,0 +1,453 @@
+/* -*- 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 <string.h>
+#include <stdlib.h>
+
+#include "nsContentSecurityUtils.h"
+#include "nsStringFwd.h"
+
+#include "mozilla/ExtensionPolicyService.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/SimpleGlobalObject.h"
+#include "mozilla/extensions/WebExtensionPolicy.h"
+
+static constexpr auto kChromeURI = "chromeuri"_ns;
+static constexpr auto kResourceURI = "resourceuri"_ns;
+static constexpr auto kBlobUri = "bloburi"_ns;
+static constexpr auto kDataUri = "dataurl"_ns;
+static constexpr auto kAboutUri = "abouturi"_ns;
+static constexpr auto kSingleString = "singlestring"_ns;
+static constexpr auto kMozillaExtensionFile = "mozillaextension_file"_ns;
+static constexpr auto kExtensionURI = "extension_uri"_ns;
+static constexpr auto kSuspectedUserChromeJS = "suspectedUserChromeJS"_ns;
+#if defined(XP_WIN)
+static constexpr auto kSanitizedWindowsURL = "sanitizedWindowsURL"_ns;
+static constexpr auto kSanitizedWindowsPath = "sanitizedWindowsPath"_ns;
+#endif
+static constexpr auto kOther = "other"_ns;
+
+#define ASSERT_AND_PRINT(first, second, condition) \
+ fprintf(stderr, "First: %s\n", first.get()); \
+ fprintf(stderr, "Second: %s\n", NS_ConvertUTF16toUTF8(second).get()); \
+ ASSERT_TRUE((condition));
+// Usage: ASSERT_AND_PRINT(ret.first, ret.second.value(), ...
+
+#define ASSERT_AND_PRINT_FIRST(first, condition) \
+ fprintf(stderr, "First: %s\n", (first).get()); \
+ ASSERT_TRUE((condition));
+// Usage: ASSERT_AND_PRINT_FIRST(ret.first, ...
+
+TEST(FilenameEvalParser, ResourceChrome)
+{
+ {
+ constexpr auto str = u"chrome://firegestures/content/browser.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kChromeURI && ret.second.isSome() &&
+ ret.second.value() == str);
+ }
+ {
+ constexpr auto str = u"resource://firegestures/content/browser.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kResourceURI && ret.second.isSome() &&
+ ret.second.value() == str);
+ }
+}
+
+TEST(FilenameEvalParser, BlobData)
+{
+ {
+ constexpr auto str = u"blob://000-000"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kBlobUri && !ret.second.isSome());
+ }
+ {
+ constexpr auto str = u"blob:000-000"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kBlobUri && !ret.second.isSome());
+ }
+ {
+ constexpr auto str = u"data://blahblahblah"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kDataUri && !ret.second.isSome());
+ }
+ {
+ constexpr auto str = u"data:blahblahblah"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kDataUri && !ret.second.isSome());
+ }
+}
+
+TEST(FilenameEvalParser, MozExtension)
+{
+ { // Test shield.mozilla.org replacing
+ constexpr auto str =
+ u"jar:file:///c:/users/bob/appdata/roaming/mozilla/firefox/profiles/"
+ u"foo/"
+ "extensions/federated-learning@shield.mozilla.org.xpi!/experiments/"
+ "study/api.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kMozillaExtensionFile &&
+ ret.second.value() ==
+ u"federated-learning@s!/experiments/study/api.js"_ns);
+ }
+ { // Test mozilla.org replacing
+ constexpr auto str =
+ u"jar:file:///c:/users/bob/appdata/roaming/mozilla/firefox/profiles/"
+ u"foo/"
+ "extensions/federated-learning@shigeld.mozilla.org.xpi!/experiments/"
+ "study/api.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(
+ ret.first == kMozillaExtensionFile &&
+ ret.second.value() ==
+ nsLiteralString(
+ u"federated-learning@shigeld.m!/experiments/study/api.js"));
+ }
+ { // Test truncating
+ constexpr auto str =
+ u"jar:file:///c:/users/bob/appdata/roaming/mozilla/firefox/profiles/"
+ u"foo/"
+ "extensions/federated-learning@shigeld.mozilla.org.xpi!/experiments/"
+ "study/apiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kMozillaExtensionFile &&
+ ret.second.value() ==
+ u"federated-learning@shigeld.m!/experiments/"
+ "study/apiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"_ns);
+ }
+}
+
+TEST(FilenameEvalParser, UserChromeJS)
+{
+ {
+ constexpr auto str = u"firegestures/content/browser.uc.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kSuspectedUserChromeJS && !ret.second.isSome());
+ }
+ {
+ constexpr auto str = u"firegestures/content/browser.uc.js?"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kSuspectedUserChromeJS && !ret.second.isSome());
+ }
+ {
+ constexpr auto str = u"firegestures/content/browser.uc.js?243244224"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kSuspectedUserChromeJS && !ret.second.isSome());
+ }
+ {
+ constexpr auto str =
+ u"file:///b:/fxprofiles/mark/chrome/"
+ "addbookmarkherewithmiddleclick.uc.js?1558444389291"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kSuspectedUserChromeJS && !ret.second.isSome());
+ }
+}
+
+TEST(FilenameEvalParser, SingleFile)
+{
+ {
+ constexpr auto str = u"browser.uc.js?2456"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kSingleString && ret.second.isSome() &&
+ ret.second.value() == str);
+ }
+ {
+ constexpr auto str = u"debugger"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kSingleString && ret.second.isSome() &&
+ ret.second.value() == str);
+ }
+}
+
+TEST(FilenameEvalParser, Other)
+{
+ {
+ constexpr auto str = u"firegestures--content"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+ }
+ {
+ constexpr auto str = u"gallop://thing/fire"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsURL &&
+ ret.second.value() == u"gallop"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"gallop://fire"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsURL &&
+ ret.second.value() == u"gallop"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"firegestures/content"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsPath &&
+ ret.second.value() == u"content"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"firegestures\\content"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsPath &&
+ ret.second.value() == u"content"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"/home/tom/files/thing"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsPath &&
+ ret.second.value() == u"thing"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"file://c/uers/tom/file.txt"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsURL &&
+ ret.second.value() == u"file://.../file.txt"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"c:/uers/tom/file.txt"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsPath &&
+ ret.second.value() == u"file.txt"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"http://example.com/"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsURL &&
+ ret.second.value() == u"http"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+ {
+ constexpr auto str = u"http://example.com/thing.html"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+#if defined(XP_WIN)
+ ASSERT_TRUE(ret.first == kSanitizedWindowsURL &&
+ ret.second.value() == u"http"_ns);
+#else
+ ASSERT_TRUE(ret.first == kOther && !ret.second.isSome());
+#endif
+ }
+}
+
+TEST(FilenameEvalParser, WebExtensionPathParser)
+{
+ {
+ // Set up an Extension and register it so we can test against it.
+ mozilla::dom::AutoJSAPI jsAPI;
+ ASSERT_TRUE(jsAPI.Init(xpc::PrivilegedJunkScope()));
+ JSContext* cx = jsAPI.cx();
+
+ mozilla::dom::GlobalObject go(cx, xpc::PrivilegedJunkScope());
+ auto* wEI = new mozilla::extensions::WebExtensionInit();
+
+ JS::Rooted<JSObject*> func(
+ cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, "customMethodA"));
+ JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx));
+ wEI->mLocalizeCallback = new mozilla::dom::WebExtensionLocalizeCallback(
+ cx, func, tempGlobalRoot, nullptr);
+
+ wEI->mAllowedOrigins =
+ mozilla::dom::OwningMatchPatternSetOrStringSequence();
+ nsString* slotPtr =
+ wEI->mAllowedOrigins.SetAsStringSequence().AppendElement(
+ mozilla::fallible);
+ ASSERT_TRUE(slotPtr != nullptr);
+ nsString& slot = *slotPtr;
+ slot.Truncate();
+ slot = u"http://example.com"_ns;
+
+ wEI->mName = u"gtest Test Extension"_ns;
+ wEI->mId = u"gtesttestextension@mozilla.org"_ns;
+ wEI->mBaseURL = u"file://foo"_ns;
+ wEI->mMozExtensionHostname = "e37c3c08-beac-a04b-8032-c4f699a1a856"_ns;
+
+ mozilla::ErrorResult eR;
+ RefPtr<mozilla::WebExtensionPolicy> w =
+ mozilla::extensions::WebExtensionPolicy::Constructor(go, *wEI, eR);
+ w->SetActive(true, eR);
+
+ constexpr auto str =
+ u"moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/file.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, true);
+
+ ASSERT_TRUE(ret.first == kExtensionURI &&
+ ret.second.value() ==
+ u"moz-extension://[gtesttestextension@mozilla.org: "
+ "gtest Test Extension]P=0/path/to/file.js"_ns);
+
+ w->SetActive(false, eR);
+
+ delete wEI;
+ }
+ {
+ // Set up an Extension and register it so we can test against it.
+ mozilla::dom::AutoJSAPI jsAPI;
+ ASSERT_TRUE(jsAPI.Init(xpc::PrivilegedJunkScope()));
+ JSContext* cx = jsAPI.cx();
+
+ mozilla::dom::GlobalObject go(cx, xpc::PrivilegedJunkScope());
+ auto wEI = new mozilla::extensions::WebExtensionInit();
+
+ JS::Rooted<JSObject*> func(
+ cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, "customMethodA"));
+ JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx));
+ wEI->mLocalizeCallback = new mozilla::dom::WebExtensionLocalizeCallback(
+ cx, func, tempGlobalRoot, NULL);
+
+ wEI->mAllowedOrigins =
+ mozilla::dom::OwningMatchPatternSetOrStringSequence();
+ nsString* slotPtr =
+ wEI->mAllowedOrigins.SetAsStringSequence().AppendElement(
+ mozilla::fallible);
+ nsString& slot = *slotPtr;
+ slot.Truncate();
+ slot = u"http://example.com"_ns;
+
+ wEI->mName = u"gtest Test Extension"_ns;
+ wEI->mId = u"gtesttestextension@mozilla.org"_ns;
+ wEI->mBaseURL = u"file://foo"_ns;
+ wEI->mMozExtensionHostname = "e37c3c08-beac-a04b-8032-c4f699a1a856"_ns;
+ wEI->mIsPrivileged = true;
+
+ mozilla::ErrorResult eR;
+ RefPtr<mozilla::WebExtensionPolicy> w =
+ mozilla::extensions::WebExtensionPolicy::Constructor(go, *wEI, eR);
+ w->SetActive(true, eR);
+
+ constexpr auto str =
+ u"moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/file.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, true);
+
+ ASSERT_TRUE(ret.first == kExtensionURI &&
+ ret.second.value() ==
+ u"moz-extension://[gtesttestextension@mozilla.org: "
+ "gtest Test Extension]P=1/path/to/file.js"_ns);
+
+ w->SetActive(false, eR);
+
+ delete wEI;
+ }
+ {
+ constexpr auto str =
+ u"moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/file.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kExtensionURI && !ret.second.isSome());
+ }
+ {
+ constexpr auto str =
+ u"moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/file.js"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, true);
+ ASSERT_TRUE(
+ ret.first == kExtensionURI &&
+ ret.second.value() ==
+ nsLiteralString(
+ u"moz-extension://[failed finding addon by host]/file.js"));
+ }
+ {
+ constexpr auto str =
+ u"moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/"
+ "file.js?querystringx=6"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, true);
+ ASSERT_TRUE(ret.first == kExtensionURI &&
+ ret.second.value() ==
+ u"moz-extension://[failed finding addon "
+ "by host]/path/to/file.js"_ns);
+ }
+}
+
+TEST(FilenameEvalParser, AboutPageParser)
+{
+ {
+ constexpr auto str = u"about:about"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kAboutUri &&
+ ret.second.value() == u"about:about"_ns);
+ }
+ {
+ constexpr auto str = u"about:about?hello"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kAboutUri &&
+ ret.second.value() == u"about:about"_ns);
+ }
+ {
+ constexpr auto str = u"about:about#mom"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kAboutUri &&
+ ret.second.value() == u"about:about"_ns);
+ }
+ {
+ constexpr auto str = u"about:about?hello=there#mom"_ns;
+ FilenameTypeAndDetails ret =
+ nsContentSecurityUtils::FilenameToFilenameType(str, false);
+ ASSERT_TRUE(ret.first == kAboutUri &&
+ ret.second.value() == u"about:about"_ns);
+ }
+}
diff --git a/dom/security/test/gtest/TestSecureContext.cpp b/dom/security/test/gtest/TestSecureContext.cpp
new file mode 100644
index 0000000000..dbfb4a63b6
--- /dev/null
+++ b/dom/security/test/gtest/TestSecureContext.cpp
@@ -0,0 +1,121 @@
+/* -*- 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 <string.h>
+#include <stdlib.h>
+
+#include "nsContentSecurityManager.h"
+#include "nsContentUtils.h"
+#include "nsIPrincipal.h"
+#include "nsScriptSecurityManager.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/Preferences.h"
+
+using namespace mozilla;
+
+static const uint32_t kURIMaxLength = 64;
+
+struct TestExpectations {
+ char uri[kURIMaxLength];
+ bool expectedResult;
+};
+
+class MOZ_RAII AutoRestoreBoolPref final {
+ public:
+ AutoRestoreBoolPref(const char* aPref, bool aValue) : mPref(aPref) {
+ Preferences::GetBool(mPref, &mOldValue);
+ Preferences::SetBool(mPref, aValue);
+ }
+
+ ~AutoRestoreBoolPref() { Preferences::SetBool(mPref, mOldValue); }
+
+ private:
+ const char* mPref = nullptr;
+ bool mOldValue = false;
+};
+
+// ============================= TestDirectives ========================
+
+TEST(SecureContext, IsOriginPotentiallyTrustworthyWithContentPrincipal)
+{
+ // boolean isOriginPotentiallyTrustworthy(in nsIPrincipal aPrincipal);
+
+ AutoRestoreBoolPref savedPref("network.proxy.allow_hijacking_localhost",
+ false);
+
+ static const TestExpectations uris[] = {
+ {"http://example.com/", false},
+ {"https://example.com/", true},
+ {"ws://example.com/", false},
+ {"wss://example.com/", true},
+ {"file:///xyzzy", true},
+ {"about:config", false},
+ {"http://localhost", true},
+ {"http://localhost.localhost", true},
+ {"http://a.b.c.d.e.localhost", true},
+ {"http://xyzzy.localhost", true},
+ {"http://127.0.0.1", true},
+ {"http://127.0.0.2", true},
+ {"http://127.1.0.1", true},
+ {"http://128.0.0.1", false},
+ {"http://[::1]", true},
+ {"http://[::ffff:127.0.0.1]", false},
+ {"http://[::ffff:127.0.0.2]", false},
+ {"http://[::ffff:7f00:1]", false},
+ {"http://[::ffff:7f00:2]", false},
+ {"resource://xyzzy", true},
+ {"moz-extension://xyzzy", true},
+ {"data:data:text/plain;charset=utf-8;base64,eHl6enk=", false},
+ {"blob://unique-id", false},
+ {"mailto:foo@bar.com", false},
+ {"moz-icon://example.com", false},
+ {"javascript:42", false},
+ };
+
+ uint32_t numExpectations = sizeof(uris) / sizeof(TestExpectations);
+ nsCOMPtr<nsIContentSecurityManager> csManager =
+ do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+ ASSERT_TRUE(!!csManager);
+
+ nsresult rv;
+ for (uint32_t i = 0; i < numExpectations; i++) {
+ nsCOMPtr<nsIPrincipal> prin;
+ nsAutoCString uri(uris[i].uri);
+ rv = nsScriptSecurityManager::GetScriptSecurityManager()
+ ->CreateContentPrincipalFromOrigin(uri, getter_AddRefs(prin));
+ ASSERT_EQ(rv, NS_OK);
+ bool isPotentiallyTrustworthy = prin->GetIsOriginPotentiallyTrustworthy();
+ ASSERT_EQ(isPotentiallyTrustworthy, uris[i].expectedResult)
+ << uris[i].uri << uris[i].expectedResult;
+ }
+}
+
+TEST(SecureContext, IsOriginPotentiallyTrustworthyWithSystemPrincipal)
+{
+ RefPtr<nsScriptSecurityManager> ssManager =
+ nsScriptSecurityManager::GetScriptSecurityManager();
+ ASSERT_TRUE(!!ssManager);
+ nsCOMPtr<nsIPrincipal> sysPrin = nsContentUtils::GetSystemPrincipal();
+ bool isPotentiallyTrustworthy = sysPrin->GetIsOriginPotentiallyTrustworthy();
+ ASSERT_TRUE(isPotentiallyTrustworthy);
+}
+
+TEST(SecureContext, IsOriginPotentiallyTrustworthyWithNullPrincipal)
+{
+ RefPtr<nsScriptSecurityManager> ssManager =
+ nsScriptSecurityManager::GetScriptSecurityManager();
+ ASSERT_TRUE(!!ssManager);
+
+ RefPtr<NullPrincipal> nullPrin =
+ NullPrincipal::CreateWithoutOriginAttributes();
+ bool isPotentiallyTrustworthy;
+ nsresult rv =
+ nullPrin->GetIsOriginPotentiallyTrustworthy(&isPotentiallyTrustworthy);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_TRUE(!isPotentiallyTrustworthy);
+}
diff --git a/dom/security/test/gtest/TestSmartCrashTrimmer.cpp b/dom/security/test/gtest/TestSmartCrashTrimmer.cpp
new file mode 100644
index 0000000000..d2238c0d75
--- /dev/null
+++ b/dom/security/test/gtest/TestSmartCrashTrimmer.cpp
@@ -0,0 +1,44 @@
+/* -*- 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nsContentSecurityUtils.h"
+#include "nsTString.h"
+#include "nsStringFwd.h"
+#include "mozilla/Sprintf.h"
+
+#define ASSERT_STRCMP(first, second) ASSERT_TRUE(strcmp(first, second) == 0);
+
+#define ASSERT_STRCMP_AND_PRINT(first, second) \
+ fprintf(stderr, "First: %s\n", first); \
+ fprintf(stderr, "Second: %s\n", second); \
+ fprintf(stderr, "strcmp = %i\n", strcmp(first, second)); \
+ ASSERT_EQUAL(first, second);
+
+TEST(SmartCrashTrimmer, Test)
+{
+ static_assert(sPrintfCrashReasonSize == 1024);
+ {
+ auto ret = nsContentSecurityUtils::SmartFormatCrashString(
+ std::string(1025, '.').c_str());
+ ASSERT_EQ(strlen(ret), 1023ul);
+ }
+
+ {
+ auto ret = nsContentSecurityUtils::SmartFormatCrashString(
+ std::string(1025, '.').c_str(), std::string(1025, 'A').c_str(),
+ "Hello %s world %s!");
+ char expected[1025];
+ SprintfLiteral(expected, "Hello %s world AAAAAAAAAAAAAAAAAAAAAAAAA!",
+ std::string(984, '.').c_str());
+ ASSERT_STRCMP(ret.get(), expected);
+ }
+}
diff --git a/dom/security/test/gtest/TestUnexpectedPrivilegedLoads.cpp b/dom/security/test/gtest/TestUnexpectedPrivilegedLoads.cpp
new file mode 100644
index 0000000000..772e4bd353
--- /dev/null
+++ b/dom/security/test/gtest/TestUnexpectedPrivilegedLoads.cpp
@@ -0,0 +1,305 @@
+/* -*- 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 "core/TelemetryEvent.h"
+#include "gtest/gtest.h"
+#include "js/Array.h" // JS::GetArrayLength
+#include "js/PropertyAndElement.h" // JS_GetElement, JS_GetProperty
+#include "js/TypeDecls.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "TelemetryFixture.h"
+#include "TelemetryTestHelpers.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include "nsContentSecurityManager.h"
+#include "nsContentSecurityUtils.h"
+#include "nsContentUtils.h"
+#include "nsIContentPolicy.h"
+#include "nsILoadInfo.h"
+#include "nsNetUtil.h"
+#include "nsStringFwd.h"
+
+using namespace mozilla;
+using namespace TelemetryTestHelpers;
+
+extern Atomic<bool, mozilla::Relaxed> sJSHacksChecked;
+extern Atomic<bool, mozilla::Relaxed> sJSHacksPresent;
+extern Atomic<bool, mozilla::Relaxed> sCSSHacksChecked;
+extern Atomic<bool, mozilla::Relaxed> sCSSHacksPresent;
+
+TEST_F(TelemetryTestFixture, UnexpectedPrivilegedLoadsTelemetryTest) {
+ // Disable JS/CSS Hacks Detection, which would consider this current profile
+ // as uninteresting for our measurements:
+ bool origJSHacksPresent = sJSHacksPresent;
+ bool origJSHacksChecked = sJSHacksChecked;
+ sJSHacksPresent = false;
+ sJSHacksChecked = true;
+ bool origCSSHacksPresent = sCSSHacksPresent;
+ bool origCSSHacksChecked = sCSSHacksChecked;
+ sCSSHacksPresent = false;
+ sCSSHacksChecked = true;
+
+ struct testResults {
+ nsCString fileinfo;
+ nsCString extraValueContenttype;
+ nsCString extraValueRemotetype;
+ nsCString extraValueFiledetails;
+ nsCString extraValueRedirects;
+ };
+
+ struct testCasesAndResults {
+ nsCString urlstring;
+ nsContentPolicyType contentType;
+ nsCString remoteType;
+ testResults expected;
+ };
+
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+ // Make sure we don't look at events from other tests.
+ Unused << mTelemetry->ClearEvents();
+
+ // required for telemetry lookups
+ constexpr auto category = "security"_ns;
+ constexpr auto method = "unexpectedload"_ns;
+ constexpr auto object = "systemprincipal"_ns;
+ constexpr auto extraKeyContenttype = "contenttype"_ns;
+ constexpr auto extraKeyRemotetype = "remotetype"_ns;
+ constexpr auto extraKeyFiledetails = "filedetails"_ns;
+ constexpr auto extraKeyRedirects = "redirects"_ns;
+
+ // some cases from TestFilenameEvalParser
+ // no need to replicate all scenarios?!
+ testCasesAndResults myTestCases[] = {
+ {"chrome://firegestures/content/browser.js"_ns,
+ nsContentPolicyType::TYPE_SCRIPT,
+ "web"_ns,
+ {"chromeuri"_ns, "TYPE_SCRIPT"_ns, "web"_ns,
+ "chrome://firegestures/content/browser.js"_ns, ""_ns}},
+ {"resource://firegestures/content/browser.js"_ns,
+ nsContentPolicyType::TYPE_SCRIPT,
+ "web"_ns,
+ {"resourceuri"_ns, "TYPE_SCRIPT"_ns, "web"_ns,
+ "resource://firegestures/content/browser.js"_ns, ""_ns}},
+ {// test that we don't report blob details
+ // ..and test that we strip of URLs from remoteTypes
+ "blob://000-000"_ns,
+ nsContentPolicyType::TYPE_SCRIPT,
+ "webIsolated=https://blob.example/"_ns,
+ {"bloburi"_ns, "TYPE_SCRIPT"_ns, "webIsolated"_ns, "unknown"_ns, ""_ns}},
+ {// test for cases where finalURI is null, due to a broken nested URI
+ // .. like malformed moz-icon URLs
+ "moz-icon:blahblah"_ns,
+ nsContentPolicyType::TYPE_DOCUMENT,
+ "web"_ns,
+ {"other"_ns, "TYPE_DOCUMENT"_ns, "web"_ns, "unknown"_ns, ""_ns}},
+ {// we dont report data urls
+ // ..and test that we strip of URLs from remoteTypes
+ "data://blahblahblah"_ns,
+ nsContentPolicyType::TYPE_SCRIPT,
+ "webCOOP+COEP=https://data.example"_ns,
+ {"dataurl"_ns, "TYPE_SCRIPT"_ns, "webCOOP+COEP"_ns, "unknown"_ns,
+ ""_ns}},
+ {// handle data URLs for webextension content scripts differently
+ // .. by noticing their annotation
+ "data:text/css;extension=style;charset=utf-8,/* some css here */"_ns,
+ nsContentPolicyType::TYPE_STYLESHEET,
+ "web"_ns,
+ {"dataurl-extension-contentstyle"_ns, "TYPE_STYLESHEET"_ns, "web"_ns,
+ "unknown"_ns, ""_ns}},
+ {// we only report file URLs on windows, where we can easily sanitize
+ "file://c/users/tom/file.txt"_ns,
+ nsContentPolicyType::TYPE_SCRIPT,
+ "web"_ns,
+ {
+#if defined(XP_WIN)
+ "sanitizedWindowsURL"_ns, "TYPE_SCRIPT"_ns, "web"_ns,
+ "file://.../file.txt"_ns, ""_ns
+
+#else
+ "other"_ns, "TYPE_SCRIPT"_ns, "web"_ns, "unknown"_ns, ""_ns
+#endif
+ }},
+ {// test for one redirect
+ "moz-extension://abcdefab-1234-4321-0000-abcdefabcdef/js/assets.js"_ns,
+ nsContentPolicyType::TYPE_SCRIPT,
+ "web"_ns,
+ {"extension_uri"_ns, "TYPE_SCRIPT"_ns, "web"_ns,
+ // the extension-id is made-up, so the extension will report failure
+ "moz-extension://[failed finding addon by host]/js/assets.js"_ns,
+ "https"_ns}},
+ {// test for cases where finalURI is empty
+ ""_ns,
+ nsContentPolicyType::TYPE_STYLESHEET,
+ "web"_ns,
+ {"other"_ns, "TYPE_STYLESHEET"_ns, "web"_ns, "unknown"_ns, ""_ns}},
+ {// test for cases where finalURI is null, due to the struct layout, we'll
+ // override the URL with nullptr in loop below.
+ "URLWillResultInNullPtr"_ns,
+ nsContentPolicyType::TYPE_SCRIPT,
+ "web"_ns,
+ {"other"_ns, "TYPE_SCRIPT"_ns, "web"_ns, "unknown"_ns, ""_ns}},
+ };
+
+ int i = 0;
+ for (auto const& currentTest : myTestCases) {
+ nsresult rv;
+ nsCOMPtr<nsIURI> uri;
+
+ // special-casing for a case where the uri is null
+ if (!currentTest.urlstring.Equals("URLWillResultInNullPtr")) {
+ NS_NewURI(getter_AddRefs(uri), currentTest.urlstring);
+ }
+
+ // We can't create channels for chrome: URLs unless they are in a chrome
+ // registry that maps them into the actual destination URL (usually
+ // file://). It seems that gtest don't have chrome manifest registered, so
+ // we'll use a mockChannel with a mockUri.
+ nsCOMPtr<nsIURI> mockUri;
+ rv = NS_NewURI(getter_AddRefs(mockUri), "http://example.com"_ns);
+ ASSERT_EQ(rv, NS_OK) << "Could not create mockUri";
+ nsCOMPtr<nsIChannel> mockChannel;
+ nsCOMPtr<nsIIOService> service = do_GetIOService();
+ if (!service) {
+ ASSERT_TRUE(false)
+ << "Couldn't initialize IOService";
+ }
+ rv = service->NewChannelFromURI(
+ mockUri, nullptr, nsContentUtils::GetSystemPrincipal(),
+ nsContentUtils::GetSystemPrincipal(), 0, currentTest.contentType,
+ getter_AddRefs(mockChannel));
+ ASSERT_EQ(rv, NS_OK) << "Could not create a mock channel";
+ nsCOMPtr<nsILoadInfo> mockLoadInfo = mockChannel->LoadInfo();
+
+ // We're adding a redirect entry for one specific test
+ if (currentTest.urlstring.EqualsASCII(
+ "moz-extension://abcdefab-1234-4321-0000-abcdefabcdef/js/"
+ "assets.js")) {
+ nsCOMPtr<nsIURI> redirUri;
+ NS_NewURI(getter_AddRefs(redirUri),
+ "https://www.analytics.example/analytics.js"_ns);
+ nsCOMPtr<nsIPrincipal> redirPrincipal =
+ BasePrincipal::CreateContentPrincipal(redirUri, OriginAttributes());
+ nsCOMPtr<nsIChannel> redirectChannel;
+ Unused << service->NewChannelFromURI(redirUri, nullptr, redirPrincipal,
+ nullptr, 0, currentTest.contentType,
+ getter_AddRefs(redirectChannel));
+
+ mockLoadInfo->AppendRedirectHistoryEntry(redirectChannel, false);
+ }
+
+ // this will record the event
+ nsContentSecurityManager::MeasureUnexpectedPrivilegedLoads(
+ mockLoadInfo, uri, currentTest.remoteType);
+
+ // let's inspect the recorded events
+
+ JS::Rooted<JS::Value> eventsSnapshot(cx.GetJSContext());
+ GetEventSnapshot(cx.GetJSContext(), &eventsSnapshot);
+
+ ASSERT_TRUE(EventPresent(cx.GetJSContext(), eventsSnapshot, category,
+ method, object))
+ << "Test event with value and extra must be present.";
+
+ // Convert eventsSnapshot into array/object
+ JSContext* aCx = cx.GetJSContext();
+ JS::Rooted<JSObject*> arrayObj(aCx, &eventsSnapshot.toObject());
+
+ JS::Rooted<JS::Value> eventRecord(aCx);
+ ASSERT_TRUE(JS_GetElement(aCx, arrayObj, i++, &eventRecord))
+ << "Must be able to get record."; // record is already undefined :-/
+
+ ASSERT_TRUE(!eventRecord.isUndefined())
+ << "eventRecord should not be undefined";
+
+ JS::Rooted<JSObject*> recordArray(aCx, &eventRecord.toObject());
+ uint32_t recordLength;
+ ASSERT_TRUE(JS::GetArrayLength(aCx, recordArray, &recordLength))
+ << "Event record array must have length.";
+ ASSERT_TRUE(recordLength == 6)
+ << "Event record must have 6 elements.";
+
+ JS::Rooted<JS::Value> str(aCx);
+ nsAutoJSString jsStr;
+ // The fileinfo string is at index 4
+ ASSERT_TRUE(JS_GetElement(aCx, recordArray, 4, &str))
+ << "Must be able to get value.";
+ ASSERT_TRUE(jsStr.init(aCx, str))
+ << "Value must be able to be init'd to a jsstring.";
+
+ ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
+ currentTest.expected.fileinfo.get())
+ << "Reported fileinfo '" << NS_ConvertUTF16toUTF8(jsStr).get()
+ << " 'equals expected value: " << currentTest.expected.fileinfo.get();
+
+ // Extra is at index 5
+ JS::Rooted<JS::Value> obj(aCx);
+ ASSERT_TRUE(JS_GetElement(aCx, recordArray, 5, &obj))
+ << "Must be able to get extra data";
+ JS::Rooted<JSObject*> extraObj(aCx, &obj.toObject());
+ // looking at remotetype extra for content type
+ JS::Rooted<JS::Value> extraValC(aCx);
+ ASSERT_TRUE(
+ JS_GetProperty(aCx, extraObj, extraKeyContenttype.get(), &extraValC))
+ << "Must be able to get the extra key's value for contenttype";
+ ASSERT_TRUE(jsStr.init(aCx, extraValC))
+ << "Extra value contenttype must be able to be init'd to a jsstring.";
+ ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
+ currentTest.expected.extraValueContenttype.get())
+ << "Reported value for extra contenttype '"
+ << NS_ConvertUTF16toUTF8(jsStr).get()
+ << "' should equals supplied value"
+ << currentTest.expected.extraValueContenttype.get();
+ // and again for remote type
+ JS::Rooted<JS::Value> extraValP(aCx);
+ ASSERT_TRUE(
+ JS_GetProperty(aCx, extraObj, extraKeyRemotetype.get(), &extraValP))
+ << "Must be able to get the extra key's value for remotetype";
+ ASSERT_TRUE(jsStr.init(aCx, extraValP))
+ << "Extra value remotetype must be able to be init'd to a jsstring.";
+ ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
+ currentTest.expected.extraValueRemotetype.get())
+ << "Reported value for extra remotetype '"
+ << NS_ConvertUTF16toUTF8(jsStr).get()
+ << "' should equals supplied value: "
+ << currentTest.expected.extraValueRemotetype.get();
+ // repeating the same for filedetails extra
+ JS::Rooted<JS::Value> extraValF(aCx);
+ ASSERT_TRUE(
+ JS_GetProperty(aCx, extraObj, extraKeyFiledetails.get(), &extraValF))
+ << "Must be able to get the extra key's value for filedetails";
+ ASSERT_TRUE(jsStr.init(aCx, extraValF))
+ << "Extra value filedetails must be able to be init'd to a jsstring.";
+ ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
+ currentTest.expected.extraValueFiledetails.get())
+ << "Reported value for extra filedetails '"
+ << NS_ConvertUTF16toUTF8(jsStr).get() << "'should equals supplied value"
+ << currentTest.expected.extraValueFiledetails.get();
+ // checking the extraKeyRedirects match
+ JS::Rooted<JS::Value> extraValRedirects(aCx);
+ ASSERT_TRUE(JS_GetProperty(aCx, extraObj, extraKeyRedirects.get(),
+ &extraValRedirects))
+ << "Must be able to get the extra value for redirects";
+ ASSERT_TRUE(jsStr.init(aCx, extraValRedirects))
+ << "Extra value redirects must be able to be init'd to a jsstring";
+ ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
+ currentTest.expected.extraValueRedirects.get())
+ << "Reported value for extra redirect '"
+ << NS_ConvertUTF16toUTF8(jsStr).get()
+ << "' should equals supplied value: "
+ << currentTest.expected.extraValueRedirects.get();
+ }
+
+ // Re-store JS/CSS hacks detection state
+ sJSHacksPresent = origJSHacksPresent;
+ sJSHacksChecked = origJSHacksChecked;
+ sCSSHacksPresent = origCSSHacksPresent;
+ sCSSHacksChecked = origCSSHacksChecked;
+}
diff --git a/dom/security/test/gtest/moz.build b/dom/security/test/gtest/moz.build
new file mode 100644
index 0000000000..c9ab4dcece
--- /dev/null
+++ b/dom/security/test/gtest/moz.build
@@ -0,0 +1,25 @@
+# -*- 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 += [
+ "TestCSPParser.cpp",
+ "TestFilenameEvalParser.cpp",
+ "TestSecureContext.cpp",
+ "TestSmartCrashTrimmer.cpp",
+]
+
+if CONFIG["OS_TARGET"] != "Android":
+ UNIFIED_SOURCES += [
+ "TestUnexpectedPrivilegedLoads.cpp",
+ ]
+
+FINAL_LIBRARY = "xul-gtest"
+
+LOCAL_INCLUDES += [
+ "/caps",
+ "/toolkit/components/telemetry/",
+ "/toolkit/components/telemetry/tests/gtest",
+]