blob: afaeb5a9289af20d92545f87476038803da8314c [file] [log] [blame]
// Copyright 2019 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 <string>
#include "base/memory/ref_counted.h"
#include "base/test/scoped_feature_list.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "services/network/cross_origin_resource_policy.h"
#include "services/network/public/cpp/features.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
CrossOriginResourcePolicy::ParsedHeader ParseHeader(
const std::string& test_headers) {
std::string all_headers = "HTTP/1.1 200 OK\n" + test_headers + "\n";
auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(all_headers));
return CrossOriginResourcePolicy::ParseHeaderForTesting(headers.get());
}
// This test is somewhat redundant with
// wpt/fetch/cross-origin-resource-policy/syntax.any.js
// The delta in coverage is mostly around testing case insensitivity of the
// header name.
TEST(CrossOriginResourcePolicyTest, ParseHeader) {
// Basic tests.
EXPECT_EQ(CrossOriginResourcePolicy::kNoHeader, ParseHeader(""));
EXPECT_EQ(CrossOriginResourcePolicy::kSameOrigin,
ParseHeader("Cross-Origin-Resource-Policy: same-origin"));
EXPECT_EQ(CrossOriginResourcePolicy::kSameSite,
ParseHeader("Cross-Origin-Resource-Policy: same-site"));
// Header names are case-insensitive.
EXPECT_EQ(CrossOriginResourcePolicy::kSameOrigin,
ParseHeader("Cross-Origin-RESOURCE-Policy: same-origin"));
EXPECT_EQ(CrossOriginResourcePolicy::kSameSite,
ParseHeader("Cross-ORIGIN-Resource-Policy: same-site"));
// Header values are case-sensitive.
EXPECT_EQ(CrossOriginResourcePolicy::kParsingError,
ParseHeader("Cross-Origin-Resource-Policy: sAme-origin"));
EXPECT_EQ(CrossOriginResourcePolicy::kParsingError,
ParseHeader("Cross-Origin-Resource-Policy: saMe-site"));
// Specific origins are not yet part of the spec. See also:
// https://github.com/whatwg/fetch/issues/760
EXPECT_EQ(
CrossOriginResourcePolicy::kParsingError,
ParseHeader("Cross-Origin-Resource-Policy: https://www.example.com"));
// Parsing failures explicitly called out in the note for step 3:
// https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header:
//
// > This means that `Cross-Origin-Resource-Policy: same-site, same-origin`
// > ends up as allowed below as it will never match anything. Two or more
// > `Cross-Origin-Resource-Policy` headers will have the same effect.
//
EXPECT_EQ(
CrossOriginResourcePolicy::kParsingError,
ParseHeader("Cross-Origin-Resource-Policy: same-site, same-origin"));
EXPECT_EQ(CrossOriginResourcePolicy::kParsingError,
ParseHeader("Cross-Origin-Resource-Policy: same-site\n"
"Cross-Origin-Resource-Policy: same-origin"));
}
TEST(CrossOriginResourcePolicyTest, CrossSiteHeaderWithCOEP) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kCrossOriginEmbedderPolicy);
EXPECT_EQ(CrossOriginResourcePolicy::kCrossOrigin,
ParseHeader("Cross-Origin-Resource-Policy: cross-origin"));
}
TEST(CrossOriginResourcePolicyTest, CrossSiteHeaderWithoutCOEP) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(features::kCrossOriginEmbedderPolicy);
EXPECT_EQ(CrossOriginResourcePolicy::kParsingError,
ParseHeader("Cross-Origin-Resource-Policy: cross-origin"));
}
bool ShouldAllowSameSite(const std::string& initiator,
const std::string& target) {
return CrossOriginResourcePolicy::ShouldAllowSameSiteForTesting(
url::Origin::Create(GURL(initiator)), url::Origin::Create(GURL(target)));
}
TEST(CrossOriginResourcePolicyTest, ShouldAllowSameSite) {
// Basic tests.
EXPECT_TRUE(ShouldAllowSameSite("https://foo.com", "https://foo.com"));
EXPECT_FALSE(ShouldAllowSameSite("https://foo.com", "https://bar.com"));
// Subdomains.
EXPECT_TRUE(ShouldAllowSameSite("https://foo.a.com", "https://a.com"));
EXPECT_TRUE(ShouldAllowSameSite("https://a.com", "https://bar.a.com"));
EXPECT_TRUE(ShouldAllowSameSite("https://foo.a.com", "https://bar.a.com"));
EXPECT_FALSE(ShouldAllowSameSite("https://foo.a.com", "https://b.com"));
EXPECT_FALSE(ShouldAllowSameSite("https://a.com", "https://bar.b.com"));
EXPECT_FALSE(ShouldAllowSameSite("https://foo.a.com", "https://bar.b.com"));
// Same host, different HTTPS vs HTTP scheme.
//
// The intent here is that HTTPS response shouldn't be exposed to a page
// served over HTTP (which might leak it), but serving an HTTP response isn't
// secret so we don't need to block it from HTTPS pages. This behavior should
// hopefully help with adoption on sites that still need to use mixed
// http/https content.
EXPECT_TRUE(ShouldAllowSameSite(
/* initiator = */ "https://foo.com", /* target = */ "http://foo.com"));
EXPECT_FALSE(ShouldAllowSameSite(
/* initiator = */ "http://foo.com", /* target = */ "https://foo.com"));
// IP addresses.
//
// Different sites might be served from the same IP address - they should
// still be considered to be different sites - see also
// https://url.spec.whatwg.org/#host-same-site which excludes IP addresses by
// imposing the requirement that one of the addresses has to have a non-null
// registrable domain.
EXPECT_FALSE(ShouldAllowSameSite("http://127.0.0.1", "http://127.0.0.1"));
}
TEST(CrossOriginResourcePolicyTest, WithCOEP) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kCrossOriginEmbedderPolicy);
ResourceResponseInfo corp_none;
ResourceResponseInfo corp_same_origin;
ResourceResponseInfo corp_cross_origin;
corp_same_origin.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(
"HTTP/1.1 200 OK\n"
"cross-origin-resource-policy: same-origin\n"));
corp_cross_origin.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(
"HTTP/1.1 200 OK\n"
"cross-origin-resource-policy: cross-origin\n"));
GURL destination("https://www.example.com/");
url::Origin destination_origin =
url::Origin::Create(GURL("https://www.example.com"));
url::Origin another_origin =
url::Origin::Create(GURL("https://www2.example.com"));
constexpr auto kAllow = CrossOriginResourcePolicy::kAllow;
constexpr auto kBlock = CrossOriginResourcePolicy::kBlock;
using mojom::RequestMode;
struct TestCase {
const RequestMode request_mode;
const url::Origin origin;
const ResourceResponseInfo response_info;
const CrossOriginResourcePolicy::VerificationResult
expectation_with_coep_none;
const CrossOriginResourcePolicy::VerificationResult
expectation_with_coep_require_corp;
} test_cases[] = {
// We don't have a cross-origin-resource-policy header on a response. That
// leads to kBlock when COEP: kRequireCorp is used.
{RequestMode::kNoCors, another_origin, corp_none, kAllow, kBlock},
// We have "cross-origin-resource-policy: same-origin", so regardless of
// COEP the response is blocked.
{RequestMode::kNoCors, another_origin, corp_same_origin, kBlock, kBlock},
// We have "cross-origin-resource-policy: cross-origin", so regardless of
// COEP the response is allowed.
{RequestMode::kNoCors, another_origin, corp_cross_origin, kAllow, kAllow},
// The origin of the request URL and request's origin match, so regardless
// of COEP the response is allowed.
{RequestMode::kNoCors, destination_origin, corp_same_origin, kAllow,
kAllow},
// The request mode is "cors", so so regardless of COEP the response is
// allowed.
{RequestMode::kCors, another_origin, corp_same_origin, kAllow, kAllow},
};
for (const auto& test_case : test_cases) {
EXPECT_EQ(test_case.expectation_with_coep_none,
CrossOriginResourcePolicy::Verify(
destination, test_case.origin, test_case.response_info,
test_case.request_mode, test_case.origin,
mojom::CrossOriginEmbedderPolicy::kNone));
EXPECT_EQ(test_case.expectation_with_coep_require_corp,
CrossOriginResourcePolicy::Verify(
destination, test_case.origin, test_case.response_info,
test_case.request_mode, test_case.origin,
mojom::CrossOriginEmbedderPolicy::kRequireCorp));
}
}
} // namespace network