Commit 240c8a0c authored by Antonio Sartori's avatar Antonio Sartori Committed by Chromium LUCI CQ

CSP: Replace blink::CSPSource with mojo type

In the Blink Content Security Policy code, we switch from using the
internal blink type blink::CSPSource to using the mojo type
network::mojom::blink::CSPSource for handling CSP source expressions.

This is part of a project to harmonize the CSP code in Blink and in
services/network, and will make it easier to synchronize Content
Security Policies between the two.

Change-Id: If7769b321934ee73cf1aa0faa6d8b371360684a7
Bug: 1021462,1149272
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2412339Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Commit-Queue: Antonio Sartori <antoniosartori@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834599}
parent a66dbdcf
......@@ -178,19 +178,18 @@ void ContentSecurityPolicy::BindToDelegate(
void ContentSecurityPolicy::SetupSelf(const SecurityOrigin& security_origin) {
// Ensure that 'self' processes correctly.
self_protocol_ = security_origin.Protocol();
self_source_ = MakeGarbageCollected<CSPSource>(
this, self_protocol_, security_origin.Host(),
self_source_ = network::mojom::blink::CSPSource::New(
self_protocol_, security_origin.Host(),
security_origin.Port() == DefaultPortForProtocol(self_protocol_)
? CSPSource::kPortUnspecified
? url::PORT_UNSPECIFIED
: security_origin.Port(),
String(), CSPSource::kNoWildcard, CSPSource::kNoWildcard);
"", /*is_host_wildcard=*/false, /*is_port_wildcard=*/false);
}
void ContentSecurityPolicy::SetupSelf(const ContentSecurityPolicy& other) {
self_protocol_ = other.self_protocol_;
if (other.self_source_) {
self_source_ =
MakeGarbageCollected<CSPSource>(this, *(other.self_source_.Get()));
self_source_ = other.self_source_.Clone();
}
}
......@@ -294,15 +293,14 @@ void ContentSecurityPolicy::Trace(Visitor* visitor) const {
visitor->Trace(delegate_);
visitor->Trace(policies_);
visitor->Trace(console_messages_);
visitor->Trace(self_source_);
}
void ContentSecurityPolicy::CopyStateFrom(const ContentSecurityPolicy* other) {
DCHECK(policies_.IsEmpty());
SetupSelf(*other);
for (const auto& policy : other->policies_)
AddAndReportPolicyFromHeaderValue(policy->Header(), policy->HeaderType(),
policy->HeaderSource());
SetupSelf(*other);
}
void ContentSecurityPolicy::CopyPluginTypesFrom(
......@@ -435,9 +433,9 @@ void ContentSecurityPolicy::SetOverrideURLForSelf(const KURL& url) {
// to an execution context.
scoped_refptr<const SecurityOrigin> origin = SecurityOrigin::Create(url);
self_protocol_ = origin->Protocol();
self_source_ = MakeGarbageCollected<CSPSource>(
this, self_protocol_, origin->Host(), origin->Port(), String(),
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
self_source_ = network::mojom::blink::CSPSource::New(
self_protocol_, origin->Host(), origin->Port(), "",
/*is_host_wildcard=*/false, /*is_port_wildcard=*/false);
}
Vector<CSPHeaderAndType> ContentSecurityPolicy::Headers() const {
......@@ -1478,7 +1476,8 @@ bool ContentSecurityPolicy::ShouldSendCSPHeader(ResourceType type) const {
}
bool ContentSecurityPolicy::UrlMatchesSelf(const KURL& url) const {
return self_source_->MatchesAsSelf(url);
DCHECK(self_source_);
return CSPSourceMatchesAsSelf(*self_source_, self_protocol_, url);
}
bool ContentSecurityPolicy::ProtocolEqualsSelf(const String& protocol) const {
......
......@@ -64,7 +64,6 @@ namespace blink {
class ContentSecurityPolicyResponseHeaders;
class ConsoleMessage;
class CSPDirectiveList;
class CSPSource;
class DOMWrapperWorld;
class Element;
class ExecutionContext;
......@@ -443,7 +442,9 @@ class CORE_EXPORT ContentSecurityPolicy final
bool ShouldSendCSPHeader(ResourceType) const;
CSPSource* GetSelfSource() const { return self_source_; }
network::mojom::blink::CSPSource* GetSelfSource() const {
return self_source_.get();
}
// Whether the main world's CSP should be bypassed based on the current
// javascript world we are in.
......@@ -595,7 +596,7 @@ class CORE_EXPORT ContentSecurityPolicy final
String disable_eval_error_message_;
mojom::blink::InsecureRequestPolicy insecure_request_policy_;
Member<CSPSource> self_source_;
network::mojom::blink::CSPSourcePtr self_source_;
String self_protocol_;
bool supports_wasm_eval_ = false;
......
......@@ -980,7 +980,7 @@ void CSPDirectiveList::ParseReportURI(const String& name, const String& value) {
return false;
}
if (MixedContentChecker::IsMixedContent(
policy_->GetSelfSource()->GetScheme(),
policy_->GetSelfSource()->scheme,
parsed_endpoint)) {
policy_->ReportMixedContentReportURI(endpoint);
return true;
......@@ -1431,9 +1431,7 @@ CSPDirectiveList::ExposeForNavigationalChecks() const {
auto policy = network::mojom::blink::ContentSecurityPolicy::New();
policy->self_origin =
policy_->GetSelfSource()
? policy_->GetSelfSource()->ExposeForNavigationalChecks()
: nullptr;
policy_->GetSelfSource() ? policy_->GetSelfSource()->Clone() : nullptr;
policy->use_reporting_api = use_reporting_api_;
policy->report_endpoints = report_endpoints_;
policy->header = network::mojom::blink::ContentSecurityPolicyHeader::New(
......
......@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/frame/csp/csp_source.h"
#include "services/network/public/mojom/content_security_policy.mojom-blink.h"
#include "third_party/blink/public/platform/web_content_security_policy_struct.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
......@@ -15,96 +16,28 @@
namespace blink {
constexpr int CSPSource::kPortUnspecified = -1;
CSPSource::CSPSource(ContentSecurityPolicy* policy,
const String& scheme,
const String& host,
int port,
const String& path,
WildcardDisposition host_wildcard,
WildcardDisposition port_wildcard)
: policy_(policy),
scheme_(scheme.DeprecatedLower()),
host_(host),
port_(port),
path_(path),
host_wildcard_(host_wildcard),
port_wildcard_(port_wildcard) {}
CSPSource::CSPSource(ContentSecurityPolicy* policy, const CSPSource& other)
: CSPSource(policy,
other.scheme_,
other.host_,
other.port_,
other.path_,
other.host_wildcard_,
other.port_wildcard_) {}
bool CSPSource::Matches(const KURL& url,
ResourceRequest::RedirectStatus redirect_status) const {
SchemeMatchingResult schemes_match = SchemeMatches(url.Protocol());
if (schemes_match == SchemeMatchingResult::kNotMatching)
return false;
if (IsSchemeOnly())
return true;
bool paths_match = (redirect_status == RedirectStatus::kFollowedRedirect) ||
PathMatches(url.GetPath());
PortMatchingResult ports_match = PortMatches(
url.HasPort() ? url.Port() : kPortUnspecified, url.Protocol());
// if either the scheme or the port would require an upgrade (e.g. from http
// to https) then check that both of them can upgrade to ensure that we don't
// run into situations where we only upgrade the port but not the scheme or
// viceversa
if ((RequiresUpgrade(schemes_match) || (RequiresUpgrade(ports_match))) &&
(!CanUpgrade(schemes_match) || !CanUpgrade(ports_match))) {
return false;
}
return HostMatches(url.Host()) &&
ports_match != PortMatchingResult::kNotMatching && paths_match;
}
bool CSPSource::MatchesAsSelf(const KURL& url) {
// https://w3c.github.io/webappsec-csp/#match-url-to-source-expression
// Step 4.
SchemeMatchingResult schemes_match = SchemeMatches(url.Protocol());
bool hosts_match = HostMatches(url.Host());
PortMatchingResult ports_match = PortMatches(
url.HasPort() ? url.Port() : kPortUnspecified, url.Protocol());
// check if the origin is exactly matching
if (schemes_match == SchemeMatchingResult::kMatchingExact && hosts_match &&
(ports_match == PortMatchingResult::kMatchingExact ||
ports_match == PortMatchingResult::kMatchingWildcard)) {
return true;
}
String self_scheme =
(scheme_.IsEmpty() ? policy_->GetSelfProtocol() : scheme_);
bool ports_match_or_defaults =
(ports_match == PortMatchingResult::kMatchingExact ||
((IsDefaultPortForProtocol(port_, self_scheme) ||
port_ == kPortUnspecified) &&
(!url.HasPort() ||
IsDefaultPortForProtocol(url.Port(), url.Protocol()))));
if (hosts_match && ports_match_or_defaults &&
(url.Protocol() == "https" || url.Protocol() == "wss" ||
self_scheme == "http")) {
return true;
}
return false;
}
CSPSource::SchemeMatchingResult CSPSource::SchemeMatches(
const String& protocol) const {
namespace {
enum class SchemeMatchingResult {
kNotMatching,
kMatchingUpgrade,
kMatchingExact
};
enum class PortMatchingResult {
kNotMatching,
kMatchingWildcard,
kMatchingUpgrade,
kMatchingExact
};
SchemeMatchingResult SchemeMatches(
const network::mojom::blink::CSPSource& source,
const String& protocol,
const String& self_protocol) {
DCHECK_EQ(protocol, protocol.DeprecatedLower());
const String& scheme =
(scheme_.IsEmpty() ? policy_->GetSelfProtocol() : scheme_);
(source.scheme.IsEmpty() ? self_protocol : source.scheme);
if (scheme == protocol)
return SchemeMatchingResult::kMatchingExact;
......@@ -117,98 +50,161 @@ CSPSource::SchemeMatchingResult CSPSource::SchemeMatches(
return SchemeMatchingResult::kNotMatching;
}
bool CSPSource::HostMatches(const String& host) const {
bool match;
bool equal_hosts = EqualIgnoringASCIICase(host_, host);
if (host_wildcard_ == kHasWildcard) {
if (host_.IsEmpty()) {
bool HostMatches(const network::mojom::blink::CSPSource& source,
const String& host) {
if (source.is_host_wildcard) {
if (source.host.IsEmpty()) {
// host-part = "*"
match = true;
} else {
return true;
}
if (host.EndsWithIgnoringCase(String("." + source.host))) {
// host-part = "*." 1*host-char *( "." 1*host-char )
match = host.EndsWithIgnoringCase(String("." + host_));
return true;
}
} else {
// host-part = 1*host-char *( "." 1*host-char )
match = equal_hosts;
return false;
}
return match;
return EqualIgnoringASCIICase(source.host, host);
}
bool CSPSource::PathMatches(const String& url_path) const {
if (path_.IsEmpty() || (path_ == "/" && url_path.IsEmpty()))
bool PathMatches(const network::mojom::blink::CSPSource& source,
const String& url_path) {
if (source.path.IsEmpty() || (source.path == "/" && url_path.IsEmpty()))
return true;
String path =
DecodeURLEscapeSequences(url_path, DecodeURLMode::kUTF8OrIsomorphic);
if (path_.EndsWith("/"))
return path.StartsWith(path_);
if (source.path.EndsWith("/"))
return path.StartsWith(source.path);
return path == path_;
return path == source.path;
}
CSPSource::PortMatchingResult CSPSource::PortMatches(
int port,
const String& protocol) const {
if (port_wildcard_ == kHasWildcard)
PortMatchingResult PortMatches(const network::mojom::blink::CSPSource& source,
const String& self_protocol,
int port,
const String& protocol) {
if (source.is_port_wildcard)
return PortMatchingResult::kMatchingWildcard;
if (port == port_) {
if (port == kPortUnspecified)
if (port == source.port) {
if (port == url::PORT_UNSPECIFIED)
return PortMatchingResult::kMatchingWildcard;
return PortMatchingResult::kMatchingExact;
}
bool is_scheme_http; // needed for detecting an upgrade when the port is 0
is_scheme_http = scheme_.IsEmpty() ? policy_->ProtocolEqualsSelf("http")
: EqualIgnoringASCIICase("http", scheme_);
if ((port_ == 80 ||
((port_ == kPortUnspecified || port_ == 443) && is_scheme_http)) &&
(port == 443 ||
(port == kPortUnspecified && DefaultPortForProtocol(protocol) == 443))) {
is_scheme_http = source.scheme.IsEmpty()
? EqualIgnoringASCIICase("http", self_protocol)
: EqualIgnoringASCIICase("http", source.scheme);
if ((source.port == 80 ||
((source.port == url::PORT_UNSPECIFIED || source.port == 443) &&
is_scheme_http)) &&
(port == 443 || (port == url::PORT_UNSPECIFIED &&
DefaultPortForProtocol(protocol) == 443))) {
return PortMatchingResult::kMatchingUpgrade;
}
if (port == kPortUnspecified) {
if (IsDefaultPortForProtocol(port_, protocol))
if (port == url::PORT_UNSPECIFIED) {
if (IsDefaultPortForProtocol(source.port, protocol))
return PortMatchingResult::kMatchingExact;
return PortMatchingResult::kNotMatching;
}
if (port_ == kPortUnspecified) {
if (source.port == url::PORT_UNSPECIFIED) {
if (IsDefaultPortForProtocol(port, protocol))
return PortMatchingResult::kMatchingExact;
return PortMatchingResult::kNotMatching;
}
return PortMatchingResult::kNotMatching;
}
bool CSPSource::IsSchemeOnly() const {
return host_.IsEmpty() && (host_wildcard_ == kNoWildcard);
// Helper inline functions for Port and Scheme MatchingResult enums
bool inline RequiresUpgrade(const PortMatchingResult result) {
return result == PortMatchingResult::kMatchingUpgrade;
}
bool inline RequiresUpgrade(const SchemeMatchingResult result) {
return result == SchemeMatchingResult::kMatchingUpgrade;
}
bool inline CanUpgrade(const PortMatchingResult result) {
return result == PortMatchingResult::kMatchingUpgrade ||
result == PortMatchingResult::kMatchingWildcard;
}
network::mojom::blink::CSPSourcePtr CSPSource::ExposeForNavigationalChecks()
const {
return network::mojom::blink::CSPSource::New(
scheme_ ? scheme_ : "", // scheme
host_ ? host_ : "", // host
port_ == kPortUnspecified ? -1 /* url::PORT_UNSPECIFIED */
: port_, // port
path_ ? path_ : "", // path
host_wildcard_ == kHasWildcard, // is_host_wildcard
port_wildcard_ == kHasWildcard // is_port_wildcard
);
bool inline CanUpgrade(const SchemeMatchingResult result) {
return result == SchemeMatchingResult::kMatchingUpgrade;
}
} // namespace
bool CSPSourceMatches(const network::mojom::blink::CSPSource& source,
const String& self_protocol,
const KURL& url,
ResourceRequest::RedirectStatus redirect_status) {
SchemeMatchingResult schemes_match =
SchemeMatches(source, url.Protocol(), self_protocol);
if (schemes_match == SchemeMatchingResult::kNotMatching)
return false;
if (CSPSourceIsSchemeOnly(source))
return true;
bool paths_match = (redirect_status == RedirectStatus::kFollowedRedirect) ||
PathMatches(source, url.GetPath());
PortMatchingResult ports_match = PortMatches(
source, self_protocol, url.HasPort() ? url.Port() : url::PORT_UNSPECIFIED,
url.Protocol());
// if either the scheme or the port would require an upgrade (e.g. from http
// to https) then check that both of them can upgrade to ensure that we don't
// run into situations where we only upgrade the port but not the scheme or
// viceversa
if ((RequiresUpgrade(schemes_match) || (RequiresUpgrade(ports_match))) &&
(!CanUpgrade(schemes_match) || !CanUpgrade(ports_match))) {
return false;
}
return HostMatches(source, url.Host()) &&
ports_match != PortMatchingResult::kNotMatching && paths_match;
}
bool CSPSourceMatchesAsSelf(const network::mojom::blink::CSPSource& source,
const String& self_protocol,
const KURL& url) {
// https://w3c.github.io/webappsec-csp/#match-url-to-source-expression
// Step 4.
SchemeMatchingResult schemes_match =
SchemeMatches(source, url.Protocol(), self_protocol);
bool hosts_match = HostMatches(source, url.Host());
PortMatchingResult ports_match = PortMatches(
source, self_protocol, url.HasPort() ? url.Port() : url::PORT_UNSPECIFIED,
url.Protocol());
// check if the origin is exactly matching
if (schemes_match == SchemeMatchingResult::kMatchingExact && hosts_match &&
(ports_match == PortMatchingResult::kMatchingExact ||
ports_match == PortMatchingResult::kMatchingWildcard)) {
return true;
}
String self_scheme =
(source.scheme.IsEmpty() ? self_protocol : source.scheme);
bool ports_match_or_defaults =
(ports_match == PortMatchingResult::kMatchingExact ||
((IsDefaultPortForProtocol(source.port, self_scheme) ||
source.port == url::PORT_UNSPECIFIED) &&
(!url.HasPort() ||
IsDefaultPortForProtocol(url.Port(), url.Protocol()))));
return hosts_match && ports_match_or_defaults &&
(url.Protocol() == "https" || url.Protocol() == "wss" ||
self_scheme == "http");
}
void CSPSource::Trace(Visitor* visitor) const {
visitor->Trace(policy_);
bool CSPSourceIsSchemeOnly(const network::mojom::blink::CSPSource& source) {
return source.host.IsEmpty() && (!source.is_host_wildcard);
}
} // namespace blink
......@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_CSP_CSP_SOURCE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_CSP_CSP_SOURCE_H_
#include "services/network/public/mojom/content_security_policy.mojom-blink-forward.h"
#include "third_party/blink/public/platform/web_content_security_policy_struct.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
......@@ -15,86 +16,22 @@
namespace blink {
class ContentSecurityPolicy;
class KURL;
class CORE_EXPORT CSPSource final : public GarbageCollected<CSPSource> {
public:
// Represents the absence of a port.
const static int kPortUnspecified;
CORE_EXPORT
bool CSPSourceIsSchemeOnly(const network::mojom::blink::CSPSource& source);
enum WildcardDisposition { kNoWildcard, kHasWildcard };
CORE_EXPORT
bool CSPSourceMatches(const network::mojom::blink::CSPSource& source,
const String& self_protocol,
const KURL& url,
ResourceRequest::RedirectStatus =
ResourceRequest::RedirectStatus::kNoRedirect);
// NotMatching is the only negative member, the rest are different types of
// matches. NotMatching should always be 0 to let if statements work nicely
enum class PortMatchingResult {
kNotMatching,
kMatchingWildcard,
kMatchingUpgrade,
kMatchingExact
};
enum class SchemeMatchingResult {
kNotMatching,
kMatchingUpgrade,
kMatchingExact
};
CSPSource(ContentSecurityPolicy*,
const String& scheme,
const String& host,
int port,
const String& path,
WildcardDisposition host_wildcard,
WildcardDisposition port_wildcard);
CSPSource(ContentSecurityPolicy* policy, const CSPSource& other);
bool IsSchemeOnly() const;
const String& GetScheme() { return scheme_; }
bool Matches(const KURL&,
ResourceRequest::RedirectStatus =
ResourceRequest::RedirectStatus::kNoRedirect) const;
bool MatchesAsSelf(const KURL&);
network::mojom::blink::CSPSourcePtr ExposeForNavigationalChecks() const;
void Trace(Visitor*) const;
private:
FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, OperativeDirectiveGivenType);
SchemeMatchingResult SchemeMatches(const String&) const;
bool HostMatches(const String&) const;
bool PathMatches(const String&) const;
// Protocol is necessary to determine default port if it is zero.
PortMatchingResult PortMatches(int port, const String& protocol) const;
// Helper inline functions for Port and Scheme MatchingResult enums
bool inline RequiresUpgrade(const PortMatchingResult result) const {
return result == PortMatchingResult::kMatchingUpgrade;
}
bool inline RequiresUpgrade(const SchemeMatchingResult result) const {
return result == SchemeMatchingResult::kMatchingUpgrade;
}
bool inline CanUpgrade(const PortMatchingResult result) const {
return result == PortMatchingResult::kMatchingUpgrade ||
result == PortMatchingResult::kMatchingWildcard;
}
bool inline CanUpgrade(const SchemeMatchingResult result) const {
return result == SchemeMatchingResult::kMatchingUpgrade;
}
Member<ContentSecurityPolicy> policy_;
String scheme_;
String host_;
int port_;
String path_;
WildcardDisposition host_wildcard_;
WildcardDisposition port_wildcard_;
};
CORE_EXPORT
bool CSPSourceMatchesAsSelf(const network::mojom::blink::CSPSource& source,
const String& self_protocol,
const KURL& url);
} // namespace blink
......
......@@ -13,425 +13,437 @@
namespace blink {
class CSPSourceTest : public testing::Test {
public:
CSPSourceTest() : csp(MakeGarbageCollected<ContentSecurityPolicy>()) {}
protected:
Persistent<ContentSecurityPolicy> csp;
struct Source {
String scheme;
String host;
String path;
// port is CSPSource::kPortUnspecified if it was not specified so the
// default port for a given scheme will be used.
const int port;
CSPSource::WildcardDisposition host_wildcard;
CSPSource::WildcardDisposition port_wildcard;
};
bool EqualSources(const Source& a, const Source& b) {
return a.scheme == b.scheme && a.host == b.host && a.port == b.port &&
a.path == b.path && a.host_wildcard == b.host_wildcard &&
a.port_wildcard == b.port_wildcard;
}
};
TEST_F(CSPSourceTest, BasicMatching) {
TEST(CSPSourceTest, BasicMatching) {
KURL base;
CSPSource source(csp.Get(), "http", "example.com", 8000, "/foo/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com:8000/foo/")));
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com:8000/foo/bar")));
EXPECT_TRUE(source.Matches(KURL(base, "HTTP://EXAMPLE.com:8000/foo/BAR")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:8000/bar/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://example.com:8000/bar/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:9000/bar/")));
EXPECT_FALSE(source.Matches(KURL(base, "HTTP://example.com:8000/FOO/bar")));
EXPECT_FALSE(source.Matches(KURL(base, "HTTP://example.com:8000/FOO/BAR")));
auto source = network::mojom::blink::CSPSource::New(
"http", "example.com", 8000, "/foo/", false, false);
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "http://example.com:8000/foo/")));
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "http://example.com:8000/foo/bar")));
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "HTTP://EXAMPLE.com:8000/foo/BAR")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://example.com:8000/bar/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "https://example.com:8000/bar/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://example.com:9000/bar/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "HTTP://example.com:8000/FOO/bar")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "HTTP://example.com:8000/FOO/BAR")));
}
TEST_F(CSPSourceTest, BasicPathMatching) {
TEST(CSPSourceTest, BasicPathMatching) {
KURL base;
CSPSource a(csp.Get(), "http", "example.com", 8000, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
auto a = network::mojom::blink::CSPSource::New("http", "example.com", 8000,
"/", false, false);
EXPECT_TRUE(a.Matches(KURL(base, "http://example.com:8000")));
EXPECT_TRUE(a.Matches(KURL(base, "http://example.com:8000/")));
EXPECT_TRUE(a.Matches(KURL(base, "http://example.com:8000/foo/bar")));
EXPECT_TRUE(CSPSourceMatches(*a, "", KURL(base, "http://example.com:8000")));
EXPECT_TRUE(CSPSourceMatches(*a, "", KURL(base, "http://example.com:8000/")));
EXPECT_TRUE(
CSPSourceMatches(*a, "", KURL(base, "http://example.com:8000/foo/bar")));
EXPECT_FALSE(a.Matches(KURL(base, "http://example.com:8000path")));
EXPECT_FALSE(a.Matches(KURL(base, "http://example.com:9000/")));
EXPECT_FALSE(
CSPSourceMatches(*a, "", KURL(base, "http://example.com:8000path")));
EXPECT_FALSE(
CSPSourceMatches(*a, "", KURL(base, "http://example.com:9000/")));
CSPSource b(csp.Get(), "http", "example.com", 8000, "",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(b.Matches(KURL(base, "http://example.com:8000")));
EXPECT_TRUE(b.Matches(KURL(base, "http://example.com:8000/")));
EXPECT_TRUE(a.Matches(KURL(base, "http://example.com:8000/foo/bar")));
auto b = network::mojom::blink::CSPSource::New("http", "example.com", 8000,
"", false, false);
EXPECT_TRUE(CSPSourceMatches(*b, "", KURL(base, "http://example.com:8000")));
EXPECT_TRUE(CSPSourceMatches(*b, "", KURL(base, "http://example.com:8000/")));
EXPECT_TRUE(
CSPSourceMatches(*a, "", KURL(base, "http://example.com:8000/foo/bar")));
EXPECT_FALSE(b.Matches(KURL(base, "http://example.com:8000path")));
EXPECT_FALSE(b.Matches(KURL(base, "http://example.com:9000/")));
EXPECT_FALSE(
CSPSourceMatches(*b, "", KURL(base, "http://example.com:8000path")));
EXPECT_FALSE(
CSPSourceMatches(*b, "", KURL(base, "http://example.com:9000/")));
}
TEST_F(CSPSourceTest, WildcardMatching) {
TEST(CSPSourceTest, WildcardMatching) {
KURL base;
CSPSource source(csp.Get(), "http", "example.com",
CSPSource::kPortUnspecified, "/", CSPSource::kHasWildcard,
CSPSource::kHasWildcard);
auto source = network::mojom::blink::CSPSource::New(
"http", "example.com", url::PORT_UNSPECIFIED, "/", true, true);
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "http://foo.example.com:8000/")));
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "http://foo.example.com:8000/foo")));
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "http://foo.example.com:9000/foo/")));
EXPECT_TRUE(CSPSourceMatches(
*source, "", KURL(base, "HTTP://FOO.EXAMPLE.com:8000/foo/BAR")));
EXPECT_TRUE(source.Matches(KURL(base, "http://foo.example.com:8000/")));
EXPECT_TRUE(source.Matches(KURL(base, "http://foo.example.com:8000/foo")));
EXPECT_TRUE(source.Matches(KURL(base, "http://foo.example.com:9000/foo/")));
EXPECT_TRUE(
source.Matches(KURL(base, "HTTP://FOO.EXAMPLE.com:8000/foo/BAR")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:8000/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:8000/foo")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:9000/foo/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.foo.com:8000/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://example.foo.com:8000/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://example.com:8000/bar/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8000/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8000/foo")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://example.com:9000/foo/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://example.foo.com:8000/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "https://example.foo.com:8000/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "https://example.com:8000/bar/")));
}
TEST_F(CSPSourceTest, RedirectMatching) {
TEST(CSPSourceTest, RedirectMatching) {
KURL base;
CSPSource source(csp.Get(), "http", "example.com", 8000, "/bar/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
auto source = network::mojom::blink::CSPSource::New(
"http", "example.com", 8000, "/bar/", false, false);
EXPECT_TRUE(
source.Matches(KURL(base, "http://example.com:8000/"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8000/"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
EXPECT_TRUE(
source.Matches(KURL(base, "http://example.com:8000/foo"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8000/foo"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
// Should not allow upgrade of port or scheme without upgrading both
EXPECT_FALSE(
source.Matches(KURL(base, "https://example.com:8000/foo"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
EXPECT_FALSE(
source.Matches(KURL(base, "http://not-example.com:8000/foo"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:9000/foo/"),
ResourceRequest::RedirectStatus::kNoRedirect));
CSPSourceMatches(*source, "", KURL(base, "https://example.com:8000/foo"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
EXPECT_FALSE(CSPSourceMatches(
*source, "", KURL(base, "http://not-example.com:8000/foo"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://example.com:9000/foo/"),
ResourceRequest::RedirectStatus::kNoRedirect));
}
TEST_F(CSPSourceTest, InsecureSchemeMatchesSecureScheme) {
TEST(CSPSourceTest, InsecureSchemeMatchesSecureScheme) {
KURL base;
CSPSource source(csp.Get(), "http", "", CSPSource::kPortUnspecified, "/",
CSPSource::kNoWildcard, CSPSource::kHasWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com:8000/")));
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com:8000/")));
EXPECT_TRUE(source.Matches(KURL(base, "http://not-example.com:8000/")));
EXPECT_TRUE(source.Matches(KURL(base, "https://not-example.com:8000/")));
EXPECT_FALSE(source.Matches(KURL(base, "ftp://example.com:8000/")));
auto source = network::mojom::blink::CSPSource::New(
"http", "", url::PORT_UNSPECIFIED, "/", false, true);
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8000/")));
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:8000/")));
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "http://not-example.com:8000/")));
EXPECT_TRUE(CSPSourceMatches(*source, "",
KURL(base, "https://not-example.com:8000/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "ftp://example.com:8000/")));
}
TEST_F(CSPSourceTest, InsecureHostSchemeMatchesSecureScheme) {
TEST(CSPSourceTest, InsecureHostSchemeMatchesSecureScheme) {
KURL base;
CSPSource source(csp.Get(), "http", "example.com",
CSPSource::kPortUnspecified, "/", CSPSource::kNoWildcard,
CSPSource::kHasWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com:8000/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://not-example.com:8000/")));
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com:8000/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://not-example.com:8000/")));
auto source = network::mojom::blink::CSPSource::New(
"http", "example.com", url::PORT_UNSPECIFIED, "/", false, true);
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8000/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://not-example.com:8000/")));
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:8000/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "https://not-example.com:8000/")));
}
TEST_F(CSPSourceTest, SchemeIsEmpty) {
TEST(CSPSourceTest, SchemeIsEmpty) {
KURL base;
// Self scheme is http.
{
Persistent<ContentSecurityPolicy> csp(
MakeGarbageCollected<ContentSecurityPolicy>());
csp->SetupSelf(*SecurityOrigin::CreateFromString("http://a.com/"));
CSPSource source(csp.Get(), "", "a.com", CSPSource::kPortUnspecified, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://a.com")));
EXPECT_TRUE(source.Matches(KURL(base, "https://a.com")));
EXPECT_FALSE(source.Matches(KURL(base, "ftp://a.com")));
auto source = network::mojom::blink::CSPSource::New(
"", "a.com", url::PORT_UNSPECIFIED, "/", false, false);
EXPECT_TRUE(CSPSourceMatches(*source, "http", KURL(base, "http://a.com")));
EXPECT_TRUE(CSPSourceMatches(*source, "http", KURL(base, "https://a.com")));
EXPECT_FALSE(CSPSourceMatches(*source, "http", KURL(base, "ftp://a.com")));
}
// Self scheme is https.
{
Persistent<ContentSecurityPolicy> csp(
MakeGarbageCollected<ContentSecurityPolicy>());
csp->SetupSelf(*SecurityOrigin::CreateFromString("https://a.com/"));
CSPSource source(csp.Get(), "", "a.com", CSPSource::kPortUnspecified, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_FALSE(source.Matches(KURL(base, "http://a.com")));
EXPECT_TRUE(source.Matches(KURL(base, "https://a.com")));
EXPECT_FALSE(source.Matches(KURL(base, "ftp://a.com")));
auto source = network::mojom::blink::CSPSource::New(
"", "a.com", url::PORT_UNSPECIFIED, "/", false, false);
EXPECT_FALSE(
CSPSourceMatches(*source, "https", KURL(base, "http://a.com")));
EXPECT_TRUE(
CSPSourceMatches(*source, "https", KURL(base, "https://a.com")));
EXPECT_FALSE(CSPSourceMatches(*source, "https", KURL(base, "ftp://a.com")));
}
// Self scheme is not in the http familly.
{
Persistent<ContentSecurityPolicy> csp(
MakeGarbageCollected<ContentSecurityPolicy>());
csp->SetupSelf(*SecurityOrigin::CreateFromString("ftp://a.com/"));
CSPSource source(csp.Get(), "", "a.com", CSPSource::kPortUnspecified, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_FALSE(source.Matches(KURL(base, "http://a.com")));
EXPECT_TRUE(source.Matches(KURL(base, "ftp://a.com")));
auto source = network::mojom::blink::CSPSource::New(
"", "a.com", url::PORT_UNSPECIFIED, "/", false, false);
EXPECT_FALSE(CSPSourceMatches(*source, "ftp", KURL(base, "http://a.com")));
EXPECT_TRUE(CSPSourceMatches(*source, "ftp", KURL(base, "ftp://a.com")));
}
// Self scheme is unique
{
Persistent<ContentSecurityPolicy> csp(
MakeGarbageCollected<ContentSecurityPolicy>());
csp->SetupSelf(
*SecurityOrigin::CreateFromString("non-standard-scheme://a.com/"));
CSPSource source(csp.Get(), "", "a.com", CSPSource::kPortUnspecified, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_FALSE(source.Matches(KURL(base, "http://a.com")));
auto source = network::mojom::blink::CSPSource::New(
"", "a.com", url::PORT_UNSPECIFIED, "/", false, false);
EXPECT_FALSE(CSPSourceMatches(*source, "non-standard-scheme",
KURL(base, "http://a.com")));
// The reason matching fails is because the host is parsed as "" when
// using a non standard scheme even though it should be parsed as "a.com"
// After adding it to the list of standard schemes it now gets parsed
// correctly. This does not matter in practice though because there is
// no way to render/load anything like "non-standard-scheme://a.com"
EXPECT_FALSE(source.Matches(KURL(base, "non-standard-scheme://a.com")));
EXPECT_FALSE(CSPSourceMatches(*source, "non-standard-scheme",
KURL(base, "non-standard-scheme://a.com")));
}
}
TEST_F(CSPSourceTest, InsecureHostSchemePortMatchesSecurePort) {
TEST(CSPSourceTest, InsecureHostSchemePortMatchesSecurePort) {
KURL base;
// source scheme is "http", source port is 80
{
CSPSource source(csp.Get(), "http", "example.com", 80, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com/")));
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com:80/")));
auto source = network::mojom::blink::CSPSource::New("http", "example.com",
80, "/", false, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com/")));
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:80/")));
// Should not allow scheme upgrades unless both port and scheme are upgraded
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:443/")));
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://example.com:80/")));
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com:443/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:8443/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://example.com:8443/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://not-example.com/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://not-example.com:80/")));
EXPECT_FALSE(source.Matches(KURL(base, "http://not-example.com:443/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://not-example.com/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://not-example.com:80/")));
EXPECT_FALSE(source.Matches(KURL(base, "https://not-example.com:443/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:443/")));
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:80/")));
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:443/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8443/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:8443/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "http://not-example.com/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://not-example.com:80/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "http://not-example.com:443/")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "https://not-example.com/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "https://not-example.com:80/")));
EXPECT_FALSE(CSPSourceMatches(*source, "",
KURL(base, "https://not-example.com:443/")));
}
// source scheme is "http", source port is 443
{
CSPSource source(csp.Get(), "http", "example.com", 443, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com/")));
auto source = network::mojom::blink::CSPSource::New("http", "example.com",
443, "/", false, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com/")));
}
// source scheme is empty
{
Persistent<ContentSecurityPolicy> csp(
MakeGarbageCollected<ContentSecurityPolicy>());
csp->SetupSelf(*SecurityOrigin::CreateFromString("http://example.com"));
CSPSource source(csp.Get(), "", "example.com", 80, "/",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com/")));
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com:443")));
auto source = network::mojom::blink::CSPSource::New("", "example.com", 80,
"/", false, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "http", KURL(base, "http://example.com/")));
EXPECT_TRUE(CSPSourceMatches(*source, "http",
KURL(base, "https://example.com:443")));
// Should not allow upgrade of port or scheme without upgrading both
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:443")));
EXPECT_FALSE(CSPSourceMatches(*source, "http",
KURL(base, "http://example.com:443")));
}
// source port is empty
{
CSPSource source(csp.Get(), "http", "example.com",
CSPSource::kPortUnspecified, "/", CSPSource::kNoWildcard,
CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://example.com")));
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com")));
EXPECT_TRUE(source.Matches(KURL(base, "https://example.com:443")));
auto source = network::mojom::blink::CSPSource::New(
"http", "example.com", url::PORT_UNSPECIFIED, "/", false, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com")));
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com")));
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:443")));
// Should not allow upgrade of port or scheme without upgrading both
EXPECT_FALSE(source.Matches(KURL(base, "https://example.com:80")));
EXPECT_FALSE(source.Matches(KURL(base, "http://example.com:443")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:80")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:443")));
}
}
TEST_F(CSPSourceTest, HostMatches) {
TEST(CSPSourceTest, HostMatches) {
KURL base;
Persistent<ContentSecurityPolicy> csp(
MakeGarbageCollected<ContentSecurityPolicy>());
csp->SetupSelf(*SecurityOrigin::CreateFromString("http://a.com"));
// Host is * (source-expression = "http://*")
{
CSPSource source(csp.Get(), "http", "", CSPSource::kPortUnspecified, "",
CSPSource::kHasWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://a.com")));
EXPECT_TRUE(source.Matches(KURL(base, "http://.")));
auto source = network::mojom::blink::CSPSource::New(
"http", "", url::PORT_UNSPECIFIED, "", true, false);
EXPECT_TRUE(CSPSourceMatches(*source, "http", KURL(base, "http://a.com")));
EXPECT_TRUE(CSPSourceMatches(*source, "http", KURL(base, "http://.")));
}
// Host is *.foo.bar
{
CSPSource source(csp.Get(), "", "foo.bar", CSPSource::kPortUnspecified, "",
CSPSource::kHasWildcard, CSPSource::kNoWildcard);
EXPECT_FALSE(source.Matches(KURL(base, "http://a.com")));
EXPECT_FALSE(source.Matches(KURL(base, "http://bar")));
EXPECT_FALSE(source.Matches(KURL(base, "http://foo.bar")));
EXPECT_FALSE(source.Matches(KURL(base, "http://o.bar")));
EXPECT_TRUE(source.Matches(KURL(base, "http://*.foo.bar")));
EXPECT_TRUE(source.Matches(KURL(base, "http://sub.foo.bar")));
EXPECT_TRUE(source.Matches(KURL(base, "http://sub.sub.foo.bar")));
auto source = network::mojom::blink::CSPSource::New(
"", "foo.bar", url::PORT_UNSPECIFIED, "", true, false);
EXPECT_FALSE(CSPSourceMatches(*source, "http", KURL(base, "http://a.com")));
EXPECT_FALSE(CSPSourceMatches(*source, "http", KURL(base, "http://bar")));
EXPECT_FALSE(
CSPSourceMatches(*source, "http", KURL(base, "http://foo.bar")));
EXPECT_FALSE(CSPSourceMatches(*source, "http", KURL(base, "http://o.bar")));
EXPECT_TRUE(
CSPSourceMatches(*source, "http", KURL(base, "http://*.foo.bar")));
EXPECT_TRUE(
CSPSourceMatches(*source, "http", KURL(base, "http://sub.foo.bar")));
EXPECT_TRUE(CSPSourceMatches(*source, "http",
KURL(base, "http://sub.sub.foo.bar")));
// Please see http://crbug.com/692505
EXPECT_TRUE(source.Matches(KURL(base, "http://.foo.bar")));
EXPECT_TRUE(
CSPSourceMatches(*source, "http", KURL(base, "http://.foo.bar")));
}
// Host is exact.
{
CSPSource source(csp.Get(), "", "foo.bar", CSPSource::kPortUnspecified, "",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://foo.bar")));
EXPECT_FALSE(source.Matches(KURL(base, "http://sub.foo.bar")));
EXPECT_FALSE(source.Matches(KURL(base, "http://bar")));
auto source = network::mojom::blink::CSPSource::New(
"", "foo.bar", url::PORT_UNSPECIFIED, "", false, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "http", KURL(base, "http://foo.bar")));
EXPECT_FALSE(
CSPSourceMatches(*source, "http", KURL(base, "http://sub.foo.bar")));
EXPECT_FALSE(CSPSourceMatches(*source, "http", KURL(base, "http://bar")));
// Please see http://crbug.com/692505
EXPECT_FALSE(source.Matches(KURL(base, "http://.foo.bar")));
EXPECT_FALSE(
CSPSourceMatches(*source, "http", KURL(base, "http://.foo.bar")));
}
// Host matching is case-insensitive.
{
CSPSource source(csp.Get(), "", "FoO.BaR", CSPSource::kPortUnspecified, "",
CSPSource::kNoWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://foo.bar")));
EXPECT_FALSE(source.Matches(KURL(base, "http://sub.foo.bar")));
auto source = network::mojom::blink::CSPSource::New(
"", "FoO.BaR", url::PORT_UNSPECIFIED, "", false, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "http", KURL(base, "http://foo.bar")));
EXPECT_FALSE(
CSPSourceMatches(*source, "http", KURL(base, "http://sub.foo.bar")));
}
// Wildcarded host matching is case-insensitive.
{
CSPSource source(csp.Get(), "", "FoO.BaR", CSPSource::kPortUnspecified, "",
CSPSource::kHasWildcard, CSPSource::kNoWildcard);
EXPECT_TRUE(source.Matches(KURL(base, "http://sub.foo.bar")));
EXPECT_FALSE(source.Matches(KURL(base, "http://foo.bar")));
auto source = network::mojom::blink::CSPSource::New(
"", "FoO.BaR", url::PORT_UNSPECIFIED, "", true, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "http", KURL(base, "http://sub.foo.bar")));
EXPECT_FALSE(
CSPSourceMatches(*source, "http", KURL(base, "http://foo.bar")));
}
}
TEST_F(CSPSourceTest, MatchingAsSelf) {
TEST(CSPSourceTest, MatchingAsSelf) {
// Testing Step 4 of
// https://w3c.github.io/webappsec-csp/#match-url-to-source-expression
struct Source {
String scheme;
String host;
String path;
int port;
bool host_wildcard;
bool port_wildcard;
};
struct TestCase {
const Source self_source;
const String& url;
bool expected;
} cases[] = {
// Same origin
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"http://example.com:80/",
true},
{{"https", "example.com", "", 443, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"https", "example.com", "", 443, false, false},
"https://example.com:443/",
true},
{{"https", "example.com", "", 4545, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"https", "example.com", "", 4545, false, false},
"https://example.com:4545/",
true}, // Mismatching origin
// Mismatching host
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"http://example2.com:80/",
false},
// Ports not matching default schemes
{{"http", "example.com", "", 8080, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 8080, false, false},
"https://example.com:443/",
false},
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"wss://example.com:8443/",
false},
// Allowed different scheme combinations (4.2.1 and 4.2.2)
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"https://example.com:443/",
true},
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"ws://example.com:80/",
true},
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"wss://example.com:443/",
true},
{{"ws", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"ws", "example.com", "", 80, false, false},
"https://example.com:443/",
true},
{{"wss", "example.com", "", 443, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"wss", "example.com", "", 443, false, false},
"https://example.com:443/",
true},
{{"https", "example.com", "", 443, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"https", "example.com", "", 443, false, false},
"wss://example.com:443/",
true},
// Ports not set (aka default)
{{"https", "example.com", "", CSPSource::kPortUnspecified,
CSPSource::kNoWildcard, CSPSource::kNoWildcard},
{{"https", "example.com", "", url::PORT_UNSPECIFIED, false, false},
"wss://example.com:443/",
true},
{{"https", "example.com", "", 443, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"https", "example.com", "", 443, false, false},
"wss://example.com/",
true},
// Paths are ignored
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"https://example.com:443/some-path-here",
true},
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"ws://example.com:80/some-other-path-here",
true},
// Custom schemes
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"custom-scheme://example.com/",
false},
{{"http", "example.com", "", 80, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"http", "example.com", "", 80, false, false},
"custom-scheme://example.com:80/",
false},
{{"https", "example.com", "", 443, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"https", "example.com", "", 443, false, false},
"custom-scheme://example.com/",
false},
{{"https", "example.com", "", 443, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"https", "example.com", "", 443, false, false},
"custom-scheme://example.com:443/",
false},
{{"https", "example.com", "", 443, CSPSource::kNoWildcard,
CSPSource::kNoWildcard},
{{"https", "example.com", "", 443, false, false},
"custom-scheme://example.com/some-path",
false},
{{"http", "example.com", "", CSPSource::kPortUnspecified,
CSPSource::kNoWildcard, CSPSource::kNoWildcard},
{{"http", "example.com", "", url::PORT_UNSPECIFIED, false, false},
"custom-scheme://example.com/some-path",
false},
};
KURL base;
for (const auto& test : cases) {
CSPSource* self_source = MakeGarbageCollected<CSPSource>(
csp.Get(), test.self_source.scheme, test.self_source.host,
test.self_source.port, test.self_source.path,
test.self_source.host_wildcard, test.self_source.port_wildcard);
EXPECT_EQ(self_source->MatchesAsSelf(KURL(base, test.url)), test.expected);
auto self_source = network::mojom::blink::CSPSource::New(
test.self_source.scheme, test.self_source.host, test.self_source.port,
test.self_source.path, test.self_source.host_wildcard,
test.self_source.port_wildcard);
EXPECT_EQ(test.expected,
CSPSourceMatchesAsSelf(*self_source, "", KURL(base, test.url)));
}
}
......
......@@ -160,10 +160,14 @@ void SourceListDirective::Parse(const UChar* begin, const UChar* end) {
const UChar* begin_source = position;
SkipWhile<UChar, IsSourceCharacter>(position, end);
String scheme, host, path;
int port = CSPSource::kPortUnspecified;
CSPSource::WildcardDisposition host_wildcard = CSPSource::kNoWildcard;
CSPSource::WildcardDisposition port_wildcard = CSPSource::kNoWildcard;
// We need to initialize all strings, since they can't be null in the mojo
// struct.
String scheme = "";
String host = "";
String path = "";
int port = url::PORT_UNSPECIFIED;
bool host_wildcard = false;
bool port_wildcard = false;
if (ParseSource(begin_source, position, &scheme, &host, &port, &path,
&host_wildcard, &port_wildcard)) {
......@@ -175,8 +179,8 @@ void SourceListDirective::Parse(const UChar* begin, const UChar* end) {
if (ContentSecurityPolicy::GetDirectiveType(host) !=
ContentSecurityPolicy::DirectiveType::kUndefined)
policy_->ReportDirectiveAsSourceExpression(directive_name_, host);
list_.push_back(MakeGarbageCollected<CSPSource>(
policy_, scheme, host, port, path, host_wildcard, port_wildcard));
list_.push_back(network::mojom::blink::CSPSource::New(
scheme, host, port, path, host_wildcard, port_wildcard));
} else {
policy_->ReportInvalidSourceExpression(
directive_name_, String(begin_source, static_cast<wtf_size_t>(
......@@ -190,15 +194,14 @@ void SourceListDirective::Parse(const UChar* begin, const UChar* end) {
// source = scheme ":"
// / ( [ scheme "://" ] host [ port ] [ path ] )
// / "'self'"
bool SourceListDirective::ParseSource(
const UChar* begin,
const UChar* end,
String* scheme,
String* host,
int* port,
String* path,
CSPSource::WildcardDisposition* host_wildcard,
CSPSource::WildcardDisposition* port_wildcard) {
bool SourceListDirective::ParseSource(const UChar* begin,
const UChar* end,
String* scheme,
String* host,
int* port,
String* path,
bool* host_wildcard,
bool* port_wildcard) {
if (begin == end)
return false;
......@@ -339,7 +342,7 @@ bool SourceListDirective::ParseSource(
if (!ParsePort(begin_port, begin_path, port, port_wildcard))
return false;
} else {
*port = CSPSource::kPortUnspecified;
*port = url::PORT_UNSPECIFIED;
}
if (begin_path != end) {
......@@ -480,14 +483,13 @@ bool SourceListDirective::ParseScheme(const UChar* begin,
// host-char = ALPHA / DIGIT / "-"
//
// static
bool SourceListDirective::ParseHost(
const UChar* begin,
const UChar* end,
String* host,
CSPSource::WildcardDisposition* host_wildcard) {
bool SourceListDirective::ParseHost(const UChar* begin,
const UChar* end,
String* host,
bool* host_wildcard) {
DCHECK(begin <= end);
DCHECK(host->IsEmpty());
DCHECK(*host_wildcard == CSPSource::kNoWildcard);
DCHECK(!*host_wildcard);
if (begin == end)
return false;
......@@ -496,7 +498,7 @@ bool SourceListDirective::ParseHost(
// Parse "*" or [ "*." ].
if (SkipExactly<UChar>(position, end, '*')) {
*host_wildcard = CSPSource::kHasWildcard;
*host_wildcard = true;
if (position == end) {
// "*"
......@@ -553,14 +555,13 @@ bool SourceListDirective::ParsePath(const UChar* begin,
// port = ":" ( 1*DIGIT / "*" )
//
bool SourceListDirective::ParsePort(
const UChar* begin,
const UChar* end,
int* port,
CSPSource::WildcardDisposition* port_wildcard) {
bool SourceListDirective::ParsePort(const UChar* begin,
const UChar* end,
int* port,
bool* port_wildcard) {
DCHECK(begin <= end);
DCHECK_EQ(*port, CSPSource::kPortUnspecified);
DCHECK(*port_wildcard == CSPSource::kNoWildcard);
DCHECK_EQ(*port, url::PORT_UNSPECIFIED);
DCHECK(!*port_wildcard);
if (!SkipExactly<UChar>(begin, end, ':'))
NOTREACHED();
......@@ -569,8 +570,8 @@ bool SourceListDirective::ParsePort(
return false;
if (end - begin == 1 && *begin == '*') {
*port = CSPSource::kPortUnspecified;
*port_wildcard = CSPSource::kHasWildcard;
*port = url::PORT_UNSPECIFIED;
*port_wildcard = true;
return true;
}
......@@ -636,9 +637,11 @@ void SourceListDirective::AddSourceHash(
bool SourceListDirective::HasSourceMatchInList(
const KURL& url,
ResourceRequest::RedirectStatus redirect_status) const {
for (wtf_size_t i = 0; i < list_.size(); ++i) {
if (list_[i]->Matches(url, redirect_status))
for (const auto& source : list_) {
if (CSPSourceMatches(*source, policy_->GetSelfProtocol(), url,
redirect_status)) {
return true;
}
}
return false;
......@@ -661,7 +664,7 @@ network::mojom::blink::CSPSourceListPtr
SourceListDirective::ExposeForNavigationalChecks() const {
WTF::Vector<network::mojom::blink::CSPSourcePtr> sources;
for (const auto& source : list_)
sources.push_back(source->ExposeForNavigationalChecks());
sources.push_back(source.Clone());
// We do not need nonces and hashes for navigational checks
WTF::Vector<WTF::String> nonces;
......@@ -675,7 +678,6 @@ SourceListDirective::ExposeForNavigationalChecks() const {
void SourceListDirective::Trace(Visitor* visitor) const {
visitor->Trace(policy_);
visitor->Trace(list_);
CSPDirective::Trace(visitor);
}
......
......@@ -71,17 +71,17 @@ class CORE_EXPORT SourceListDirective final : public CSPDirective {
String* host,
int* port,
String* path,
CSPSource::WildcardDisposition*,
CSPSource::WildcardDisposition*);
bool* is_host_wildcard,
bool* is_port_wildcard);
bool ParseScheme(const UChar* begin, const UChar* end, String* scheme);
static bool ParseHost(const UChar* begin,
const UChar* end,
String* host,
CSPSource::WildcardDisposition*);
bool* is_host_wildcard);
bool ParsePort(const UChar* begin,
const UChar* end,
int* port,
CSPSource::WildcardDisposition*);
bool* is_port_wildcard);
bool ParsePath(const UChar* begin, const UChar* end, String* path);
bool ParseNonce(const UChar* begin, const UChar* end, String* nonce);
bool ParseHash(const UChar* begin,
......@@ -105,7 +105,7 @@ class CORE_EXPORT SourceListDirective final : public CSPDirective {
bool HasSourceMatchInList(const KURL&, ResourceRequest::RedirectStatus) const;
Member<ContentSecurityPolicy> policy_;
HeapVector<Member<CSPSource>> list_;
WTF::Vector<network::mojom::blink::CSPSourcePtr> list_;
String directive_name_;
bool allow_self_;
bool allow_star_;
......
......@@ -26,8 +26,8 @@ class SourceListDirectiveTest : public testing::Test {
String host;
const int port;
String path;
CSPSource::WildcardDisposition host_wildcard;
CSPSource::WildcardDisposition port_wildcard;
bool host_wildcard;
bool port_wildcard;
};
void SetUp() override {
......@@ -430,7 +430,7 @@ TEST_F(SourceListDirectiveTest, ParseHost) {
for (const auto& test : cases) {
String host;
CSPSource::WildcardDisposition disposition = CSPSource::kNoWildcard;
bool disposition = false;
Vector<UChar> characters;
test.sources.AppendTo(characters);
const UChar* start = characters.data();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment