Index: third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp |
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp |
index 2fcde837e88b691ee4073c580e1ce9c8423735d5..48c4aaeef6020f9b283d81659efab9571798a354 100644 |
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp |
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp |
@@ -435,4 +435,327 @@ TEST_F(CSPDirectiveListTest, workerSrc) { |
} |
} |
+TEST_F(CSPDirectiveListTest, SubsumesBasedOnCSPSourcesOnly) { |
+ CSPDirectiveList* A = createList( |
+ "script-src http://*.one.com; img-src https://one.com " |
+ "http://two.com/imgs/", |
+ ContentSecurityPolicyHeaderTypeEnforce); |
+ |
+ struct TestCase { |
+ const std::vector<const char*> policies; |
+ bool expected; |
+ bool expectedFirstPolicyOpposite; |
+ } cases[] = { |
+ // `listB`, which is not as restrictive as `A`, is not subsumed. |
+ {{""}, false, true}, |
+ {{"script-src http://example.com"}, false, false}, |
+ {{"img-src http://example.com"}, false, false}, |
+ {{"script-src http://*.one.com"}, false, true}, |
+ {{"img-src https://one.com http://two.com/imgs/"}, false, true}, |
+ {{"default-src http://example.com"}, false, false}, |
+ {{"default-src https://one.com http://two.com/imgs/"}, false, false}, |
+ {{"default-src http://one.com"}, false, false}, |
+ {{"script-src http://*.one.com; img-src http://two.com/"}, false, false}, |
+ {{"script-src http://*.one.com", "img-src http://one.com"}, false, true}, |
+ {{"script-src http://*.one.com", "script-src https://two.com"}, |
+ false, |
+ true}, |
+ {{"script-src http://*.random.com", "script-src https://random.com"}, |
+ false, |
+ false}, |
+ {{"script-src http://one.com", "script-src https://random.com"}, |
+ false, |
+ false}, |
+ {{"script-src http://*.random.com; default-src http://one.com " |
+ "http://two.com/imgs/", |
+ "default-src https://random.com"}, |
+ false, |
+ false}, |
+ // `listB`, which is as restrictive as `A`, is subsumed. |
+ {{"default-src https://one.com"}, true, false}, |
+ {{"default-src http://random.com", |
+ "default-src https://non-random.com:*"}, |
+ true, |
+ false}, |
+ {{"script-src http://*.one.com; img-src https://one.com"}, true, false}, |
+ {{"script-src http://*.one.com; img-src https://one.com " |
+ "http://two.com/imgs/"}, |
+ true, |
+ true}, |
+ {{"script-src http://*.one.com", |
+ "img-src https://one.com http://two.com/imgs/"}, |
+ true, |
+ true}, |
+ {{"script-src http://*.random.com; default-src https://one.com " |
+ "http://two.com/imgs/", |
+ "default-src https://else.com"}, |
+ true, |
+ false}, |
+ {{"script-src http://*.random.com; default-src https://one.com " |
+ "http://two.com/imgs/", |
+ "default-src https://one.com"}, |
+ true, |
+ false}, |
+ }; |
+ |
+ CSPDirectiveList* emptyA = |
+ createList("", ContentSecurityPolicyHeaderTypeEnforce); |
+ |
+ for (const auto& test : cases) { |
+ HeapVector<Member<CSPDirectiveList>> listB; |
+ for (const auto& policy : test.policies) { |
+ listB.append(createList(policy, ContentSecurityPolicyHeaderTypeEnforce)); |
+ } |
+ |
+ EXPECT_EQ(test.expected, A->subsumes(listB)); |
+ // Empty CSPDirective subsumes any list. |
+ EXPECT_TRUE(emptyA->subsumes(listB)); |
+ // Check if first policy of `listB` subsumes `A`. |
+ EXPECT_EQ(test.expectedFirstPolicyOpposite, |
+ listB[0]->subsumes(HeapVector<Member<CSPDirectiveList>>(1, A))); |
+ } |
+} |
+ |
+TEST_F(CSPDirectiveListTest, OperativeDirectiveGivenType) { |
+ enum DefaultBehaviour { Default, NoDefault, ChildAndDefault }; |
+ |
+ struct TestCase { |
+ ContentSecurityPolicy::DirectiveType directive; |
+ const DefaultBehaviour type; |
+ } cases[] = { |
+ // Directives with default directive. |
+ {ContentSecurityPolicy::DirectiveType::ChildSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::ConnectSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::FontSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::ImgSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::ManifestSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::MediaSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::ObjectSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::ScriptSrc, Default}, |
+ {ContentSecurityPolicy::DirectiveType::StyleSrc, Default}, |
+ // Directives with no default directive. |
+ {ContentSecurityPolicy::DirectiveType::BaseURI, NoDefault}, |
+ {ContentSecurityPolicy::DirectiveType::DefaultSrc, NoDefault}, |
+ {ContentSecurityPolicy::DirectiveType::FrameAncestors, NoDefault}, |
+ {ContentSecurityPolicy::DirectiveType::FormAction, NoDefault}, |
+ // Directive with multiple default directives. |
+ {ContentSecurityPolicy::DirectiveType::FrameSrc, ChildAndDefault}, |
+ {ContentSecurityPolicy::DirectiveType::WorkerSrc, ChildAndDefault}, |
+ }; |
+ |
+ // Initial set-up. |
+ std::stringstream allDirectives; |
+ for (const auto& test : cases) { |
+ const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); |
+ allDirectives << name << " http://" << name << ".com; "; |
+ } |
+ CSPDirectiveList* allDirectivesList = createList( |
+ allDirectives.str().c_str(), ContentSecurityPolicyHeaderTypeEnforce); |
+ CSPDirectiveList* empty = |
+ createList("", ContentSecurityPolicyHeaderTypeEnforce); |
+ |
+ for (const auto& test : cases) { |
+ const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); |
+ // When CSPDirectiveList is empty, then `null` should be returned for any |
+ // type. |
+ EXPECT_FALSE(empty->operativeDirective(test.directive)); |
+ |
+ // When all directives present, then given a type that directive value |
+ // should be returned. |
+ HeapVector<Member<CSPSource>> sources = |
+ allDirectivesList->operativeDirective(test.directive)->m_list; |
+ EXPECT_EQ(sources.size(), 1u); |
+ EXPECT_TRUE(sources[0]->m_host.startsWith(name)); |
+ |
+ std::stringstream allExceptThis; |
+ std::stringstream allExceptChildSrcAndThis; |
+ for (const auto& subtest : cases) { |
+ if (subtest.directive == test.directive) |
+ continue; |
+ const char* directiveName = |
+ ContentSecurityPolicy::getDirectiveName(subtest.directive); |
+ allExceptThis << directiveName << " http://" << directiveName << ".com; "; |
+ if (subtest.directive != ContentSecurityPolicy::DirectiveType::ChildSrc) { |
+ allExceptChildSrcAndThis << directiveName << " http://" << directiveName |
+ << ".com; "; |
+ } |
+ } |
+ CSPDirectiveList* allExceptThisList = createList( |
+ allExceptThis.str().c_str(), ContentSecurityPolicyHeaderTypeEnforce); |
+ CSPDirectiveList* allExceptChildSrcAndThisList = |
+ createList(allExceptChildSrcAndThis.str().c_str(), |
+ ContentSecurityPolicyHeaderTypeEnforce); |
+ |
+ switch (test.type) { |
+ case Default: |
+ sources = allExceptThisList->operativeDirective(test.directive)->m_list; |
+ EXPECT_EQ(sources.size(), 1u); |
+ EXPECT_EQ(sources[0]->m_host, "default-src.com"); |
+ break; |
+ case NoDefault: |
+ EXPECT_FALSE(allExceptThisList->operativeDirective(test.directive)); |
+ break; |
+ case ChildAndDefault: |
+ sources = allExceptThisList->operativeDirective(test.directive)->m_list; |
+ EXPECT_EQ(sources.size(), 1u); |
+ EXPECT_EQ(sources[0]->m_host, "child-src.com"); |
+ sources = |
+ allExceptChildSrcAndThisList->operativeDirective(test.directive) |
+ ->m_list; |
+ EXPECT_EQ(sources.size(), 1u); |
+ EXPECT_EQ(sources[0]->m_host, "default-src.com"); |
+ break; |
+ } |
+ } |
+} |
+ |
+TEST_F(CSPDirectiveListTest, GetSourceVector) { |
+ const std::vector<const char*> policies = { |
+ // Policy 1 |
+ "default-src https://default-src.com", |
+ // Policy 2 |
+ "child-src http://child-src.com", |
+ // Policy 3 |
+ "child-src http://child-src.com; default-src https://default-src.com", |
+ // Policy 4 |
+ "base-uri http://base-uri.com", |
+ // Policy 5 |
+ "frame-src http://frame-src.com"}; |
+ |
+ // Check expectations on the initial set-up. |
+ HeapVector<Member<CSPDirectiveList>> policyVector; |
+ for (const auto& policy : policies) { |
+ policyVector.append( |
+ createList(policy, ContentSecurityPolicyHeaderTypeEnforce)); |
+ } |
+ HeapVector<Member<SourceListDirective>> result = |
+ CSPDirectiveList::getSourceVector( |
+ ContentSecurityPolicy::DirectiveType::DefaultSrc, policyVector); |
+ EXPECT_EQ(result.size(), 2u); |
+ result = CSPDirectiveList::getSourceVector( |
+ ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector); |
+ EXPECT_EQ(result.size(), 3u); |
+ result = CSPDirectiveList::getSourceVector( |
+ ContentSecurityPolicy::DirectiveType::BaseURI, policyVector); |
+ EXPECT_EQ(result.size(), 1u); |
+ result = CSPDirectiveList::getSourceVector( |
+ ContentSecurityPolicy::DirectiveType::FrameSrc, policyVector); |
+ EXPECT_EQ(result.size(), 4u); |
+ |
+ enum DefaultBehaviour { Default, NoDefault, ChildAndDefault }; |
+ |
+ struct TestCase { |
+ ContentSecurityPolicy::DirectiveType directive; |
+ const DefaultBehaviour type; |
+ size_t expectedTotal; |
+ int expectedCurrent; |
+ int expectedDefaultSrc; |
+ int expectedChildSrc; |
+ } cases[] = { |
+ // Directives with default directive. |
+ {ContentSecurityPolicy::DirectiveType::ChildSrc, Default, 4u, 3, 1, 3}, |
+ {ContentSecurityPolicy::DirectiveType::ConnectSrc, Default, 3u, 1, 2, 0}, |
+ {ContentSecurityPolicy::DirectiveType::FontSrc, Default, 3u, 1, 2, 0}, |
+ {ContentSecurityPolicy::DirectiveType::ImgSrc, Default, 3u, 1, 2, 0}, |
+ {ContentSecurityPolicy::DirectiveType::ManifestSrc, Default, 3u, 1, 2, 0}, |
+ {ContentSecurityPolicy::DirectiveType::MediaSrc, Default, 3u, 1, 2, 0}, |
+ {ContentSecurityPolicy::DirectiveType::ObjectSrc, Default, 3u, 1, 2, 0}, |
+ {ContentSecurityPolicy::DirectiveType::ScriptSrc, Default, 3u, 1, 2, 0}, |
+ {ContentSecurityPolicy::DirectiveType::StyleSrc, Default, 3u, 1, 2, 0}, |
+ // Directives with no default directive. |
+ {ContentSecurityPolicy::DirectiveType::BaseURI, NoDefault, 2u, 2, 0, 0}, |
+ {ContentSecurityPolicy::DirectiveType::FrameAncestors, NoDefault, 1u, 1, |
+ 0, 0}, |
+ {ContentSecurityPolicy::DirectiveType::FormAction, NoDefault, 1u, 1, 0, |
+ 0}, |
+ // Directive with multiple default directives. |
+ {ContentSecurityPolicy::DirectiveType::FrameSrc, ChildAndDefault, 5u, 2, |
+ 1, 2}, |
+ }; |
+ |
+ for (const auto& test : cases) { |
+ // Initial set-up. |
+ HeapVector<Member<CSPDirectiveList>> policyVector; |
+ for (const auto& policy : policies) { |
+ policyVector.append( |
+ createList(policy, ContentSecurityPolicyHeaderTypeEnforce)); |
+ } |
+ // Append current test's policy. |
+ std::stringstream currentDirective; |
+ const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); |
+ currentDirective << name << " http://" << name << ".com;"; |
+ policyVector.append(createList(currentDirective.str().c_str(), |
+ ContentSecurityPolicyHeaderTypeEnforce)); |
+ |
+ HeapVector<Member<SourceListDirective>> result = |
+ CSPDirectiveList::getSourceVector(test.directive, policyVector); |
+ |
+ EXPECT_EQ(result.size(), test.expectedTotal); |
+ |
+ int actualCurrent = 0, actualDefault = 0, actualChild = 0; |
+ for (const auto& srcList : result) { |
+ HeapVector<Member<CSPSource>> sources = srcList->m_list; |
+ for (const auto& source : sources) { |
+ if (source->m_host.startsWith(name)) |
+ actualCurrent += 1; |
+ else if (source->m_host == "default-src.com") |
+ actualDefault += 1; |
+ |
+ if (source->m_host == "child-src.com") |
+ actualChild += 1; |
+ } |
+ } |
+ |
+ EXPECT_EQ(actualDefault, test.expectedDefaultSrc); |
+ EXPECT_EQ(actualCurrent, test.expectedCurrent); |
+ EXPECT_EQ(actualChild, test.expectedChildSrc); |
+ |
+ // If another default-src is added that should only impact Fetch Directives |
+ policyVector.append(createList("default-src https://default-src.com;", |
+ ContentSecurityPolicyHeaderTypeEnforce)); |
+ size_t udpatedTotal = |
+ test.type != NoDefault ? test.expectedTotal + 1 : test.expectedTotal; |
+ EXPECT_EQ( |
+ CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), |
+ udpatedTotal); |
+ size_t expectedChildSrc = |
+ test.directive == ContentSecurityPolicy::DirectiveType::ChildSrc ? 5u |
+ : 4u; |
+ EXPECT_EQ(CSPDirectiveList::getSourceVector( |
+ ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) |
+ .size(), |
+ expectedChildSrc); |
+ |
+ // If another child-src is added that should only impact frame-src and |
+ // child-src |
+ policyVector.append(createList("child-src http://child-src.com;", |
+ ContentSecurityPolicyHeaderTypeEnforce)); |
+ udpatedTotal = |
+ test.type == ChildAndDefault || |
+ test.directive == ContentSecurityPolicy::DirectiveType::ChildSrc |
+ ? udpatedTotal + 1 |
+ : udpatedTotal; |
+ EXPECT_EQ( |
+ CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), |
+ udpatedTotal); |
+ expectedChildSrc = expectedChildSrc + 1u; |
+ EXPECT_EQ(CSPDirectiveList::getSourceVector( |
+ ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) |
+ .size(), |
+ expectedChildSrc); |
+ |
+ // If we add sandbox, nothing should change since it is currenly not |
+ // considered. |
+ policyVector.append(createList("sandbox http://sandbox.com;", |
+ ContentSecurityPolicyHeaderTypeEnforce)); |
+ EXPECT_EQ( |
+ CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), |
+ udpatedTotal); |
+ EXPECT_EQ(CSPDirectiveList::getSourceVector( |
+ ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) |
+ .size(), |
+ expectedChildSrc); |
+ } |
+} |
+ |
} // namespace blink |