OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/frame/csp/CSPDirectiveList.h" | 5 #include "core/frame/csp/CSPDirectiveList.h" |
6 | 6 |
7 #include "core/frame/csp/ContentSecurityPolicy.h" | 7 #include "core/frame/csp/ContentSecurityPolicy.h" |
8 #include "core/frame/csp/SourceListDirective.h" | 8 #include "core/frame/csp/SourceListDirective.h" |
9 #include "platform/network/ContentSecurityPolicyParsers.h" | 9 #include "platform/network/ContentSecurityPolicyParsers.h" |
10 #include "platform/network/ResourceRequest.h" | 10 #include "platform/network/ResourceRequest.h" |
(...skipping 417 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
428 KURL resource = KURL(KURL(), "https://example.test/worker.js"); | 428 KURL resource = KURL(KURL(), "https://example.test/worker.js"); |
429 Member<CSPDirectiveList> directiveList = | 429 Member<CSPDirectiveList> directiveList = |
430 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); | 430 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); |
431 EXPECT_EQ(test.allowed, | 431 EXPECT_EQ(test.allowed, |
432 directiveList->allowWorkerFromSource( | 432 directiveList->allowWorkerFromSource( |
433 resource, ResourceRequest::RedirectStatus::NoRedirect, | 433 resource, ResourceRequest::RedirectStatus::NoRedirect, |
434 ContentSecurityPolicy::SuppressReport)); | 434 ContentSecurityPolicy::SuppressReport)); |
435 } | 435 } |
436 } | 436 } |
437 | 437 |
438 TEST_F(CSPDirectiveListTest, SubsumesBasedOnCSPSourcesOnly) { | |
439 CSPDirectiveList* A = createList( | |
440 "script-src http://*.one.com; img-src https://one.com " | |
441 "http://two.com/imgs/", | |
442 ContentSecurityPolicyHeaderTypeReport); | |
443 | |
444 struct TestCase { | |
445 const std::vector<const char*> policies; | |
446 bool expected; | |
447 bool expectedFirstPolicyOpposite; | |
448 } cases[] = { | |
449 // `listB`, which is not as restrictive as `A`, is not subsumed. | |
450 {{""}, false, true}, | |
451 {{"script-src http://example.com"}, false, false}, | |
452 {{"img-src http://example.com"}, false, false}, | |
453 {{"script-src http://*.one.com"}, false, true}, | |
454 {{"img-src https://one.com http://two.com/imgs/"}, false, true}, | |
455 {{"default-src http://example.com"}, false, false}, | |
456 {{"default-src https://one.com http://two.com/imgs/"}, false, false}, | |
457 {{"default-src http://one.com"}, false, false}, | |
458 {{"script-src http://*.one.com; img-src http://two.com/"}, false, false}, | |
459 {{"script-src http://*.one.com", "img-src http://one.com"}, false, true}, | |
460 {{"script-src http://*.one.com", "script-src https://two.com"}, | |
461 false, | |
462 true}, | |
463 {{"script-src http://*.random.com", "script-src https://random.com"}, | |
464 false, | |
465 false}, | |
466 {{"script-src http://one.com", "script-src https://random.com"}, | |
467 false, | |
468 false}, | |
469 {{"script-src http://*.random.com; default-src http://one.com " | |
470 "http://two.com/imgs/", | |
471 "default-src https://random.com"}, | |
472 false, | |
473 false}, | |
474 // `listB`, which is as restrictive as `A`, is subsumed. | |
475 {{"default-src https://one.com"}, true, false}, | |
476 {{"default-src http://random.com", | |
477 "default-src https://non-random.com:*"}, | |
478 true, | |
479 false}, | |
480 {{"script-src http://*.one.com; img-src https://one.com"}, true, false}, | |
481 {{"script-src http://*.one.com; img-src https://one.com " | |
482 "http://two.com/imgs/"}, | |
483 true, | |
484 true}, | |
485 {{"script-src http://*.one.com", | |
486 "img-src https://one.com http://two.com/imgs/"}, | |
487 true, | |
488 true}, | |
489 {{"script-src http://*.random.com; default-src https://one.com " | |
490 "http://two.com/imgs/", | |
491 "default-src https://else.com"}, | |
492 true, | |
493 false}, | |
494 {{"script-src http://*.random.com; default-src https://one.com " | |
495 "http://two.com/imgs/", | |
496 "default-src https://one.com"}, | |
497 true, | |
498 false}, | |
499 }; | |
500 | |
501 CSPDirectiveList* emptyA = | |
502 createList("", ContentSecurityPolicyHeaderTypeReport); | |
503 | |
504 for (const auto& test : cases) { | |
505 HeapVector<Member<CSPDirectiveList>> listB; | |
506 for (const auto& policy : test.policies) { | |
507 listB.append(createList(policy, ContentSecurityPolicyHeaderTypeReport)); | |
508 } | |
509 | |
510 EXPECT_EQ(test.expected, A->subsumes(listB)); | |
511 // Empty CSPDirective subsumes any list. | |
512 EXPECT_TRUE(emptyA->subsumes(listB)); | |
513 // Check if first policy of `listB` subsumes `A`. | |
514 EXPECT_EQ(test.expectedFirstPolicyOpposite, | |
515 listB[0]->subsumes(HeapVector<Member<CSPDirectiveList>>(1, A))); | |
516 } | |
517 } | |
518 | |
519 TEST_F(CSPDirectiveListTest, OperativeDirectiveGivenType) { | |
520 enum DefaultBehaviour { Default, NoDefault, ChildAndDefault }; | |
521 | |
522 struct TestCase { | |
523 ContentSecurityPolicy::DirectiveType directive; | |
524 const DefaultBehaviour type; | |
525 } cases[] = { | |
526 // Directives with default directive. | |
527 {ContentSecurityPolicy::DirectiveType::ChildSrc, Default}, | |
528 {ContentSecurityPolicy::DirectiveType::ConnectSrc, Default}, | |
529 {ContentSecurityPolicy::DirectiveType::FontSrc, Default}, | |
530 {ContentSecurityPolicy::DirectiveType::ImgSrc, Default}, | |
531 {ContentSecurityPolicy::DirectiveType::ManifestSrc, Default}, | |
532 {ContentSecurityPolicy::DirectiveType::MediaSrc, Default}, | |
533 {ContentSecurityPolicy::DirectiveType::ObjectSrc, Default}, | |
534 {ContentSecurityPolicy::DirectiveType::ScriptSrc, Default}, | |
535 {ContentSecurityPolicy::DirectiveType::StyleSrc, Default}, | |
536 // Directives with no default directive. | |
537 {ContentSecurityPolicy::DirectiveType::BaseURI, NoDefault}, | |
538 {ContentSecurityPolicy::DirectiveType::DefaultSrc, NoDefault}, | |
539 {ContentSecurityPolicy::DirectiveType::FrameAncestors, NoDefault}, | |
540 {ContentSecurityPolicy::DirectiveType::FormAction, NoDefault}, | |
541 // Directive with multiple default directives. | |
542 {ContentSecurityPolicy::DirectiveType::FrameSrc, ChildAndDefault}, | |
543 {ContentSecurityPolicy::DirectiveType::WorkerSrc, ChildAndDefault}, | |
544 }; | |
545 | |
546 // Initial set-up. | |
547 std::stringstream allDirectives; | |
548 for (const auto& test : cases) { | |
549 const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); | |
550 allDirectives << name << " http://" << name << ".com; "; | |
551 } | |
552 CSPDirectiveList* allDirectivesList = createList( | |
553 allDirectives.str().c_str(), ContentSecurityPolicyHeaderTypeReport); | |
554 CSPDirectiveList* empty = | |
555 createList("", ContentSecurityPolicyHeaderTypeReport); | |
556 | |
557 for (const auto& test : cases) { | |
558 const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); | |
559 // When CSPDirectiveList is empty, then `null` should be returned for any | |
560 // type. | |
561 EXPECT_FALSE(empty->operativeDirective(test.directive)); | |
562 | |
563 // When all directives present, then given a type that directive value | |
564 // should be returned. | |
565 HeapVector<Member<CSPSource>> sources = | |
566 allDirectivesList->operativeDirective(test.directive)->m_list; | |
567 EXPECT_EQ(sources.size(), 1u); | |
568 EXPECT_TRUE(sources[0]->m_host.startsWith(name)); | |
569 | |
570 std::stringstream allExceptThis; | |
571 std::stringstream allExceptChildSrcAndThis; | |
572 for (const auto& subtest : cases) { | |
573 if (subtest.directive == test.directive) | |
574 continue; | |
575 const char* directiveName = | |
576 ContentSecurityPolicy::getDirectiveName(subtest.directive); | |
577 allExceptThis << directiveName << " http://" << directiveName << ".com; "; | |
578 if (subtest.directive != ContentSecurityPolicy::DirectiveType::ChildSrc) { | |
579 allExceptChildSrcAndThis << directiveName << " http://" << directiveName | |
580 << ".com; "; | |
581 } | |
582 } | |
583 CSPDirectiveList* allExceptThisList = createList( | |
584 allExceptThis.str().c_str(), ContentSecurityPolicyHeaderTypeReport); | |
585 CSPDirectiveList* allExceptChildSrcAndThisList = | |
amalika
2016/11/24 15:38:20
to check that frame-src and worker-src default to
| |
586 createList(allExceptChildSrcAndThis.str().c_str(), | |
587 ContentSecurityPolicyHeaderTypeReport); | |
588 | |
589 switch (test.type) { | |
590 case Default: | |
591 sources = allExceptThisList->operativeDirective(test.directive)->m_list; | |
592 EXPECT_EQ(sources.size(), 1u); | |
593 EXPECT_EQ(sources[0]->m_host, "default-src.com"); | |
594 break; | |
595 case NoDefault: | |
596 EXPECT_FALSE(allExceptThisList->operativeDirective(test.directive)); | |
597 break; | |
598 case ChildAndDefault: | |
599 sources = allExceptThisList->operativeDirective(test.directive)->m_list; | |
600 EXPECT_EQ(sources.size(), 1u); | |
601 EXPECT_EQ(sources[0]->m_host, "child-src.com"); | |
602 sources = | |
603 allExceptChildSrcAndThisList->operativeDirective(test.directive) | |
604 ->m_list; | |
605 EXPECT_EQ(sources.size(), 1u); | |
606 EXPECT_EQ(sources[0]->m_host, "default-src.com"); | |
607 break; | |
608 } | |
609 } | |
610 } | |
611 | |
612 TEST_F(CSPDirectiveListTest, GetSourceVector) { | |
613 const std::vector<const char*> policies = { | |
614 // Policy 1 | |
615 "default-src https://default-src.com", | |
616 // Policy 2 | |
617 "child-src http://child-src.com", | |
618 // Policy 3 | |
619 "child-src http://child-src.com; default-src https://default-src.com", | |
620 // Policy 4 | |
621 "base-uri http://base-uri.com", | |
622 // Policy 5 | |
623 "frame-src http://frame-src.com"}; | |
624 | |
625 // Check expectations on the initial set-up. | |
626 HeapVector<Member<CSPDirectiveList>> policyVector; | |
627 for (const auto& policy : policies) { | |
628 policyVector.append( | |
629 createList(policy, ContentSecurityPolicyHeaderTypeReport)); | |
630 } | |
631 HeapVector<Member<SourceListDirective>> result = | |
632 CSPDirectiveList::getSourceVector( | |
633 ContentSecurityPolicy::DirectiveType::DefaultSrc, policyVector); | |
634 EXPECT_EQ(result.size(), 2u); | |
635 result = CSPDirectiveList::getSourceVector( | |
636 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector); | |
637 EXPECT_EQ(result.size(), 3u); | |
638 result = CSPDirectiveList::getSourceVector( | |
639 ContentSecurityPolicy::DirectiveType::BaseURI, policyVector); | |
640 EXPECT_EQ(result.size(), 1u); | |
641 result = CSPDirectiveList::getSourceVector( | |
642 ContentSecurityPolicy::DirectiveType::FrameSrc, policyVector); | |
643 EXPECT_EQ(result.size(), 4u); | |
644 | |
645 enum DefaultBehaviour { Default, NoDefault, ChildAndDefault }; | |
646 | |
647 struct TestCase { | |
648 ContentSecurityPolicy::DirectiveType directive; | |
649 const DefaultBehaviour type; | |
650 size_t expectedTotal; | |
651 int expectedCurrent; | |
652 int expectedDefaultSrc; | |
653 int expectedChildSrc; | |
654 } cases[] = { | |
655 // Directives with default directive. | |
656 {ContentSecurityPolicy::DirectiveType::ChildSrc, Default, 4u, 3, 1, 3}, | |
657 {ContentSecurityPolicy::DirectiveType::ConnectSrc, Default, 3u, 1, 2, 0}, | |
658 {ContentSecurityPolicy::DirectiveType::FontSrc, Default, 3u, 1, 2, 0}, | |
659 {ContentSecurityPolicy::DirectiveType::ImgSrc, Default, 3u, 1, 2, 0}, | |
660 {ContentSecurityPolicy::DirectiveType::ManifestSrc, Default, 3u, 1, 2, 0}, | |
661 {ContentSecurityPolicy::DirectiveType::MediaSrc, Default, 3u, 1, 2, 0}, | |
662 {ContentSecurityPolicy::DirectiveType::ObjectSrc, Default, 3u, 1, 2, 0}, | |
663 {ContentSecurityPolicy::DirectiveType::ScriptSrc, Default, 3u, 1, 2, 0}, | |
664 {ContentSecurityPolicy::DirectiveType::StyleSrc, Default, 3u, 1, 2, 0}, | |
665 // Directives with no default directive. | |
666 {ContentSecurityPolicy::DirectiveType::BaseURI, NoDefault, 2u, 2, 0, 0}, | |
667 {ContentSecurityPolicy::DirectiveType::FrameAncestors, NoDefault, 1u, 1, | |
668 0, 0}, | |
669 {ContentSecurityPolicy::DirectiveType::FormAction, NoDefault, 1u, 1, 0, | |
670 0}, | |
671 // Directive with multiple default directives. | |
672 {ContentSecurityPolicy::DirectiveType::FrameSrc, ChildAndDefault, 5u, 2, | |
673 1, 2}, | |
674 }; | |
675 | |
676 for (const auto& test : cases) { | |
677 // Initial set-up. | |
678 HeapVector<Member<CSPDirectiveList>> policyVector; | |
679 for (const auto& policy : policies) { | |
680 policyVector.append( | |
681 createList(policy, ContentSecurityPolicyHeaderTypeReport)); | |
682 } | |
683 // Append current test's policy. | |
684 std::stringstream currentDirective; | |
685 const char* name = ContentSecurityPolicy::getDirectiveName(test.directive); | |
686 currentDirective << name << " http://" << name << ".com;"; | |
687 policyVector.append(createList(currentDirective.str().c_str(), | |
688 ContentSecurityPolicyHeaderTypeReport)); | |
689 | |
690 HeapVector<Member<SourceListDirective>> result = | |
691 CSPDirectiveList::getSourceVector(test.directive, policyVector); | |
692 | |
693 EXPECT_EQ(result.size(), test.expectedTotal); | |
694 | |
695 int actualCurrent = 0, actualDefault = 0, actualChild = 0; | |
696 for (const auto& srcList : result) { | |
697 HeapVector<Member<CSPSource>> sources = srcList->m_list; | |
698 for (const auto& source : sources) { | |
699 if (source->m_host.startsWith(name)) | |
700 actualCurrent += 1; | |
701 else if (source->m_host == "default-src.com") | |
702 actualDefault += 1; | |
703 | |
704 if (source->m_host == "child-src.com") | |
705 actualChild += 1; | |
706 } | |
707 } | |
708 | |
709 EXPECT_EQ(actualDefault, test.expectedDefaultSrc); | |
710 EXPECT_EQ(actualCurrent, test.expectedCurrent); | |
711 EXPECT_EQ(actualChild, test.expectedChildSrc); | |
712 | |
713 // If another default-src is added that should only impact Fetch Directives | |
714 policyVector.append(createList("default-src https://default-src.com;", | |
715 ContentSecurityPolicyHeaderTypeReport)); | |
716 size_t udpatedTotal = | |
717 test.type != NoDefault ? test.expectedTotal + 1 : test.expectedTotal; | |
718 EXPECT_EQ( | |
719 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), | |
720 udpatedTotal); | |
721 size_t expectedChildSrc = | |
722 test.directive == ContentSecurityPolicy::DirectiveType::ChildSrc ? 5u | |
723 : 4u; | |
724 EXPECT_EQ(CSPDirectiveList::getSourceVector( | |
725 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) | |
726 .size(), | |
727 expectedChildSrc); | |
728 | |
729 // If another child-src is added that should only impact frame-src and | |
730 // child-src | |
731 policyVector.append(createList("child-src http://child-src.com;", | |
732 ContentSecurityPolicyHeaderTypeReport)); | |
733 udpatedTotal = | |
734 test.type == ChildAndDefault || | |
735 test.directive == ContentSecurityPolicy::DirectiveType::ChildSrc | |
736 ? udpatedTotal + 1 | |
737 : udpatedTotal; | |
738 EXPECT_EQ( | |
739 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), | |
740 udpatedTotal); | |
741 expectedChildSrc = expectedChildSrc + 1u; | |
742 EXPECT_EQ(CSPDirectiveList::getSourceVector( | |
743 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) | |
744 .size(), | |
745 expectedChildSrc); | |
746 | |
747 // If we add sandbox, nothing should change since it is currenly not | |
748 // considered. | |
749 policyVector.append(createList("sandbox http://sandbox.com;", | |
750 ContentSecurityPolicyHeaderTypeReport)); | |
751 EXPECT_EQ( | |
752 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), | |
753 udpatedTotal); | |
754 EXPECT_EQ(CSPDirectiveList::getSourceVector( | |
755 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) | |
756 .size(), | |
757 expectedChildSrc); | |
758 } | |
759 } | |
760 | |
438 } // namespace blink | 761 } // namespace blink |
OLD | NEW |