blob: 9ef2f4e367fd1b254c9ac2226f165116e51063a1 [file] [log] [blame]
// Copyright 2015 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/csp_source.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
TEST(CSPSourceTest, BasicMatching) {
KURL base;
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(CSPSourceTest, BasicPathMatching) {
KURL base;
auto a = network::mojom::blink::CSPSource::New("http", "example.com", 8000,
"/", false, false);
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(
CSPSourceMatches(*a, "", KURL(base, "http://example.com:8000path")));
EXPECT_FALSE(
CSPSourceMatches(*a, "", KURL(base, "http://example.com:9000/")));
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(
CSPSourceMatches(*b, "", KURL(base, "http://example.com:8000path")));
EXPECT_FALSE(
CSPSourceMatches(*b, "", KURL(base, "http://example.com:9000/")));
}
TEST(CSPSourceTest, WildcardMatching) {
KURL base;
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_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(CSPSourceTest, RedirectMatching) {
KURL base;
auto source = network::mojom::blink::CSPSource::New(
"http", "example.com", 8000, "/bar/", false, false);
EXPECT_TRUE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:8000/"),
ResourceRequest::RedirectStatus::kFollowedRedirect));
EXPECT_TRUE(
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(
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(CSPSourceTest, InsecureSchemeMatchesSecureScheme) {
KURL base;
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(CSPSourceTest, InsecureHostSchemeMatchesSecureScheme) {
KURL base;
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(CSPSourceTest, SchemeIsEmpty) {
KURL base;
// Self scheme is http.
{
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.
{
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.
{
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
{
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(CSPSourceMatches(*source, "non-standard-scheme",
KURL(base, "non-standard-scheme://a.com")));
}
}
TEST(CSPSourceTest, InsecureHostSchemePortMatchesSecurePort) {
KURL base;
// source scheme is "http", source port is 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(
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
{
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
{
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(CSPSourceMatches(*source, "http",
KURL(base, "http://example.com:443")));
}
// source port is empty
{
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(
CSPSourceMatches(*source, "", KURL(base, "https://example.com:80")));
EXPECT_FALSE(
CSPSourceMatches(*source, "", KURL(base, "http://example.com:443")));
}
}
TEST(CSPSourceTest, HostMatches) {
KURL base;
// Host is * (source-expression = "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
{
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(
CSPSourceMatches(*source, "http", KURL(base, "http://.foo.bar")));
}
// Host is exact.
{
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(
CSPSourceMatches(*source, "http", KURL(base, "http://.foo.bar")));
}
// Host matching is case-insensitive.
{
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.
{
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(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, false, false},
"http://example.com:80/",
true},
{{"https", "example.com", "", 443, false, false},
"https://example.com:443/",
true},
{{"https", "example.com", "", 4545, false, false},
"https://example.com:4545/",
true}, // Mismatching origin
// Mismatching host
{{"http", "example.com", "", 80, false, false},
"http://example2.com:80/",
false},
// Ports not matching default schemes
{{"http", "example.com", "", 8080, false, false},
"https://example.com:443/",
false},
{{"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, false, false},
"https://example.com:443/",
true},
{{"http", "example.com", "", 80, false, false},
"ws://example.com:80/",
true},
{{"http", "example.com", "", 80, false, false},
"wss://example.com:443/",
true},
{{"ws", "example.com", "", 80, false, false},
"https://example.com:443/",
true},
{{"wss", "example.com", "", 443, false, false},
"https://example.com:443/",
true},
{{"https", "example.com", "", 443, false, false},
"wss://example.com:443/",
true},
// Ports not set (aka default)
{{"https", "example.com", "", url::PORT_UNSPECIFIED, false, false},
"wss://example.com:443/",
true},
{{"https", "example.com", "", 443, false, false},
"wss://example.com/",
true},
// Paths are ignored
{{"http", "example.com", "", 80, false, false},
"https://example.com:443/some-path-here",
true},
{{"http", "example.com", "", 80, false, false},
"ws://example.com:80/some-other-path-here",
true},
// Custom schemes
{{"http", "example.com", "", 80, false, false},
"custom-scheme://example.com/",
false},
{{"http", "example.com", "", 80, false, false},
"custom-scheme://example.com:80/",
false},
{{"https", "example.com", "", 443, false, false},
"custom-scheme://example.com/",
false},
{{"https", "example.com", "", 443, false, false},
"custom-scheme://example.com:443/",
false},
{{"https", "example.com", "", 443, false, false},
"custom-scheme://example.com/some-path",
false},
{{"http", "example.com", "", url::PORT_UNSPECIFIED, false, false},
"custom-scheme://example.com/some-path",
false},
};
KURL base;
for (const auto& test : cases) {
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)));
}
}
} // namespace blink