blob: 81363ca659f039bae377502bcd15fea24d96848d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "base/test/scoped_feature_list.h"
#include "services/network/public/cpp/features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/security_context/insecure_request_policy.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/document_init.h"
#include "third_party/blink/renderer/core/frame/csp/csp_directive_list.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/html/html_script_element.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/core/testing/null_execution_context.h"
#include "third_party/blink/renderer/platform/crypto.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
#include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
using network::mojom::ContentSecurityPolicySource;
using network::mojom::ContentSecurityPolicyType;
class ContentSecurityPolicyTest : public testing::Test {
public:
ContentSecurityPolicyTest()
: csp(MakeGarbageCollected<ContentSecurityPolicy>()),
secure_url("https://example.test/index.html"),
secure_origin(SecurityOrigin::Create(secure_url)) {}
~ContentSecurityPolicyTest() override {
execution_context->NotifyContextDestroyed();
}
protected:
void SetUp() override { CreateExecutionContext(); }
void CreateExecutionContext() {
if (execution_context)
execution_context->NotifyContextDestroyed();
execution_context = MakeGarbageCollected<NullExecutionContext>();
execution_context->SetUpSecurityContextForTesting();
execution_context->GetSecurityContext().SetSecurityOriginForTesting(
secure_origin);
}
Persistent<ContentSecurityPolicy> csp;
KURL secure_url;
scoped_refptr<SecurityOrigin> secure_origin;
Persistent<NullExecutionContext> execution_context;
};
TEST_F(ContentSecurityPolicyTest, ParseInsecureRequestPolicy) {
struct TestCase {
const char* header;
mojom::blink::InsecureRequestPolicy expected_policy;
} cases[] = {
{"default-src 'none'",
mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone},
{"upgrade-insecure-requests",
mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests},
{"block-all-mixed-content",
mojom::blink::InsecureRequestPolicy::kBlockAllMixedContent},
{"upgrade-insecure-requests; block-all-mixed-content",
mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests |
mojom::blink::InsecureRequestPolicy::kBlockAllMixedContent},
{"upgrade-insecure-requests, block-all-mixed-content",
mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests |
mojom::blink::InsecureRequestPolicy::kBlockAllMixedContent}};
// Enforced
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "[Enforce] Header: `" << test.header << "`");
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader(test.header, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(test.expected_policy, csp->GetInsecureRequestPolicy());
auto dummy = std::make_unique<DummyPageHolder>();
dummy->GetDocument().SetURL(secure_url);
auto& security_context =
dummy->GetFrame().DomWindow()->GetSecurityContext();
security_context.SetSecurityOriginForTesting(secure_origin);
csp->BindToDelegate(
dummy->GetFrame().DomWindow()->GetContentSecurityPolicyDelegate());
EXPECT_EQ(test.expected_policy,
security_context.GetInsecureRequestPolicy());
bool expect_upgrade =
(test.expected_policy &
mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests) !=
mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone;
EXPECT_EQ(expect_upgrade,
security_context.InsecureNavigationsToUpgrade().Contains(
dummy->GetDocument().Url().Host().Impl()->GetHash()));
}
// Report-Only
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "[Report-Only] Header: `" << test.header << "`");
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader(test.header, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
csp->GetInsecureRequestPolicy());
CreateExecutionContext();
execution_context->GetSecurityContext().SetSecurityOrigin(secure_origin);
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
EXPECT_EQ(
mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
execution_context->GetSecurityContext().GetInsecureRequestPolicy());
EXPECT_FALSE(execution_context->GetSecurityContext()
.InsecureNavigationsToUpgrade()
.Contains(secure_origin->Host().Impl()->GetHash()));
}
}
TEST_F(ContentSecurityPolicyTest, CopyStateFrom) {
csp->DidReceiveHeader("script-src 'none'", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
csp->DidReceiveHeader("img-src http://example.com", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
const KURL example_url("http://example.com");
const KURL not_example_url("http://not-example.com");
auto* csp2 = MakeGarbageCollected<ContentSecurityPolicy>();
csp2->CopyStateFrom(csp.Get());
EXPECT_FALSE(csp2->AllowScriptFromSource(
example_url, String(), IntegrityMetadataSet(), kParserInserted,
example_url, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting,
ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly));
EXPECT_TRUE(csp2->AllowImageFromSource(
example_url, example_url, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting,
ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly));
EXPECT_FALSE(csp2->AllowImageFromSource(
not_example_url, not_example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting,
ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly));
}
TEST_F(ContentSecurityPolicyTest, IsActiveForConnectionsWithConnectSrc) {
EXPECT_FALSE(csp->IsActiveForConnections());
csp->DidReceiveHeader("connect-src 'none';", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(csp->IsActiveForConnections());
}
TEST_F(ContentSecurityPolicyTest, IsActiveForConnectionsWithDefaultSrc) {
EXPECT_FALSE(csp->IsActiveForConnections());
csp->DidReceiveHeader("default-src 'none';", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(csp->IsActiveForConnections());
}
// Tests that sandbox directives are discarded from policies
// delivered in <meta> elements.
TEST_F(ContentSecurityPolicyTest, SandboxInMeta) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
EXPECT_EQ(network::mojom::blink::WebSandboxFlags::kNone,
csp->GetSandboxMask());
csp->DidReceiveHeader("sandbox;", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kMeta);
EXPECT_EQ(network::mojom::blink::WebSandboxFlags::kNone,
csp->GetSandboxMask());
execution_context->GetSecurityContext().SetSandboxFlags(
network::mojom::blink::WebSandboxFlags::kAll);
csp->DidReceiveHeader("sandbox;", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(network::mojom::blink::WebSandboxFlags::kAll,
csp->GetSandboxMask());
}
// Tests that object-src directives are applied to a request to load a
// plugin, but not to subresource requests that the plugin itself
// makes. https://crbug.com/603952
TEST_F(ContentSecurityPolicyTest, ObjectSrc) {
const KURL url("https://example.test");
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("object-src 'none';", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kMeta);
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty,
url, String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::EMBED,
network::mojom::RequestDestination::kEmbed,
url, String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::PLUGIN,
network::mojom::RequestDestination::kEmpty, url,
String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
}
TEST_F(ContentSecurityPolicyTest, ConnectSrc) {
const KURL url("https://example.test");
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("connect-src 'none';", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kMeta);
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::SUBRESOURCE,
network::mojom::RequestDestination::kEmpty,
url, String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(
csp->AllowRequest(mojom::blink::RequestContextType::XML_HTTP_REQUEST,
network::mojom::RequestDestination::kEmpty, url,
String(), IntegrityMetadataSet(), kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::BEACON,
network::mojom::RequestDestination::kEmpty,
url, String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::FETCH,
network::mojom::RequestDestination::kEmpty,
url, String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::PLUGIN,
network::mojom::RequestDestination::kEmpty, url,
String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
}
TEST_F(ContentSecurityPolicyTest, NonceSinglePolicy) {
struct TestCase {
const char* policy;
const char* url;
const char* nonce;
bool allowed;
} cases[] = {
{"script-src 'nonce-yay'", "https://example.com/js", "", false},
{"script-src 'nonce-yay'", "https://example.com/js", "yay", true},
{"script-src https://example.com", "https://example.com/js", "", true},
{"script-src https://example.com", "https://example.com/js", "yay", true},
{"script-src https://example.com 'nonce-yay'",
"https://not.example.com/js", "", false},
{"script-src https://example.com 'nonce-yay'",
"https://not.example.com/js", "yay", true},
};
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "Policy: `" << test.policy << "`, URL: `" << test.url
<< "`, Nonce: `" << test.nonce << "`");
const KURL resource(test.url);
unsigned expected_reports = test.allowed ? 0u : 1u;
// Single enforce-mode policy should match `test.expected`:
Persistent<ContentSecurityPolicy> policy =
MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(
execution_context->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(test.policy, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(test.allowed,
policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(),
kParserInserted, resource,
ResourceRequest::RedirectStatus::kNoRedirect));
// If this is expected to generate a violation, we should have sent a
// report.
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
// Single report-mode policy should always be `true`:
policy = MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(
execution_context->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(test.policy, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(), kParserInserted,
resource, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kReport,
ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly));
// If this is expected to generate a violation, we should have sent a
// report, even though we don't deny access in `allowScriptFromSource`:
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
}
}
TEST_F(ContentSecurityPolicyTest, NonceInline) {
struct TestCase {
const char* policy;
const char* nonce;
bool allowed;
} cases[] = {
{"'unsafe-inline'", "", true},
{"'unsafe-inline'", "yay", true},
{"'nonce-yay'", "", false},
{"'nonce-yay'", "yay", true},
{"'unsafe-inline' 'nonce-yay'", "", false},
{"'unsafe-inline' 'nonce-yay'", "yay", true},
};
String context_url;
String content;
WTF::OrdinalNumber context_line;
// We need document for HTMLScriptElement tests.
auto dummy = std::make_unique<DummyPageHolder>();
auto* window = dummy->GetFrame().DomWindow();
window->GetSecurityContext().SetSecurityOriginForTesting(secure_origin);
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message() << "Policy: `" << test.policy
<< "`, Nonce: `" << test.nonce << "`");
unsigned expected_reports = test.allowed ? 0u : 1u;
auto* element = MakeGarbageCollected<HTMLScriptElement>(
*window->document(), CreateElementFlags());
// Enforce 'script-src'
Persistent<ContentSecurityPolicy> policy =
MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(window->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(String("script-src ") + test.policy,
*secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(test.allowed,
policy->AllowInline(ContentSecurityPolicy::InlineType::kScript,
element, content, String(test.nonce),
context_url, context_line));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
// Enforce 'style-src'
policy = MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(window->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(String("style-src ") + test.policy, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(test.allowed,
policy->AllowInline(ContentSecurityPolicy::InlineType::kStyle,
element, content, String(test.nonce),
context_url, context_line));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
// Report 'script-src'
policy = MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(window->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(String("script-src ") + test.policy,
*secure_origin, ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(policy->AllowInline(ContentSecurityPolicy::InlineType::kScript,
element, content, String(test.nonce),
context_url, context_line));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
// Report 'style-src'
policy = MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(window->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(String("style-src ") + test.policy, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(policy->AllowInline(ContentSecurityPolicy::InlineType::kStyle,
element, content, String(test.nonce),
context_url, context_line));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
}
}
TEST_F(ContentSecurityPolicyTest, NonceMultiplePolicy) {
struct TestCase {
const char* policy1;
const char* policy2;
const char* url;
const char* nonce;
bool allowed1;
bool allowed2;
} cases[] = {
// Passes both:
{"script-src 'nonce-yay'", "script-src 'nonce-yay'",
"https://example.com/js", "yay", true, true},
{"script-src https://example.com", "script-src 'nonce-yay'",
"https://example.com/js", "yay", true, true},
{"script-src 'nonce-yay'", "script-src https://example.com",
"https://example.com/js", "yay", true, true},
{"script-src https://example.com 'nonce-yay'",
"script-src https://example.com 'nonce-yay'", "https://example.com/js",
"yay", true, true},
{"script-src https://example.com 'nonce-yay'",
"script-src https://example.com 'nonce-yay'", "https://example.com/js",
"", true, true},
{"script-src https://example.com",
"script-src https://example.com 'nonce-yay'", "https://example.com/js",
"yay", true, true},
{"script-src https://example.com 'nonce-yay'",
"script-src https://example.com", "https://example.com/js", "yay", true,
true},
// Fails one:
{"script-src 'nonce-yay'", "script-src https://example.com",
"https://example.com/js", "", false, true},
{"script-src 'nonce-yay'", "script-src 'none'", "https://example.com/js",
"yay", true, false},
{"script-src 'nonce-yay'", "script-src https://not.example.com",
"https://example.com/js", "yay", true, false},
// Fails both:
{"script-src 'nonce-yay'", "script-src https://example.com",
"https://not.example.com/js", "", false, false},
{"script-src https://example.com", "script-src 'nonce-yay'",
"https://not.example.com/js", "", false, false},
{"script-src 'nonce-yay'", "script-src 'none'",
"https://not.example.com/js", "boo", false, false},
{"script-src 'nonce-yay'", "script-src https://not.example.com",
"https://example.com/js", "", false, false},
};
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message() << "Policy: `" << test.policy1 << "`/`"
<< test.policy2 << "`, URL: `" << test.url
<< "`, Nonce: `" << test.nonce << "`");
const KURL resource(test.url);
unsigned expected_reports =
test.allowed1 != test.allowed2 ? 1u : (test.allowed1 ? 0u : 2u);
// Enforce / Report
Persistent<ContentSecurityPolicy> policy =
MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(
execution_context->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(test.policy1, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
policy->DidReceiveHeader(test.policy2, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(test.allowed1,
policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(),
kParserInserted, resource,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kReport,
ContentSecurityPolicy::CheckHeaderType::kCheckEnforce));
EXPECT_TRUE(policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(), kParserInserted,
resource, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kReport,
ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
// Report / Enforce
policy = MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(
execution_context->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(test.policy1, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
policy->DidReceiveHeader(test.policy2, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(), kParserInserted,
resource, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kReport,
ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly));
EXPECT_EQ(test.allowed2,
policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(),
kParserInserted, resource,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kReport,
ContentSecurityPolicy::CheckHeaderType::kCheckEnforce));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
// Enforce / Enforce
policy = MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(
execution_context->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(test.policy1, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
policy->DidReceiveHeader(test.policy2, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_EQ(test.allowed1 && test.allowed2,
policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(),
kParserInserted, resource,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kReport,
ContentSecurityPolicy::CheckHeaderType::kCheckEnforce));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
// Report / Report
policy = MakeGarbageCollected<ContentSecurityPolicy>();
policy->BindToDelegate(
execution_context->GetContentSecurityPolicyDelegate());
policy->DidReceiveHeader(test.policy1, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
policy->DidReceiveHeader(test.policy2, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(policy->AllowScriptFromSource(
resource, String(test.nonce), IntegrityMetadataSet(), kParserInserted,
resource, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kReport,
ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly));
EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size());
}
}
TEST_F(ContentSecurityPolicyTest, DirectiveType) {
struct TestCase {
CSPDirectiveName type;
const String& name;
} cases[] = {
{CSPDirectiveName::BaseURI, "base-uri"},
{CSPDirectiveName::BlockAllMixedContent, "block-all-mixed-content"},
{CSPDirectiveName::ChildSrc, "child-src"},
{CSPDirectiveName::ConnectSrc, "connect-src"},
{CSPDirectiveName::DefaultSrc, "default-src"},
{CSPDirectiveName::FrameAncestors, "frame-ancestors"},
{CSPDirectiveName::FrameSrc, "frame-src"},
{CSPDirectiveName::FontSrc, "font-src"},
{CSPDirectiveName::FormAction, "form-action"},
{CSPDirectiveName::ImgSrc, "img-src"},
{CSPDirectiveName::ManifestSrc, "manifest-src"},
{CSPDirectiveName::MediaSrc, "media-src"},
{CSPDirectiveName::NavigateTo, "navigate-to"},
{CSPDirectiveName::ObjectSrc, "object-src"},
{CSPDirectiveName::ReportURI, "report-uri"},
{CSPDirectiveName::Sandbox, "sandbox"},
{CSPDirectiveName::ScriptSrc, "script-src"},
{CSPDirectiveName::ScriptSrcAttr, "script-src-attr"},
{CSPDirectiveName::ScriptSrcElem, "script-src-elem"},
{CSPDirectiveName::StyleSrc, "style-src"},
{CSPDirectiveName::StyleSrcAttr, "style-src-attr"},
{CSPDirectiveName::StyleSrcElem, "style-src-elem"},
{CSPDirectiveName::UpgradeInsecureRequests, "upgrade-insecure-requests"},
{CSPDirectiveName::WorkerSrc, "worker-src"},
};
EXPECT_EQ(CSPDirectiveName::Unknown,
ContentSecurityPolicy::GetDirectiveType("random"));
for (const auto& test : cases) {
const String& name_from_type =
ContentSecurityPolicy::GetDirectiveName(test.type);
CSPDirectiveName type_from_name =
ContentSecurityPolicy::GetDirectiveType(test.name);
EXPECT_EQ(name_from_type, test.name);
EXPECT_EQ(type_from_name, test.type);
EXPECT_EQ(test.type,
ContentSecurityPolicy::GetDirectiveType(name_from_type));
EXPECT_EQ(test.name,
ContentSecurityPolicy::GetDirectiveName(type_from_name));
}
}
TEST_F(ContentSecurityPolicyTest, RequestsAllowedWhenBypassingCSP) {
const KURL base;
CreateExecutionContext();
execution_context->GetSecurityContext().SetSecurityOrigin(
secure_origin); // https://example.com
execution_context->SetURL(secure_url); // https://example.com
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("default-src https://example.com", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
const KURL example_url("https://example.com/");
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty,
example_url, String(), IntegrityMetadataSet(),
kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
const KURL not_example_url("https://not-example.com/");
EXPECT_FALSE(csp->AllowRequest(
mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty, not_example_url, String(),
IntegrityMetadataSet(), kParserInserted, not_example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
// Register "https" as bypassing CSP, which should now bypass it entirely
SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy("https");
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty,
example_url, String(), IntegrityMetadataSet(),
kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_TRUE(csp->AllowRequest(
mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty, not_example_url, String(),
IntegrityMetadataSet(), kParserInserted, not_example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
SchemeRegistry::RemoveURLSchemeRegisteredAsBypassingContentSecurityPolicy(
"https");
}
TEST_F(ContentSecurityPolicyTest, FilesystemAllowedWhenBypassingCSP) {
const KURL base;
CreateExecutionContext();
execution_context->GetSecurityContext().SetSecurityOrigin(
secure_origin); // https://example.com
execution_context->SetURL(secure_url); // https://example.com
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("default-src https://example.com", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
const KURL example_url("filesystem:https://example.com/file.txt");
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty,
example_url, String(), IntegrityMetadataSet(),
kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
const KURL not_example_url("filesystem:https://not-example.com/file.txt");
EXPECT_FALSE(csp->AllowRequest(
mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty, not_example_url, String(),
IntegrityMetadataSet(), kParserInserted, not_example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
// Register "https" as bypassing CSP, which should now bypass it entirely
SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy("https");
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty,
example_url, String(), IntegrityMetadataSet(),
kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_TRUE(csp->AllowRequest(
mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty, not_example_url, String(),
IntegrityMetadataSet(), kParserInserted, not_example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
SchemeRegistry::RemoveURLSchemeRegisteredAsBypassingContentSecurityPolicy(
"https");
}
TEST_F(ContentSecurityPolicyTest, BlobAllowedWhenBypassingCSP) {
const KURL base;
CreateExecutionContext();
execution_context->GetSecurityContext().SetSecurityOrigin(
secure_origin); // https://example.com
execution_context->SetURL(secure_url); // https://example.com
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("default-src https://example.com", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
const KURL example_url("blob:https://example.com/");
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty,
example_url, String(), IntegrityMetadataSet(),
kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
const KURL not_example_url("blob:https://not-example.com/");
EXPECT_FALSE(csp->AllowRequest(
mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty, not_example_url, String(),
IntegrityMetadataSet(), kParserInserted, not_example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
// Register "https" as bypassing CSP, which should now bypass it entirely
SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy("https");
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty,
example_url, String(), IntegrityMetadataSet(),
kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_TRUE(csp->AllowRequest(
mojom::blink::RequestContextType::OBJECT,
network::mojom::RequestDestination::kEmpty, not_example_url, String(),
IntegrityMetadataSet(), kParserInserted, not_example_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
SchemeRegistry::RemoveURLSchemeRegisteredAsBypassingContentSecurityPolicy(
"https");
}
TEST_F(ContentSecurityPolicyTest, CSPBypassDisabledWhenSchemeIsPrivileged) {
const KURL base;
CreateExecutionContext();
execution_context->GetSecurityContext().SetSecurityOrigin(secure_origin);
execution_context->SetURL(BlankURL());
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("script-src http://example.com", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
const KURL allowed_url("http://example.com/script.js");
const KURL http_url("http://not-example.com/script.js");
const KURL blob_url(base, "blob:http://not-example.com/uuid");
const KURL filesystem_url(base, "filesystem:http://not-example.com/file.js");
// The {Requests,Blob,Filesystem}AllowedWhenBypassingCSP tests have already
// ensured that RegisterURLSchemeAsBypassingContentSecurityPolicy works as
// expected.
//
// "http" is registered as bypassing CSP, but the context's scheme ("https")
// is marked as a privileged scheme, so the bypass rule should be ignored.
SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy("http");
SchemeRegistry::RegisterURLSchemeAsNotAllowingJavascriptURLs("https");
EXPECT_TRUE(csp->AllowScriptFromSource(
allowed_url, String(), IntegrityMetadataSet(), kNotParserInserted,
allowed_url, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(csp->AllowScriptFromSource(
http_url, String(), IntegrityMetadataSet(), kNotParserInserted, http_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(csp->AllowScriptFromSource(
blob_url, String(), IntegrityMetadataSet(), kNotParserInserted, blob_url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(csp->AllowScriptFromSource(
filesystem_url, String(), IntegrityMetadataSet(), kNotParserInserted,
filesystem_url, ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
SchemeRegistry::RemoveURLSchemeRegisteredAsBypassingContentSecurityPolicy(
"http");
SchemeRegistry::RemoveURLSchemeAsNotAllowingJavascriptURLs("https");
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesNoDirective) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("", *secure_origin, ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesSimpleDirective) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types one two three", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesWhitespace) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types one\ntwo\rthree", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(csp->AllowTrustedTypePolicy("one", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(csp->AllowTrustedTypePolicy("two", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(csp->AllowTrustedTypePolicy("three", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("four", false, violation_details));
EXPECT_EQ(
violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kDisallowedName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("one", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("four", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesEmpty) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_FALSE(
csp->AllowTrustedTypePolicy("somepolicy", false, violation_details));
EXPECT_EQ(
violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kDisallowedName);
EXPECT_FALSE(
csp->AllowTrustedTypePolicy("somepolicy", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesStar) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types *", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_FALSE(
csp->AllowTrustedTypePolicy("somepolicy", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesStarMix) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types abc * def", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(csp->AllowTrustedTypePolicy("abc", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(csp->AllowTrustedTypePolicy("def", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(csp->AllowTrustedTypePolicy("ghi", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("abc", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("def", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("ghi", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypeDupe) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types somepolicy 'allow-duplicates'",
*secure_origin, ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypeDupeStar) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types * 'allow-duplicates'", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesReserved) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types one \"two\" 'three'", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(csp->AllowTrustedTypePolicy("one", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("one", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
// Quoted strings are considered 'reserved':
EXPECT_FALSE(csp->AllowTrustedTypePolicy("two", false, violation_details));
EXPECT_EQ(
violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kDisallowedName);
EXPECT_FALSE(
csp->AllowTrustedTypePolicy("\"two\"", false, violation_details));
EXPECT_EQ(
violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kDisallowedName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("three", false, violation_details));
EXPECT_EQ(
violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kDisallowedName);
EXPECT_FALSE(
csp->AllowTrustedTypePolicy("'three'", false, violation_details));
EXPECT_EQ(
violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kDisallowedName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("two", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("\"two\"", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("three", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("'three'", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypesReportingStar) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types *", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypeReportingSimple) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types a b c", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(csp->AllowTrustedTypePolicy("a", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(csp->AllowTrustedTypePolicy("a", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, TrustedTypeEnforce) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types one\ntwo\rthree", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_FALSE(csp->IsRequireTrustedTypes());
EXPECT_TRUE(csp->AllowTrustedTypeAssignmentFailure("blabla"));
}
TEST_F(ContentSecurityPolicyTest, TrustedTypeReport) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types one\ntwo\rthree", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_FALSE(csp->IsRequireTrustedTypes());
EXPECT_TRUE(csp->AllowTrustedTypeAssignmentFailure("blabla"));
}
TEST_F(ContentSecurityPolicyTest, TrustedTypeReportAndEnforce) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types one", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
csp->DidReceiveHeader("trusted-types two", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_FALSE(csp->IsRequireTrustedTypes());
EXPECT_TRUE(csp->AllowTrustedTypeAssignmentFailure("blabla"));
}
TEST_F(ContentSecurityPolicyTest, TrustedTypeReportAndNonTTEnforce) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types one", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
csp->DidReceiveHeader("script-src none", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_FALSE(csp->IsRequireTrustedTypes());
EXPECT_TRUE(csp->AllowTrustedTypeAssignmentFailure("blabla"));
}
TEST_F(ContentSecurityPolicyTest, RequireTrustedTypeForEnforce) {
execution_context->GetSecurityContext().SetRequireTrustedTypesForTesting();
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("require-trusted-types-for ''", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_FALSE(csp->IsRequireTrustedTypes());
csp->DidReceiveHeader("require-trusted-types-for 'script'", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(csp->IsRequireTrustedTypes());
}
TEST_F(ContentSecurityPolicyTest, RequireTrustedTypeForReport) {
execution_context->GetSecurityContext().SetRequireTrustedTypesForTesting();
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("require-trusted-types-for 'script'", *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
EXPECT_TRUE(csp->IsRequireTrustedTypes());
}
TEST_F(ContentSecurityPolicyTest, DefaultPolicy) {
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("trusted-types *", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(csp->AllowTrustedTypePolicy("default", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_FALSE(csp->AllowTrustedTypePolicy("default", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::
kDisallowedDuplicateName);
}
TEST_F(ContentSecurityPolicyTest, DirectiveNameCaseInsensitive) {
KURL example_url("http://example.com");
KURL not_example_url("http://not-example.com");
// Directive name is case insensitive.
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader("sCrIpt-sRc http://example.com", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
EXPECT_TRUE(csp->AllowScriptFromSource(
example_url, String(), IntegrityMetadataSet(), kParserInserted,
example_url, ResourceRequest::RedirectStatus::kNoRedirect));
EXPECT_FALSE(csp->AllowScriptFromSource(
not_example_url, String(), IntegrityMetadataSet(), kParserInserted,
not_example_url, ResourceRequest::RedirectStatus::kNoRedirect));
// Duplicate directive that is in a different case pattern is
// correctly treated as a duplicate directive and ignored.
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader(
"SCRipt-SRC http://example.com; script-src http://not-example.com;",
*secure_origin, ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
EXPECT_TRUE(csp->AllowScriptFromSource(
example_url, String(), IntegrityMetadataSet(), kParserInserted,
example_url, ResourceRequest::RedirectStatus::kNoRedirect));
EXPECT_FALSE(csp->AllowScriptFromSource(
not_example_url, String(), IntegrityMetadataSet(), kParserInserted,
not_example_url, ResourceRequest::RedirectStatus::kNoRedirect));
}
// Tests that using an empty CSP works and doesn't impose any policy
// restrictions.
TEST_F(ContentSecurityPolicyTest, EmptyCSPIsNoOp) {
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
const KURL example_url("http://example.com");
auto* document = Document::CreateForTest();
String source;
String context_url;
String nonce;
OrdinalNumber ordinal_number;
auto* element =
MakeGarbageCollected<HTMLScriptElement>(*document, CreateElementFlags());
EXPECT_TRUE(csp->AllowInline(ContentSecurityPolicy::InlineType::kNavigation,
element, source, String() /* nonce */,
context_url, ordinal_number));
EXPECT_TRUE(csp->AllowInline(
ContentSecurityPolicy::InlineType::kScriptAttribute, element, source,
String() /* nonce */, context_url, ordinal_number));
EXPECT_TRUE(csp->AllowEval(ReportingDisposition::kReport,
ContentSecurityPolicy::kWillNotThrowException,
g_empty_string));
EXPECT_TRUE(csp->AllowWasmEval(ReportingDisposition::kReport,
ContentSecurityPolicy::kWillNotThrowException,
g_empty_string));
CSPDirectiveName types_to_test[] = {
CSPDirectiveName::BaseURI, CSPDirectiveName::ConnectSrc,
CSPDirectiveName::FontSrc, CSPDirectiveName::FormAction,
CSPDirectiveName::FrameSrc, CSPDirectiveName::ImgSrc,
CSPDirectiveName::ManifestSrc, CSPDirectiveName::MediaSrc,
CSPDirectiveName::ObjectSrc, CSPDirectiveName::PrefetchSrc,
CSPDirectiveName::ScriptSrcElem, CSPDirectiveName::StyleSrcElem,
CSPDirectiveName::WorkerSrc};
for (auto type : types_to_test) {
EXPECT_TRUE(
csp->AllowFromSource(type, example_url, example_url,
ResourceRequest::RedirectStatus::kNoRedirect));
}
EXPECT_TRUE(csp->AllowObjectFromSource(example_url));
EXPECT_TRUE(csp->AllowImageFromSource(
example_url, example_url, ResourceRequest::RedirectStatus::kNoRedirect));
EXPECT_TRUE(csp->AllowMediaFromSource(example_url));
EXPECT_TRUE(csp->AllowConnectToSource(
example_url, example_url, ResourceRequest::RedirectStatus::kNoRedirect));
EXPECT_TRUE(csp->AllowFormAction(example_url));
EXPECT_TRUE(csp->AllowBaseURI(example_url));
EXPECT_TRUE(csp->AllowWorkerContextFromSource(example_url));
EXPECT_TRUE(csp->AllowScriptFromSource(
example_url, nonce, IntegrityMetadataSet(), kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect));
ContentSecurityPolicy::AllowTrustedTypePolicyDetails violation_details;
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", true, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(
csp->AllowTrustedTypePolicy("somepolicy", false, violation_details));
EXPECT_EQ(violation_details,
ContentSecurityPolicy::AllowTrustedTypePolicyDetails::kAllowed);
EXPECT_TRUE(csp->AllowInline(ContentSecurityPolicy::InlineType::kScript,
element, source, nonce, context_url,
ordinal_number));
EXPECT_TRUE(csp->AllowInline(ContentSecurityPolicy::InlineType::kStyle,
element, source, nonce, context_url,
ordinal_number));
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::SCRIPT,
network::mojom::RequestDestination::kScript,
example_url, nonce, IntegrityMetadataSet(),
kParserInserted, example_url,
ResourceRequest::RedirectStatus::kNoRedirect));
EXPECT_FALSE(csp->IsActive());
EXPECT_FALSE(csp->IsActiveForConnections());
EXPECT_TRUE(csp->FallbackUrlForPlugin().IsEmpty());
EXPECT_EQ(mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
csp->GetInsecureRequestPolicy());
EXPECT_FALSE(csp->HasHeaderDeliveredPolicy());
EXPECT_FALSE(csp->SupportsWasmEval());
EXPECT_EQ(network::mojom::blink::WebSandboxFlags::kNone,
csp->GetSandboxMask());
EXPECT_FALSE(csp->HasPolicyFromSource(ContentSecurityPolicySource::kHTTP));
}
TEST_F(ContentSecurityPolicyTest, OpaqueOriginBeforeBind) {
const KURL url("https://example.test");
// Security Origin of execution context might change when sandbox flags
// are applied. This shouldn't change the application of the 'self'
// determination.
secure_origin = secure_origin->DeriveNewOpaqueOrigin();
CreateExecutionContext();
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("default-src 'self';", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kMeta);
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::SUBRESOURCE,
network::mojom::RequestDestination::kEmpty, url,
String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
}
TEST_F(ContentSecurityPolicyTest, SelfForDataMatchesNothing) {
const KURL url("https://example.test");
auto reference_origin = SecurityOrigin::Create(url);
const KURL data_url("data:text/html,hello");
secure_origin = SecurityOrigin::CreateWithReferenceOrigin(
data_url, reference_origin.get());
CreateExecutionContext();
csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
csp->DidReceiveHeader("default-src 'self';", *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kMeta);
EXPECT_TRUE(csp->AllowRequest(mojom::blink::RequestContextType::SUBRESOURCE,
network::mojom::RequestDestination::kEmpty, url,
String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
EXPECT_FALSE(csp->AllowRequest(mojom::blink::RequestContextType::SUBRESOURCE,
network::mojom::RequestDestination::kEmpty,
data_url, String(), IntegrityMetadataSet(),
kParserInserted, url,
ResourceRequest::RedirectStatus::kNoRedirect,
ReportingDisposition::kSuppressReporting));
}
TEST_F(ContentSecurityPolicyTest, ReasonableRestrictionMetrics) {
struct TestCase {
const char* header;
bool expected_object;
bool expected_base;
bool expected_script;
} cases[] = {{"object-src 'none'", true, false, false},
{"object-src 'none'; base-uri 'none'", true, true, false},
{"object-src 'none'; base-uri 'none'; script-src 'none'", true,
true, true},
{"object-src 'none'; base-uri 'none'; script-src 'nonce-abc'",
true, true, true},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc'",
true, true, true},
{"object-src 'none'; base-uri 'none'; script-src 'nonce-abc' "
"'strict-dynamic'",
true, true, true},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc' "
"'strict-dynamic'",
true, true, true},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc' "
"https://example.com/",
true, true, false},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc' "
"https://example.com/ 'strict-dynamic'",
true, true, true}};
// Enforced
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "[Enforce] Header: `" << test.header << "`");
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader(test.header, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
auto dummy = std::make_unique<DummyPageHolder>();
csp->BindToDelegate(
dummy->GetFrame().DomWindow()->GetContentSecurityPolicyDelegate());
EXPECT_EQ(test.expected_object,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPWithReasonableObjectRestrictions));
EXPECT_EQ(test.expected_base,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPWithReasonableBaseRestrictions));
EXPECT_EQ(test.expected_script,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPWithReasonableScriptRestrictions));
EXPECT_EQ(
test.expected_object && test.expected_base && test.expected_script,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPWithReasonableRestrictions));
}
// Report-Only
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "[ReportOnly] Header: `" << test.header << "`");
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader(test.header, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
auto dummy = std::make_unique<DummyPageHolder>();
csp->BindToDelegate(
dummy->GetFrame().DomWindow()->GetContentSecurityPolicyDelegate());
EXPECT_EQ(test.expected_object,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPROWithReasonableObjectRestrictions));
EXPECT_EQ(test.expected_base,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPROWithReasonableBaseRestrictions));
EXPECT_EQ(test.expected_script,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPROWithReasonableScriptRestrictions));
EXPECT_EQ(
test.expected_object && test.expected_base && test.expected_script,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPROWithReasonableRestrictions));
}
}
TEST_F(ContentSecurityPolicyTest, BetterThanReasonableRestrictionMetrics) {
struct TestCase {
const char* header;
bool expected;
} cases[] = {
{"object-src 'none'", false},
{"object-src 'none'; base-uri 'none'", false},
{"object-src 'none'; base-uri 'none'; script-src 'none'", true},
{"object-src 'none'; base-uri 'none'; script-src 'nonce-abc'", true},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc'", true},
{"object-src 'none'; base-uri 'none'; script-src 'nonce-abc' "
"'strict-dynamic'",
false},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc' "
"'strict-dynamic'",
false},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc' "
"https://example.com/",
false},
{"object-src 'none'; base-uri 'none'; script-src 'sha256-abc' "
"https://example.com/ 'strict-dynamic'",
false}};
// Enforced
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "[Enforce] Header: `" << test.header << "`");
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader(test.header, *secure_origin,
ContentSecurityPolicyType::kEnforce,
ContentSecurityPolicySource::kHTTP);
auto dummy = std::make_unique<DummyPageHolder>();
csp->BindToDelegate(
dummy->GetFrame().DomWindow()->GetContentSecurityPolicyDelegate());
EXPECT_EQ(test.expected,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPWithBetterThanReasonableRestrictions));
}
// Report-Only
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "[ReportOnly] Header: `" << test.header << "`");
csp = MakeGarbageCollected<ContentSecurityPolicy>();
csp->DidReceiveHeader(test.header, *secure_origin,
ContentSecurityPolicyType::kReport,
ContentSecurityPolicySource::kHTTP);
auto dummy = std::make_unique<DummyPageHolder>();
csp->BindToDelegate(
dummy->GetFrame().DomWindow()->GetContentSecurityPolicyDelegate());
EXPECT_EQ(test.expected,
dummy->GetDocument().IsUseCounted(
WebFeature::kCSPROWithBetterThanReasonableRestrictions));
}
}
} // namespace blink