blob: a8596b39facfc9104887f7440dd1291ee5b62a69 [file] [log] [blame]
// Copyright 2017 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 "content/common/content_security_policy/csp_context.h"
#include "content/common/content_security_policy_header.h"
#include "content/common/navigation_params.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
class CSPContextTest : public CSPContext {
public:
CSPContextTest() : CSPContext() {}
const std::vector<CSPViolationParams>& violations() { return violations_; }
void AddSchemeToBypassCSP(const std::string& scheme) {
scheme_to_bypass_.push_back(scheme);
}
bool SchemeShouldBypassCSP(const base::StringPiece& scheme) override {
return std::find(scheme_to_bypass_.begin(), scheme_to_bypass_.end(),
scheme) != scheme_to_bypass_.end();
}
private:
void ReportContentSecurityPolicyViolation(
const CSPViolationParams& violation_params) override {
violations_.push_back(violation_params);
}
std::vector<CSPViolationParams> violations_;
std::vector<std::string> scheme_to_bypass_;
DISALLOW_COPY_AND_ASSIGN(CSPContextTest);
};
ContentSecurityPolicyHeader EmptyCspHeader() {
return ContentSecurityPolicyHeader(
std::string(), blink::kWebContentSecurityPolicyTypeEnforce,
blink::kWebContentSecurityPolicySourceHTTP);
}
} // namespace
TEST(ContentSecurityPolicy, NoDirective) {
CSPContextTest context;
std::vector<std::string> report_end_points; // empty
ContentSecurityPolicy policy(EmptyCspHeader(), std::vector<CSPDirective>(),
report_end_points, false);
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FormAction,
GURL("http://www.example.com"),
false, &context, SourceLocation()));
ASSERT_EQ(0u, context.violations().size());
}
TEST(ContentSecurityPolicy, ReportViolation) {
CSPContextTest context;
// source = "www.example.com"
CSPSource source("", "www.example.com", false, url::PORT_UNSPECIFIED, false,
"");
CSPSourceList source_list(false, false, {source});
CSPDirective directive(CSPDirective::FormAction, source_list);
std::vector<std::string> report_end_points; // empty
ContentSecurityPolicy policy(EmptyCspHeader(), {directive}, report_end_points,
false);
EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FormAction,
GURL("http://www.not-example.com"),
false, &context, SourceLocation()));
ASSERT_EQ(1u, context.violations().size());
const char console_message[] =
"Refused to send form data to 'http://www.not-example.com/' because it "
"violates the following Content Security Policy directive: \"form-action "
"www.example.com\".\n";
EXPECT_EQ(console_message, context.violations()[0].console_message);
}
TEST(ContentSecurityPolicy, DirectiveFallback) {
CSPSource source_a("http", "a.com", false, url::PORT_UNSPECIFIED, false, "");
CSPSource source_b("http", "b.com", false, url::PORT_UNSPECIFIED, false, "");
CSPSourceList source_list_a(false, false, {source_a});
CSPSourceList source_list_b(false, false, {source_b});
std::vector<std::string> report_end_points; // Empty.
{
CSPContextTest context;
ContentSecurityPolicy policy(
EmptyCspHeader(),
{CSPDirective(CSPDirective::DefaultSrc, source_list_a)},
report_end_points, false);
EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("http://b.com"), false,
&context, SourceLocation()));
ASSERT_EQ(1u, context.violations().size());
const char console_message[] =
"Refused to frame 'http://b.com/' because it violates "
"the following Content Security Policy directive: \"default-src "
"http://a.com\". Note that 'frame-src' was not explicitly "
"set, so 'default-src' is used as a fallback.\n";
EXPECT_EQ(console_message, context.violations()[0].console_message);
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("http://a.com"), false,
&context, SourceLocation()));
}
{
CSPContextTest context;
ContentSecurityPolicy policy(
EmptyCspHeader(), {CSPDirective(CSPDirective::ChildSrc, source_list_a)},
report_end_points, false);
EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("http://b.com"), false,
&context, SourceLocation()));
ASSERT_EQ(1u, context.violations().size());
const char console_message[] =
"Refused to frame 'http://b.com/' because it violates "
"the following Content Security Policy directive: \"child-src "
"http://a.com\". Note that 'frame-src' was not explicitly "
"set, so 'child-src' is used as a fallback.\n";
EXPECT_EQ(console_message, context.violations()[0].console_message);
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("http://a.com"), false,
&context, SourceLocation()));
}
{
CSPContextTest context;
CSPSourceList source_list(false, false, {source_a, source_b});
ContentSecurityPolicy policy(
EmptyCspHeader(),
{CSPDirective(CSPDirective::FrameSrc, {source_list_a}),
CSPDirective(CSPDirective::ChildSrc, {source_list_b})},
report_end_points, false);
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("http://a.com"), false,
&context, SourceLocation()));
EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("http://b.com"), false,
&context, SourceLocation()));
ASSERT_EQ(1u, context.violations().size());
const char console_message[] =
"Refused to frame 'http://b.com/' because it violates "
"the following Content Security Policy directive: \"frame-src "
"http://a.com\".\n";
EXPECT_EQ(console_message, context.violations()[0].console_message);
}
}
TEST(ContentSecurityPolicy, RequestsAllowedWhenBypassingCSP) {
CSPContextTest context;
std::vector<std::string> report_end_points; // empty
CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false,
"");
CSPSourceList source_list(false, false, {source});
ContentSecurityPolicy policy(
EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)},
report_end_points, false);
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("https://example.com/"), false,
&context, SourceLocation()));
EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("https://not-example.com/"),
false, &context, SourceLocation()));
// Register 'https' as bypassing CSP, which should now bypass is entirely.
context.AddSchemeToBypassCSP("https");
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("https://example.com/"), false,
&context, SourceLocation()));
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("https://not-example.com/"),
false, &context, SourceLocation()));
}
TEST(ContentSecurityPolicy, FilesystemAllowedWhenBypassingCSP) {
CSPContextTest context;
std::vector<std::string> report_end_points; // empty
CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false,
"");
CSPSourceList source_list(false, false, {source});
ContentSecurityPolicy policy(
EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)},
report_end_points, false);
EXPECT_FALSE(ContentSecurityPolicy::Allow(
policy, CSPDirective::FrameSrc,
GURL("filesystem:https://example.com/file.txt"), false, &context,
SourceLocation()));
EXPECT_FALSE(ContentSecurityPolicy::Allow(
policy, CSPDirective::FrameSrc,
GURL("filesystem:https://not-example.com/file.txt"), false, &context,
SourceLocation()));
// Register 'https' as bypassing CSP, which should now bypass is entirely.
context.AddSchemeToBypassCSP("https");
EXPECT_TRUE(ContentSecurityPolicy::Allow(
policy, CSPDirective::FrameSrc,
GURL("filesystem:https://example.com/file.txt"), false, &context,
SourceLocation()));
EXPECT_TRUE(ContentSecurityPolicy::Allow(
policy, CSPDirective::FrameSrc,
GURL("filesystem:https://not-example.com/file.txt"), false, &context,
SourceLocation()));
}
TEST(ContentSecurityPolicy, BlobAllowedWhenBypassingCSP) {
CSPContextTest context;
std::vector<std::string> report_end_points; // empty
CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false,
"");
CSPSourceList source_list(false, false, {source});
ContentSecurityPolicy policy(
EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)},
report_end_points, false);
EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("blob:https://example.com/"),
false, &context, SourceLocation()));
EXPECT_FALSE(ContentSecurityPolicy::Allow(
policy, CSPDirective::FrameSrc, GURL("blob:https://not-example.com/"),
false, &context, SourceLocation()));
// Register 'https' as bypassing CSP, which should now bypass is entirely.
context.AddSchemeToBypassCSP("https");
EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc,
GURL("blob:https://example.com/"),
false, &context, SourceLocation()));
EXPECT_TRUE(ContentSecurityPolicy::Allow(
policy, CSPDirective::FrameSrc, GURL("blob:https://not-example.com/"),
false, &context, SourceLocation()));
}
TEST(ContentSecurityPolicy, ShouldUpgradeInsecureRequest) {
std::vector<std::string> report_end_points; // empty
CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false,
"");
CSPSourceList source_list(false, false, {source});
ContentSecurityPolicy policy(
EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)},
report_end_points, false);
EXPECT_FALSE(ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy));
policy.directives.push_back(
CSPDirective(CSPDirective::UpgradeInsecureRequests, CSPSourceList()));
EXPECT_TRUE(ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy));
}
} // namespace content