| 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
|
|
|