Allow an opaque url::Origin to remember where it came from.
The problem being solved here is that, although various web platform features can cause documents to be placed in opaque origins, sometimes doing so obscures the actual source of the documents, which itself can be a security risk. "data:" URLs, "srcdoc" plus "sandbox" are particular tricky cases of this, as neither the URL nor the committed origin retains information about which network host the content is originally from. This CL is the first step towards solving this problem by keeping that information around in url::Origin. It is just the url::Origin changes from nick@'s work on precursor origins started in https://crrev.com/c/1028985. The precursor information must be used carefully. Opaque origins should generally not inherit privileges from the origins they derive from. However, in some cases (such as restrictions on process placement, or determining the http lock icon, or determining content script injection) this information may be relevant to ensure that entering an opaque origin does not grant privileges initially denied to the original non-opaque origin. This new tracking is transitive: meaning if a page loaded from http://example.com navigates to a data URL, which then navigates to a blob:null URL, which embeds an <iframe sandbox srcdoc="...">, the precursor origin for the sandboxed iframe is retained to be "http://example.com". Bug: 882053 Cq-Include-Trybots: luci.chromium.try:linux_mojo Change-Id: I021245c624b78f08bd835c5cae9fde7ec5e44b80 Reviewed-on: https://chromium-review.googlesource.com/1214745 Commit-Queue: Nasko Oskov <nasko@chromium.org> Reviewed-by: Ian Clelland <iclelland@chromium.org> Reviewed-by: Luna Lu <loonybear@chromium.org> Reviewed-by: Daniel Cheng <dcheng@chromium.org> Reviewed-by: David Benjamin <davidben@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org> Cr-Commit-Position: refs/heads/master@{#591867}
This commit is contained in:
content/child
net/http
services/network/public/cpp
third_party/blink
common
feature_policy
public
common
feature_policy
tools/ipc_fuzzer/fuzzer
url
@ -17,15 +17,16 @@ namespace content {
|
||||
|
||||
void CheckCastedOriginsAlreadyNormalized(
|
||||
const blink::WebSecurityOrigin& origin) {
|
||||
url::Origin checked_origin =
|
||||
url::Origin::UnsafelyCreateOriginWithoutNormalization(
|
||||
if (origin.IsOpaque())
|
||||
return;
|
||||
|
||||
base::Optional<url::Origin> checked_origin =
|
||||
url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
origin.Protocol().Utf8(), origin.Host().Utf8(),
|
||||
origin.EffectivePort());
|
||||
url::Origin non_checked_origin = url::Origin::CreateFromNormalizedTuple(
|
||||
origin.Protocol().Utf8(), origin.Host().Utf8(), origin.EffectivePort());
|
||||
EXPECT_EQ(checked_origin.scheme(), non_checked_origin.scheme());
|
||||
EXPECT_EQ(checked_origin.host(), non_checked_origin.host());
|
||||
EXPECT_EQ(checked_origin.port(), non_checked_origin.port());
|
||||
EXPECT_EQ(checked_origin, non_checked_origin);
|
||||
}
|
||||
|
||||
TEST(BlinkPlatformTest, CastWebSecurityOrigin) {
|
||||
|
@ -450,17 +450,17 @@ TEST_F(AlternateProtocolServerPropertiesTest, Set) {
|
||||
ASSERT_EQ(3u, map.size());
|
||||
AlternativeServiceMap::const_iterator map_it = map.begin();
|
||||
|
||||
EXPECT_TRUE(map_it->first.Equals(test_server2));
|
||||
EXPECT_EQ(map_it->first, test_server2);
|
||||
ASSERT_EQ(1u, map_it->second.size());
|
||||
EXPECT_EQ(alternative_service3, map_it->second[0].alternative_service());
|
||||
EXPECT_EQ(expiration3, map_it->second[0].expiration());
|
||||
++map_it;
|
||||
EXPECT_TRUE(map_it->first.Equals(test_server1));
|
||||
EXPECT_EQ(map_it->first, test_server1);
|
||||
ASSERT_EQ(1u, map_it->second.size());
|
||||
EXPECT_EQ(alternative_service1, map_it->second[0].alternative_service());
|
||||
EXPECT_EQ(expiration1, map_it->second[0].expiration());
|
||||
++map_it;
|
||||
EXPECT_TRUE(map_it->first.Equals(test_server3));
|
||||
EXPECT_EQ(map_it->first, test_server3);
|
||||
ASSERT_EQ(1u, map_it->second.size());
|
||||
EXPECT_EQ(alternative_service4, map_it->second[0].alternative_service());
|
||||
EXPECT_EQ(expiration4, map_it->second[0].expiration());
|
||||
@ -603,7 +603,7 @@ TEST_F(AlternateProtocolServerPropertiesTest, MRUOfGetAlternativeServiceInfos) {
|
||||
|
||||
const AlternativeServiceMap& map = impl_.alternative_service_map();
|
||||
AlternativeServiceMap::const_iterator it = map.begin();
|
||||
EXPECT_TRUE(it->first.Equals(test_server2));
|
||||
EXPECT_EQ(it->first, test_server2);
|
||||
ASSERT_EQ(1u, it->second.size());
|
||||
EXPECT_EQ(alternative_service2, it->second[0].alternative_service());
|
||||
|
||||
@ -615,7 +615,7 @@ TEST_F(AlternateProtocolServerPropertiesTest, MRUOfGetAlternativeServiceInfos) {
|
||||
|
||||
// GetAlternativeServices should reorder the AlternateProtocol map.
|
||||
it = map.begin();
|
||||
EXPECT_TRUE(it->first.Equals(test_server1));
|
||||
EXPECT_EQ(it->first, test_server1);
|
||||
ASSERT_EQ(1u, it->second.size());
|
||||
EXPECT_EQ(alternative_service1, it->second[0].alternative_service());
|
||||
}
|
||||
@ -802,7 +802,7 @@ TEST_F(AlternateProtocolServerPropertiesTest, AlternativeServiceWithScheme) {
|
||||
|
||||
const net::AlternativeServiceMap& map = impl_.alternative_service_map();
|
||||
net::AlternativeServiceMap::const_iterator it = map.begin();
|
||||
EXPECT_TRUE(it->first.Equals(http_server));
|
||||
EXPECT_EQ(it->first, http_server);
|
||||
ASSERT_EQ(2u, it->second.size());
|
||||
EXPECT_EQ(alternative_service1, it->second[0].alternative_service());
|
||||
EXPECT_EQ(alternative_service2, it->second[1].alternative_service());
|
||||
@ -839,7 +839,7 @@ TEST_F(AlternateProtocolServerPropertiesTest, ClearAlternativeServices) {
|
||||
|
||||
const net::AlternativeServiceMap& map = impl_.alternative_service_map();
|
||||
net::AlternativeServiceMap::const_iterator it = map.begin();
|
||||
EXPECT_TRUE(it->first.Equals(test_server));
|
||||
EXPECT_EQ(it->first, test_server);
|
||||
ASSERT_EQ(2u, it->second.size());
|
||||
EXPECT_EQ(alternative_service1, it->second[0].alternative_service());
|
||||
EXPECT_EQ(alternative_service2, it->second[1].alternative_service());
|
||||
@ -1381,13 +1381,13 @@ TEST_F(ServerNetworkStatsServerPropertiesTest, Set) {
|
||||
ASSERT_EQ(3u, map.size());
|
||||
ServerNetworkStatsMap::const_iterator map_it = map.begin();
|
||||
|
||||
EXPECT_TRUE(map_it->first.Equals(docs_server));
|
||||
EXPECT_EQ(map_it->first, docs_server);
|
||||
EXPECT_EQ(new_stats_docs, map_it->second);
|
||||
++map_it;
|
||||
EXPECT_TRUE(map_it->first.Equals(google_server));
|
||||
EXPECT_EQ(map_it->first, google_server);
|
||||
EXPECT_EQ(stats_google, map_it->second);
|
||||
++map_it;
|
||||
EXPECT_TRUE(map_it->first.Equals(mail_server));
|
||||
EXPECT_EQ(map_it->first, mail_server);
|
||||
EXPECT_EQ(stats_mail, map_it->second);
|
||||
}
|
||||
|
||||
|
@ -500,19 +500,20 @@ bool ParamTraits<url::Origin>::Read(const base::Pickle* m,
|
||||
uint16_t port;
|
||||
if (!ReadParam(m, iter, &unique) || !ReadParam(m, iter, &scheme) ||
|
||||
!ReadParam(m, iter, &host) || !ReadParam(m, iter, &port)) {
|
||||
*p = url::Origin();
|
||||
return false;
|
||||
}
|
||||
|
||||
*p = unique ? url::Origin()
|
||||
: url::Origin::UnsafelyCreateOriginWithoutNormalization(
|
||||
scheme, host, port);
|
||||
if (unique) {
|
||||
*p = url::Origin();
|
||||
} else {
|
||||
base::Optional<url::Origin> origin =
|
||||
url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(scheme, host,
|
||||
port);
|
||||
if (!origin.has_value())
|
||||
return false;
|
||||
|
||||
// If a unique origin was created, but the unique flag wasn't set, then
|
||||
// the values provided to 'UnsafelyCreateOriginWithoutNormalization' were
|
||||
// invalid; kill the renderer.
|
||||
if (!unique && p->unique())
|
||||
return false;
|
||||
*p = origin.value();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ std::unique_ptr<FeaturePolicy::Allowlist> AllowlistFromDeclaration(
|
||||
result->AddAll();
|
||||
for (const auto& origin : parsed_declaration.origins)
|
||||
result->Add(origin);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -82,7 +83,7 @@ bool FeaturePolicy::Allowlist::Contains(const url::Origin& origin) const {
|
||||
if (matches_all_origins_)
|
||||
return true;
|
||||
for (const auto& targetOrigin : origins_) {
|
||||
if (targetOrigin.IsSameOriginWith(origin))
|
||||
if (!origin.unique() && targetOrigin.IsSameOriginWith(origin))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -125,12 +126,8 @@ bool FeaturePolicy::IsFeatureEnabledForOrigin(
|
||||
feature_list_.at(feature);
|
||||
if (default_policy == FeaturePolicy::FeatureDefault::EnableForAll)
|
||||
return true;
|
||||
if (default_policy == FeaturePolicy::FeatureDefault::EnableForSelf) {
|
||||
// TODO(iclelland): Remove the pointer equality check once it is possible to
|
||||
// compare opaque origins successfully against themselves.
|
||||
// https://crbug.com/690520
|
||||
return (&origin_ == &origin) || origin_.IsSameOriginWith(origin);
|
||||
}
|
||||
if (default_policy == FeaturePolicy::FeatureDefault::EnableForSelf)
|
||||
return origin_.IsSameOriginWith(origin);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -170,7 +167,14 @@ void FeaturePolicy::SetHeaderPolicy(const ParsedFeaturePolicy& parsed_header) {
|
||||
|
||||
FeaturePolicy::FeaturePolicy(url::Origin origin,
|
||||
const FeatureList& feature_list)
|
||||
: origin_(origin), feature_list_(feature_list) {}
|
||||
: origin_(std::move(origin)), feature_list_(feature_list) {
|
||||
if (origin_.unique()) {
|
||||
// FeaturePolicy was written expecting opaque Origins to be indistinct, but
|
||||
// this has changed. Split out a new opaque origin here, to defend against
|
||||
// origin-equality.
|
||||
origin_ = origin_.DeriveNewOpaqueOrigin();
|
||||
}
|
||||
}
|
||||
|
||||
FeaturePolicy::~FeaturePolicy() = default;
|
||||
|
||||
|
@ -1209,8 +1209,8 @@ TEST_F(FeaturePolicyTest, TestSandboxedPolicyCanBePropagated) {
|
||||
// However, it will not pass that on to any other origin
|
||||
std::unique_ptr<FeaturePolicy> policy1 =
|
||||
CreateFromParentPolicy(nullptr, origin_a_);
|
||||
url::Origin sandboxed_origin_1 = url::Origin();
|
||||
url::Origin sandboxed_origin_2 = url::Origin();
|
||||
url::Origin sandboxed_origin_1 = origin_a_.DeriveNewOpaqueOrigin();
|
||||
url::Origin sandboxed_origin_2 = sandboxed_origin_1.DeriveNewOpaqueOrigin();
|
||||
ParsedFeaturePolicy frame_policy_1 = {
|
||||
{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
|
||||
std::unique_ptr<FeaturePolicy> policy2 = CreateFromParentWithFramePolicy(
|
||||
|
@ -40,7 +40,7 @@ namespace blink {
|
||||
// ----------
|
||||
// Allowlists are collections of origins, although two special terms can be used
|
||||
// when declaring them:
|
||||
// "self" refers to the orgin of the frame which is declaring the policy.
|
||||
// "self" refers to the origin of the frame which is declaring the policy.
|
||||
// "*" refers to all origins; any origin will match an allowlist which
|
||||
// contains it.
|
||||
//
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/shared_memory_handle.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/unguessable_token.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "ipc/ipc_message.h"
|
||||
@ -641,6 +642,20 @@ struct FuzzTraits<base::DictionaryValue> {
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FuzzTraits<base::UnguessableToken> {
|
||||
static bool Fuzz(base::UnguessableToken* p, Fuzzer* fuzzer) {
|
||||
auto low = p->GetLowForSerialization();
|
||||
auto high = p->GetHighForSerialization();
|
||||
if (!FuzzParam(&low, fuzzer))
|
||||
return false;
|
||||
if (!FuzzParam(&high, fuzzer))
|
||||
return false;
|
||||
*p = base::UnguessableToken::Deserialize(high, low);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FuzzTraits<viz::CompositorFrame> {
|
||||
static bool Fuzz(viz::CompositorFrame* p, Fuzzer* fuzzer) {
|
||||
@ -1509,21 +1524,48 @@ struct FuzzTraits<ui::LatencyInfo> {
|
||||
template <>
|
||||
struct FuzzTraits<url::Origin> {
|
||||
static bool Fuzz(url::Origin* p, Fuzzer* fuzzer) {
|
||||
std::string scheme = p->scheme();
|
||||
std::string host = p->host();
|
||||
uint16_t port = p->port();
|
||||
bool opaque = p->unique();
|
||||
if (!FuzzParam(&opaque, fuzzer))
|
||||
return false;
|
||||
std::string scheme = p->GetTupleOrPrecursorTupleIfOpaque().scheme();
|
||||
std::string host = p->GetTupleOrPrecursorTupleIfOpaque().host();
|
||||
uint16_t port = p->GetTupleOrPrecursorTupleIfOpaque().port();
|
||||
if (!FuzzParam(&scheme, fuzzer))
|
||||
return false;
|
||||
if (!FuzzParam(&host, fuzzer))
|
||||
return false;
|
||||
if (!FuzzParam(&port, fuzzer))
|
||||
return false;
|
||||
*p = url::Origin::UnsafelyCreateOriginWithoutNormalization(scheme, host,
|
||||
port);
|
||||
|
||||
// Force a unique origin 1% of the time:
|
||||
if (RandInRange(100) == 1)
|
||||
*p = url::Origin();
|
||||
base::Optional<url::Origin> origin;
|
||||
if (!opaque) {
|
||||
origin = url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
scheme, host, port);
|
||||
} else {
|
||||
base::Optional<base::UnguessableToken> token =
|
||||
p->GetNonceForSerialization();
|
||||
if (!token)
|
||||
token = base::UnguessableToken::Deserialize(RandU64(), RandU64());
|
||||
if (!FuzzParam(&(*token), fuzzer))
|
||||
return false;
|
||||
origin = url::Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
scheme, host, port, url::Origin::Nonce(*token));
|
||||
}
|
||||
|
||||
if (!origin) {
|
||||
// This means that we produced non-canonical values that were rejected by
|
||||
// UnsafelyCreate. Which is nice, except, those are arguably interesting
|
||||
// values to be sending over the wire sometimes, to make sure they're
|
||||
// rejected at the receiving end.
|
||||
//
|
||||
// We could potentially call CreateFromNormalizedTuple here to force their
|
||||
// creation, except that could lead to invariant violations within the
|
||||
// url::Origin we construct -- and potentially crash the fuzzer. What to
|
||||
// do?
|
||||
return false;
|
||||
}
|
||||
|
||||
*p = std::move(origin).value();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -25,15 +25,14 @@ struct StructTraits<url::mojom::OriginDataView, url::Origin> {
|
||||
if (!data.ReadScheme(&scheme) || !data.ReadHost(&host))
|
||||
return false;
|
||||
|
||||
*out = url::Origin::UnsafelyCreateOriginWithoutNormalization(scheme, host,
|
||||
data.port());
|
||||
}
|
||||
base::Optional<url::Origin> origin =
|
||||
url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
scheme, host, data.port());
|
||||
if (!origin.has_value())
|
||||
return false;
|
||||
|
||||
// If a unique origin was created, but the unique flag wasn't set, then
|
||||
// the values provided to 'UnsafelyCreateOriginWithoutNormalization' were
|
||||
// invalid.
|
||||
if (!data.unique() && out->unique())
|
||||
return false;
|
||||
*out = origin.value();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -72,8 +72,9 @@ TEST(MojoGURLStructTraitsTest, Basic) {
|
||||
}
|
||||
|
||||
// Test basic Origin serialization.
|
||||
Origin non_unique = Origin::UnsafelyCreateOriginWithoutNormalization(
|
||||
"http", "www.google.com", 80);
|
||||
Origin non_unique = Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
"http", "www.google.com", 80)
|
||||
.value();
|
||||
Origin output;
|
||||
EXPECT_TRUE(proxy->BounceOrigin(non_unique, &output));
|
||||
EXPECT_EQ(non_unique, output);
|
||||
|
190
url/origin.cc
190
url/origin.cc
@ -5,11 +5,13 @@
|
||||
#include "url/origin.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/url_canon.h"
|
||||
#include "url/url_canon_stdstring.h"
|
||||
@ -18,7 +20,7 @@
|
||||
|
||||
namespace url {
|
||||
|
||||
Origin::Origin() {}
|
||||
Origin::Origin() : nonce_(Nonce()) {}
|
||||
|
||||
Origin Origin::Create(const GURL& url) {
|
||||
if (!url.is_valid())
|
||||
@ -48,56 +50,54 @@ Origin Origin::Create(const GURL& url) {
|
||||
return Origin(std::move(tuple));
|
||||
}
|
||||
|
||||
// Note: this is very similar to Create(const GURL&), but opaque origins are
|
||||
// created with CreateUniqueOpaque() rather than the default constructor.
|
||||
Origin Origin::CreateCanonical(const GURL& url) {
|
||||
if (!url.is_valid())
|
||||
return CreateUniqueOpaque();
|
||||
|
||||
SchemeHostPort tuple;
|
||||
|
||||
if (url.SchemeIsFileSystem()) {
|
||||
tuple = SchemeHostPort(*url.inner_url());
|
||||
} else if (url.SchemeIsBlob()) {
|
||||
// If we're dealing with a 'blob:' URL, https://url.spec.whatwg.org/#origin
|
||||
// defines the origin as the origin of the URL which results from parsing
|
||||
// the "path", which boils down to everything after the scheme. GURL's
|
||||
// 'GetContent()' gives us exactly that.
|
||||
tuple = SchemeHostPort(GURL(url.GetContent()));
|
||||
} else {
|
||||
tuple = SchemeHostPort(url);
|
||||
|
||||
// It's SchemeHostPort's responsibility to filter out unrecognized schemes;
|
||||
// sanity check that this is happening.
|
||||
DCHECK(tuple.IsInvalid() || url.IsStandard() ||
|
||||
base::ContainsValue(GetLocalSchemes(), url.scheme_piece()));
|
||||
}
|
||||
|
||||
if (tuple.IsInvalid())
|
||||
return CreateUniqueOpaque();
|
||||
|
||||
return Origin(std::move(tuple));
|
||||
Origin Origin::Resolve(const GURL& url, const Origin& base_origin) {
|
||||
if (url.IsAboutBlank())
|
||||
return base_origin;
|
||||
Origin result = Origin::Create(url);
|
||||
if (!result.unique())
|
||||
return result;
|
||||
return base_origin.DeriveNewOpaqueOrigin();
|
||||
}
|
||||
|
||||
Origin::Origin(const Origin& other) = default;
|
||||
Origin& Origin::operator=(const Origin& other) = default;
|
||||
Origin::Origin(Origin&& other) = default;
|
||||
Origin& Origin::operator=(Origin&& other) = default;
|
||||
|
||||
Origin::~Origin() = default;
|
||||
|
||||
// static
|
||||
Origin Origin::UnsafelyCreateOriginWithoutNormalization(
|
||||
base::Optional<Origin> Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
base::StringPiece scheme,
|
||||
base::StringPiece host,
|
||||
uint16_t port) {
|
||||
SchemeHostPort tuple(scheme.as_string(), host.as_string(), port,
|
||||
SchemeHostPort::CHECK_CANONICALIZATION);
|
||||
if (tuple.IsInvalid())
|
||||
return Origin();
|
||||
return base::nullopt;
|
||||
return Origin(std::move(tuple));
|
||||
}
|
||||
|
||||
// static
|
||||
base::Optional<Origin> Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
base::StringPiece precursor_scheme,
|
||||
base::StringPiece precursor_host,
|
||||
uint16_t precursor_port,
|
||||
const Origin::Nonce& nonce) {
|
||||
SchemeHostPort precursor(precursor_scheme.as_string(),
|
||||
precursor_host.as_string(), precursor_port,
|
||||
SchemeHostPort::CHECK_CANONICALIZATION);
|
||||
// For opaque origins, it is okay for the SchemeHostPort to be invalid;
|
||||
// however, this should only arise when the arguments indicate the
|
||||
// canonical representation of the invalid SchemeHostPort.
|
||||
if (precursor.IsInvalid() &&
|
||||
!(precursor_scheme.empty() && precursor_host.empty() &&
|
||||
precursor_port == 0)) {
|
||||
return base::nullopt;
|
||||
}
|
||||
return Origin(std::move(nonce), std::move(precursor));
|
||||
}
|
||||
|
||||
// static
|
||||
Origin Origin::CreateFromNormalizedTuple(std::string scheme,
|
||||
std::string host,
|
||||
uint16_t port) {
|
||||
@ -108,6 +108,19 @@ Origin Origin::CreateFromNormalizedTuple(std::string scheme,
|
||||
return Origin(std::move(tuple));
|
||||
}
|
||||
|
||||
// static
|
||||
Origin Origin::CreateOpaqueFromNormalizedPrecursorTuple(
|
||||
std::string precursor_scheme,
|
||||
std::string precursor_host,
|
||||
uint16_t precursor_port,
|
||||
const Origin::Nonce& nonce) {
|
||||
SchemeHostPort precursor(std::move(precursor_scheme),
|
||||
std::move(precursor_host), precursor_port,
|
||||
SchemeHostPort::ALREADY_CANONICALIZED);
|
||||
// For opaque origins, it is okay for the SchemeHostPort to be invalid.
|
||||
return Origin(std::move(nonce), std::move(precursor));
|
||||
}
|
||||
|
||||
std::string Origin::Serialize() const {
|
||||
if (unique())
|
||||
return "null";
|
||||
@ -128,9 +141,17 @@ GURL Origin::GetURL() const {
|
||||
return tuple_.GetURL();
|
||||
}
|
||||
|
||||
base::Optional<base::UnguessableToken> Origin::GetNonceForSerialization()
|
||||
const {
|
||||
// TODO(nasko): Consider not making a copy here, but return a reference to
|
||||
// the nonce.
|
||||
return nonce_ ? base::make_optional(nonce_->token()) : base::nullopt;
|
||||
}
|
||||
|
||||
bool Origin::IsSameOriginWith(const Origin& other) const {
|
||||
return tuple_.Equals(other.tuple_) &&
|
||||
(!unique() || (nonce_ && nonce_ == other.nonce_));
|
||||
// scheme/host/port must match, even for opaque origins where |tuple_| holds
|
||||
// the precursor origin.
|
||||
return std::tie(tuple_, nonce_) == std::tie(other.tuple_, other.nonce_);
|
||||
}
|
||||
|
||||
bool Origin::DomainIs(base::StringPiece canonical_domain) const {
|
||||
@ -141,22 +162,109 @@ bool Origin::operator<(const Origin& other) const {
|
||||
return std::tie(tuple_, nonce_) < std::tie(other.tuple_, other.nonce_);
|
||||
}
|
||||
|
||||
Origin Origin::CreateUniqueOpaque() {
|
||||
return Origin(ConstructAsOpaque::kTag);
|
||||
Origin Origin::DeriveNewOpaqueOrigin() const {
|
||||
return Origin(Nonce(), tuple_);
|
||||
}
|
||||
|
||||
Origin::Origin(ConstructAsOpaque) : nonce_(base::UnguessableToken::Create()) {}
|
||||
|
||||
Origin::Origin(SchemeHostPort tuple) : tuple_(std::move(tuple)) {
|
||||
DCHECK(!unique());
|
||||
DCHECK(!tuple_.IsInvalid());
|
||||
}
|
||||
|
||||
// Constructs an opaque origin derived from |precursor|.
|
||||
Origin::Origin(const Nonce& nonce, SchemeHostPort precursor)
|
||||
: tuple_(std::move(precursor)), nonce_(std::move(nonce)) {
|
||||
DCHECK(unique());
|
||||
// |precursor| is retained, but not accessible via scheme()/host()/port().
|
||||
DCHECK_EQ("", scheme());
|
||||
DCHECK_EQ("", host());
|
||||
DCHECK_EQ(0U, port());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const url::Origin& origin) {
|
||||
return out << origin.Serialize();
|
||||
out << origin.Serialize();
|
||||
|
||||
if (origin.unique()) {
|
||||
// For opaque origins, log the nonce and precursor as well. Without this,
|
||||
// EXPECT_EQ failures between opaque origins are nearly impossible to
|
||||
// understand.
|
||||
out << " [internally: " << *origin.nonce_;
|
||||
if (origin.tuple_.IsInvalid())
|
||||
out << " anonymous";
|
||||
else
|
||||
out << " derived from " << origin.tuple_;
|
||||
out << "]";
|
||||
} else if (origin.scheme() == kFileScheme) {
|
||||
out << " [internally: " << origin.tuple_ << "]";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const url::Origin::Nonce& nonce) {
|
||||
// Subtle: don't let logging trigger lazy-generation of the token value.
|
||||
if (nonce.raw_token().is_empty())
|
||||
return (out << "(nonce TBD)");
|
||||
else
|
||||
return (out << nonce.raw_token());
|
||||
}
|
||||
|
||||
bool IsSameOriginWith(const GURL& a, const GURL& b) {
|
||||
return Origin::Create(a).IsSameOriginWith(Origin::Create(b));
|
||||
}
|
||||
|
||||
Origin::Nonce::Nonce() {}
|
||||
Origin::Nonce::Nonce(const base::UnguessableToken& token) : token_(token) {
|
||||
CHECK(!token_.is_empty());
|
||||
}
|
||||
|
||||
const base::UnguessableToken& Origin::Nonce::token() const {
|
||||
// Inspecting the value of a nonce triggers lazy-generation.
|
||||
// TODO(dcheng): UnguessableToken::is_empty should go away -- what sentinel
|
||||
// value to use instead?
|
||||
if (token_.is_empty())
|
||||
token_ = base::UnguessableToken::Create();
|
||||
return token_;
|
||||
}
|
||||
|
||||
const base::UnguessableToken& Origin::Nonce::raw_token() const {
|
||||
return token_;
|
||||
}
|
||||
|
||||
// Copying a Nonce triggers lazy-generation of the token.
|
||||
Origin::Nonce::Nonce(const Origin::Nonce& other) : token_(other.token()) {}
|
||||
|
||||
Origin::Nonce& Origin::Nonce::operator=(const Origin::Nonce& other) {
|
||||
// Copying a Nonce triggers lazy-generation of the token.
|
||||
token_ = other.token();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Moving a nonce does NOT trigger lazy-generation of the token.
|
||||
Origin::Nonce::Nonce(Origin::Nonce&& other) : token_(other.token_) {
|
||||
other.token_ = base::UnguessableToken(); // Reset |other|.
|
||||
}
|
||||
|
||||
Origin::Nonce& Origin::Nonce::operator=(Origin::Nonce&& other) {
|
||||
token_ = other.token_;
|
||||
other.token_ = base::UnguessableToken(); // Reset |other|.
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Origin::Nonce::operator<(const Origin::Nonce& other) const {
|
||||
// When comparing, lazy-generation is required of both tokens, so that an
|
||||
// ordering is established.
|
||||
return token() < other.token();
|
||||
}
|
||||
|
||||
bool Origin::Nonce::operator==(const Origin::Nonce& other) const {
|
||||
// Equality testing doesn't actually require that the tokens be generated.
|
||||
// If the tokens are both zero, equality only holds if they're the same
|
||||
// object.
|
||||
return (other.token_ == token_) && !(token_.is_empty() && (&other != this));
|
||||
}
|
||||
|
||||
bool Origin::Nonce::operator!=(const Origin::Nonce& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
} // namespace url
|
||||
|
210
url/origin.h
210
url/origin.h
@ -23,12 +23,18 @@
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace ipc_fuzzer {
|
||||
template <class T>
|
||||
struct FuzzTraits;
|
||||
} // namespace ipc_fuzzer
|
||||
|
||||
namespace url {
|
||||
|
||||
// Per https://html.spec.whatwg.org/multipage/origin.html#origin, an origin is
|
||||
// either:
|
||||
// - a tuple origin of (scheme, host, port) as described in RFC 6454.
|
||||
// - an opaque origin with an internal value
|
||||
// - an opaque origin with an internal value, and a memory of the tuple origin
|
||||
// from which it was derived.
|
||||
//
|
||||
// TL;DR: If you need to make a security-relevant decision, use 'url::Origin'.
|
||||
// If you only need to extract the bits of a URL which are relevant for a
|
||||
@ -47,14 +53,13 @@ namespace url {
|
||||
// between contexts. Two tuple origins are same-origin if the tuples are equal.
|
||||
// A tuple origin may also be re-created from its serialization.
|
||||
//
|
||||
// An opaque origin is cross-origin to any origin, including itself and copies
|
||||
// of itself. Unlike tuple origins, an opaque origin cannot be re-created from
|
||||
// its serialization, which is always the string "null".
|
||||
//
|
||||
// TODO(https://crbug.com/768460): work is in progress to associate an internal
|
||||
// globally unique identifier with an opaque origin: completing this work will
|
||||
// allow a copy of an opaque origin to be same-origin to the original instance
|
||||
// of that opaque origin.
|
||||
// An opaque origin has an internal globally unique identifier. When creating a
|
||||
// new opaque origin from a URL, a fresh globally unique identifier is
|
||||
// generated. However, if an opaque origin is copied or moved, the internal
|
||||
// globally unique identifier is preserved. Two opaque origins are same-origin
|
||||
// iff the globally unique identifiers match. Unlike tuple origins, an opaque
|
||||
// origin cannot be re-created from its serialization, which is always the
|
||||
// string "null".
|
||||
//
|
||||
// IMPORTANT: Since opaque origins always serialize as the string "null", it is
|
||||
// *never* safe to use the serialization for security checks!
|
||||
@ -63,9 +68,7 @@ namespace url {
|
||||
//
|
||||
// There are a few subtleties to note:
|
||||
//
|
||||
// * A default constructed Origin is opaque, but unlike the spec definition, has
|
||||
// no associated identifier. A default constructed Origin is cross-origin to
|
||||
// every other Origin object.
|
||||
// * A default constructed Origin is opaque, with no precursor origin.
|
||||
//
|
||||
// * Invalid and non-standard GURLs are parsed as opaque origins. This includes
|
||||
// non-hierarchical URLs like 'data:text/html,...' and 'javascript:alert(1)'.
|
||||
@ -101,8 +104,8 @@ namespace url {
|
||||
// }
|
||||
class URL_EXPORT Origin {
|
||||
public:
|
||||
// Creates an opaque and always unique Origin. The returned Origin is
|
||||
// always cross-origin to any Origin, including itself.
|
||||
// Creates an opaque Origin with a nonce that is different from all previously
|
||||
// existing origins.
|
||||
Origin();
|
||||
|
||||
// Creates an Origin from |url|, as described at
|
||||
@ -112,11 +115,21 @@ class URL_EXPORT Origin {
|
||||
// 2. 'filesystem' URLs behave as 'blob' URLs (that is, the origin is parsed
|
||||
// out of everything in the URL which follows the scheme).
|
||||
// 3. 'file' URLs all parse as ("file", "", 0).
|
||||
//
|
||||
// If this method returns an opaque Origin, the returned Origin will be
|
||||
// cross-origin to any Origin, including itself.
|
||||
static Origin Create(const GURL& url);
|
||||
|
||||
// Creates an Origin for the resource |url| as if it were requested
|
||||
// from the context of |base_origin|. If |url| is standard
|
||||
// (in the sense that it embeds a complete origin, like http/https),
|
||||
// this returns the same value as would Create().
|
||||
//
|
||||
// If |url| is "about:blank", this returns a copy of |base_origin|.
|
||||
//
|
||||
// Otherwise, returns a new opaque origin derived from |base_origin|.
|
||||
// In this case, the resulting opaque origin will inherit the tuple
|
||||
// (or precursor tuple) of |base_origin|, but will not be same origin
|
||||
// with |base_origin|, even if |base_origin| is already opaque.
|
||||
static Origin Resolve(const GURL& url, const Origin& base_origin);
|
||||
|
||||
// Copyable and movable.
|
||||
Origin(const Origin&);
|
||||
Origin& operator=(const Origin&);
|
||||
@ -124,14 +137,14 @@ class URL_EXPORT Origin {
|
||||
Origin& operator=(Origin&&);
|
||||
|
||||
// Creates an Origin from a |scheme|, |host|, and |port|. All the parameters
|
||||
// must be valid and canonicalized. Do not use this method to create opaque
|
||||
// origins. Use Origin() or Origin::CreateOpaque() for that.
|
||||
// must be valid and canonicalized. Returns nullopt if any parameter is not
|
||||
// canonical, or if all the parameters are empty.
|
||||
//
|
||||
// This constructor should be used in order to pass 'Origin' objects back and
|
||||
// forth over IPC (as transitioning through GURL would risk potentially
|
||||
// dangerous recanonicalization); other potential callers should prefer the
|
||||
// 'GURL'-based constructor.
|
||||
static Origin UnsafelyCreateOriginWithoutNormalization(
|
||||
static base::Optional<Origin> UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
base::StringPiece scheme,
|
||||
base::StringPiece host,
|
||||
uint16_t port);
|
||||
@ -156,17 +169,41 @@ class URL_EXPORT Origin {
|
||||
uint16_t port() const { return !unique() ? tuple_.port() : 0; }
|
||||
|
||||
// TODO(dcheng): Rename this to opaque().
|
||||
bool unique() const { return tuple_.IsInvalid(); }
|
||||
bool unique() const { return nonce_.has_value(); }
|
||||
|
||||
// An ASCII serialization of the Origin as per Section 6.2 of RFC 6454, with
|
||||
// the addition that all Origins with a 'file' scheme serialize to "file://".
|
||||
std::string Serialize() const;
|
||||
|
||||
// Two Origins are "same-origin" if their schemes, hosts, and ports are exact
|
||||
// matches; and neither is unique.
|
||||
// Two non-opaque Origins are "same-origin" if their schemes, hosts, and ports
|
||||
// are exact matches. Two opaque origins are same-origin only if their
|
||||
// internal nonce values match. A non-opaque origin is never same-origin with
|
||||
// an opaque origin.
|
||||
bool IsSameOriginWith(const Origin& other) const;
|
||||
bool operator==(const Origin& other) const {
|
||||
return IsSameOriginWith(other);
|
||||
bool operator==(const Origin& other) const { return IsSameOriginWith(other); }
|
||||
bool operator!=(const Origin& other) const {
|
||||
return !IsSameOriginWith(other);
|
||||
}
|
||||
|
||||
// Get the scheme, host, and port from which this origin derives. For
|
||||
// a tuple Origin, this gives the same values as calling scheme(), host()
|
||||
// and port(). For an opaque Origin that was created by calling
|
||||
// Origin::DeriveNewOpaqueOrigin() on a precursor or Origin::Resolve(),
|
||||
// this returns the tuple inherited from the precursor.
|
||||
//
|
||||
// If this Origin is opaque and was created via the default constructor or
|
||||
// Origin::Create(), the precursor origin is unknown.
|
||||
//
|
||||
// Use with great caution: opaque origins should generally not inherit
|
||||
// privileges from the origins they derive from. However, in some cases
|
||||
// (such as restrictions on process placement, or determining the http lock
|
||||
// icon) this information may be relevant to ensure that entering an
|
||||
// opaque origin does not grant privileges initially denied to the original
|
||||
// non-opaque origin.
|
||||
//
|
||||
// This method has a deliberately obnoxious name to prompt caution in its use.
|
||||
const SchemeHostPort& GetTupleOrPrecursorTupleIfOpaque() const {
|
||||
return tuple_;
|
||||
}
|
||||
|
||||
// Efficiently returns what GURL(Serialize()) would without re-parsing the
|
||||
@ -188,16 +225,14 @@ class URL_EXPORT Origin {
|
||||
// std::map).
|
||||
bool operator<(const Origin& other) const;
|
||||
|
||||
private:
|
||||
friend class OriginTest;
|
||||
|
||||
// Creates a new opaque origin that is guaranteed to be cross-origin to all
|
||||
// currently existing origins. An origin created by this method retains its
|
||||
// identity across copies. Copies are guaranteed to be same-origin to each
|
||||
// other, e.g.
|
||||
//
|
||||
// url::Origin a = Origin::CreateUniqueOpaque();
|
||||
// url::Origin b = Origin::CreateUniqueOpaque();
|
||||
// url::Origin page = Origin::Create(GURL("http://example.com"))
|
||||
// url::Origin a = page.DeriveNewOpaqueOrigin();
|
||||
// url::Origin b = page.DeriveNewOpaqueOrigin();
|
||||
// url::Origin c = a;
|
||||
// url::Origin d = b;
|
||||
//
|
||||
@ -206,35 +241,116 @@ class URL_EXPORT Origin {
|
||||
// of origins are considered cross-origin, e.g. |a| is cross-origin to |b| and
|
||||
// |d|, |b| is cross-origin to |a| and |c|, |c| is cross-origin to |b| and
|
||||
// |d|, and |d| is cross-origin to |a| and |c|.
|
||||
Origin DeriveNewOpaqueOrigin() const;
|
||||
|
||||
private:
|
||||
friend class OriginTest;
|
||||
friend struct ipc_fuzzer::FuzzTraits<Origin>;
|
||||
friend URL_EXPORT std::ostream& operator<<(std::ostream& out,
|
||||
const Origin& origin);
|
||||
|
||||
// Origin::Nonce is a wrapper around base::UnguessableToken that generates
|
||||
// the random value only when the value is first accessed. The lazy generation
|
||||
// allows Origin to be default-constructed quickly, without spending time
|
||||
// in random number generation.
|
||||
//
|
||||
// Note that this is private internal helper, since relatively few locations
|
||||
// should be responsible for deriving a canonical origin from a GURL.
|
||||
static Origin CreateUniqueOpaque();
|
||||
// TODO(nick): Should this optimization move into UnguessableToken, once it no
|
||||
// longer treats the Null case specially?
|
||||
class URL_EXPORT Nonce {
|
||||
public:
|
||||
// Creates a nonce to hold a newly-generated UnguessableToken. The actual
|
||||
// token value will be generated lazily.
|
||||
Nonce();
|
||||
|
||||
// Similar to Create(const GURL&). However, if the returned Origin is an
|
||||
// opaque origin, it will be created with CreateUniqueOpaque(), have an
|
||||
// associated identity, and be considered same-origin to copies of itself.
|
||||
static Origin CreateCanonical(const GURL&);
|
||||
// Creates a nonce to hold an already-generated UnguessableToken value. This
|
||||
// constructor should only be used for IPC serialization and testing --
|
||||
// regular code should never need to touch the UnguessableTokens directly,
|
||||
// and the default constructor is faster.
|
||||
explicit Nonce(const base::UnguessableToken& token);
|
||||
|
||||
enum class ConstructAsOpaque { kTag };
|
||||
explicit Origin(ConstructAsOpaque);
|
||||
// Accessor, which lazily initializes the underlying |token_| member.
|
||||
const base::UnguessableToken& token() const;
|
||||
|
||||
// |tuple| must be valid, implying that the created Origin is never an opaque
|
||||
// origin.
|
||||
// Do not use in cases where lazy initialization is expected! This
|
||||
// accessor does not initialize the |token_| member.
|
||||
const base::UnguessableToken& raw_token() const;
|
||||
|
||||
// Copyable and movable. Copying a Nonce triggers lazy-initialization,
|
||||
// moving it does not.
|
||||
Nonce(const Nonce&);
|
||||
Nonce& operator=(const Nonce&);
|
||||
Nonce(Nonce&&);
|
||||
Nonce& operator=(Nonce&&);
|
||||
|
||||
// Note that operator<, used by maps type containers, will trigger |token_|
|
||||
// lazy-initialization. Equality comparisons do not.
|
||||
bool operator<(const Nonce& other) const;
|
||||
bool operator==(const Nonce& other) const;
|
||||
bool operator!=(const Nonce& other) const;
|
||||
|
||||
private:
|
||||
friend class OriginTest;
|
||||
|
||||
// mutable to support lazy generation.
|
||||
mutable base::UnguessableToken token_;
|
||||
};
|
||||
|
||||
// This needs to be friended within Origin as well, since Nonce is a private
|
||||
// nested class of Origin.
|
||||
friend URL_EXPORT std::ostream& operator<<(std::ostream& out,
|
||||
const Nonce& nonce);
|
||||
|
||||
// Creates an origin without sanity checking that the host is canonicalized.
|
||||
// This should only be used when converting between already normalized types,
|
||||
// and should NOT be used for IPC. Method takes std::strings for use with move
|
||||
// operators to avoid copies.
|
||||
static Origin CreateOpaqueFromNormalizedPrecursorTuple(
|
||||
std::string precursor_scheme,
|
||||
std::string precursor_host,
|
||||
uint16_t precursor_port,
|
||||
const Nonce& nonce);
|
||||
|
||||
// Creates an opaque Origin with the identity given by |nonce|, and an
|
||||
// optional precursor origin given by |precursor_scheme|, |precursor_host| and
|
||||
// |precursor_port|. Returns nullopt if any parameter is not canonical. When
|
||||
// the precursor is unknown, the precursor parameters should be ("", "", 0).
|
||||
//
|
||||
// This factory method should be used in order to pass opaque Origin objects
|
||||
// back and forth over IPC (as transitioning through GURL would risk
|
||||
// potentially dangerous recanonicalization).
|
||||
static base::Optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
base::StringPiece precursor_scheme,
|
||||
base::StringPiece precursor_host,
|
||||
uint16_t precursor_port,
|
||||
const Nonce& nonce);
|
||||
|
||||
// Constructs a non-opaque tuple origin. |tuple| must be valid.
|
||||
explicit Origin(SchemeHostPort tuple);
|
||||
|
||||
// Helpers for managing union for destroy, copy, and move.
|
||||
// The tuple is used for tuple origins (e.g. https://example.com:80). This
|
||||
// is expected to be the common case. |IsInvalid()| will be true for opaque
|
||||
// origins.
|
||||
// Constructs an opaque origin derived from the |precursor| tuple, with the
|
||||
// given |nonce|.
|
||||
Origin(const Nonce& nonce, SchemeHostPort precursor);
|
||||
|
||||
// Get the nonce associated with this origin, if it is unique. This should be
|
||||
// used only when trying to send an Origin across an IPC pipe.
|
||||
base::Optional<base::UnguessableToken> GetNonceForSerialization() const;
|
||||
|
||||
// The tuple is used for both tuple origins (e.g. https://example.com:80), as
|
||||
// well as for opaque origins, where it tracks the tuple origin from which
|
||||
// the opaque origin was initially derived (we call this the "precursor"
|
||||
// origin).
|
||||
SchemeHostPort tuple_;
|
||||
|
||||
// The nonce is used for maintaining identity of an opaque origin. This
|
||||
// nonce is preserved when an opaque origin is copied or moved.
|
||||
base::Optional<base::UnguessableToken> nonce_;
|
||||
// nonce is preserved when an opaque origin is copied or moved. An Origin
|
||||
// is considered opaque if and only if |nonce_| holds a value.
|
||||
base::Optional<Nonce> nonce_;
|
||||
};
|
||||
|
||||
// Pretty-printers for logging. These expose the internal state of the nonce.
|
||||
URL_EXPORT std::ostream& operator<<(std::ostream& out, const Origin& origin);
|
||||
URL_EXPORT std::ostream& operator<<(std::ostream& out,
|
||||
const Origin::Nonce& origin);
|
||||
|
||||
URL_EXPORT bool IsSameOriginWith(const GURL& a, const GURL& b);
|
||||
|
||||
|
@ -37,7 +37,7 @@ void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) {
|
||||
}
|
||||
|
||||
class OriginTest : public ::testing::Test {
|
||||
protected:
|
||||
public:
|
||||
void SetUp() override {
|
||||
// Add two schemes which are local but nonstandard.
|
||||
AddLocalScheme("local-but-nonstandard");
|
||||
@ -56,34 +56,130 @@ class OriginTest : public ::testing::Test {
|
||||
}
|
||||
void TearDown() override { url::Shutdown(); }
|
||||
|
||||
Origin CreateUniqueOpaque() { return Origin::CreateUniqueOpaque(); }
|
||||
::testing::AssertionResult DoEqualityComparisons(const url::Origin& a,
|
||||
const url::Origin& b,
|
||||
bool should_compare_equal) {
|
||||
::testing::AssertionResult failure = ::testing::AssertionFailure();
|
||||
failure << "DoEqualityComparisons failure. Expecting "
|
||||
<< (should_compare_equal ? "equality" : "inequality")
|
||||
<< " between:\n a\n Which is: " << a
|
||||
<< "\n b\n Which is: " << b << "\nThe following check failed: ";
|
||||
if (a.IsSameOriginWith(b) != should_compare_equal)
|
||||
return failure << "a.IsSameOriginWith(b)";
|
||||
if (b.IsSameOriginWith(a) != should_compare_equal)
|
||||
return failure << "b.IsSameOriginWith(a)";
|
||||
if ((a == b) != should_compare_equal)
|
||||
return failure << "(a == b)";
|
||||
if ((b == a) != should_compare_equal)
|
||||
return failure << "(b == a)";
|
||||
if ((b != a) != !should_compare_equal)
|
||||
return failure << "(b != a)";
|
||||
if ((a != b) != !should_compare_equal)
|
||||
return failure << "(a != b)";
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
Origin CreateCanonical(const GURL& url) {
|
||||
return Origin::CreateCanonical(url);
|
||||
bool HasNonceTokenBeenInitialized(const url::Origin& origin) {
|
||||
EXPECT_TRUE(origin.unique());
|
||||
// Avoid calling nonce_.token() here, to not trigger lazy initialization.
|
||||
return !origin.nonce_->token_.is_empty();
|
||||
}
|
||||
|
||||
Origin::Nonce CreateNonce() { return Origin::Nonce(); }
|
||||
|
||||
Origin::Nonce CreateNonce(base::UnguessableToken nonce) {
|
||||
return Origin::Nonce(nonce);
|
||||
}
|
||||
|
||||
base::Optional<base::UnguessableToken> GetNonce(const Origin& origin) {
|
||||
return origin.GetNonceForSerialization();
|
||||
}
|
||||
|
||||
// Wrapper around url::Origin method to expose it to tests.
|
||||
base::Optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
base::StringPiece precursor_scheme,
|
||||
base::StringPiece precursor_host,
|
||||
uint16_t precursor_port,
|
||||
const Origin::Nonce& nonce) {
|
||||
return Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
precursor_scheme, precursor_host, precursor_port, nonce);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(OriginTest, OpaqueOriginComparison) {
|
||||
// A default constructed Origin should be cross origin to everything,
|
||||
// including itself.
|
||||
Origin unique_origin;
|
||||
EXPECT_EQ("", unique_origin.scheme());
|
||||
EXPECT_EQ("", unique_origin.host());
|
||||
EXPECT_EQ(0, unique_origin.port());
|
||||
EXPECT_TRUE(unique_origin.unique());
|
||||
EXPECT_FALSE(unique_origin.IsSameOriginWith(unique_origin));
|
||||
// A default-constructed Origin should should be cross origin to everything
|
||||
// but itself.
|
||||
url::Origin opaque_a, opaque_b;
|
||||
EXPECT_TRUE(opaque_a.unique());
|
||||
EXPECT_EQ("", opaque_a.scheme());
|
||||
EXPECT_EQ("", opaque_a.host());
|
||||
EXPECT_EQ(0, opaque_a.port());
|
||||
EXPECT_EQ(SchemeHostPort(), opaque_a.GetTupleOrPrecursorTupleIfOpaque());
|
||||
EXPECT_TRUE(opaque_a.GetTupleOrPrecursorTupleIfOpaque().IsInvalid());
|
||||
|
||||
// An opaque Origin with a nonce should be same origin to itself though.
|
||||
Origin opaque_origin = CreateUniqueOpaque();
|
||||
EXPECT_EQ("", opaque_origin.scheme());
|
||||
EXPECT_EQ("", opaque_origin.host());
|
||||
EXPECT_EQ(0, opaque_origin.port());
|
||||
EXPECT_TRUE(opaque_origin.unique());
|
||||
EXPECT_TRUE(opaque_origin.IsSameOriginWith(opaque_origin));
|
||||
EXPECT_TRUE(opaque_b.unique());
|
||||
EXPECT_EQ("", opaque_b.scheme());
|
||||
EXPECT_EQ("", opaque_b.host());
|
||||
EXPECT_EQ(0, opaque_b.port());
|
||||
EXPECT_EQ(SchemeHostPort(), opaque_b.GetTupleOrPrecursorTupleIfOpaque());
|
||||
EXPECT_TRUE(opaque_b.GetTupleOrPrecursorTupleIfOpaque().IsInvalid());
|
||||
|
||||
// The default constructed Origin and the opaque Origin should always be
|
||||
// cross origin to each other.
|
||||
EXPECT_FALSE(opaque_origin.IsSameOriginWith(unique_origin));
|
||||
// Two default-constructed Origins should always be cross origin to each
|
||||
// other.
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
|
||||
|
||||
// The streaming operator should not trigger lazy initialization to the token.
|
||||
std::ostringstream stream;
|
||||
stream << opaque_a;
|
||||
EXPECT_STREQ("null [internally: (nonce TBD) anonymous]",
|
||||
stream.str().c_str());
|
||||
EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
|
||||
|
||||
// None of the operations thus far should have triggered lazy-generation of
|
||||
// the UnguessableToken. Copying an origin, however, should trigger this.
|
||||
EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
|
||||
EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_b));
|
||||
opaque_b = opaque_a;
|
||||
|
||||
EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a));
|
||||
EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, true));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
|
||||
|
||||
// Move-initializing to a fresh Origin should restore the lazy initialization.
|
||||
opaque_a = url::Origin();
|
||||
EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
|
||||
EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
|
||||
|
||||
// Comparing two opaque Origins with matching SchemeHostPorts should trigger
|
||||
// lazy initialization.
|
||||
EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
|
||||
EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
|
||||
bool should_swap = opaque_b < opaque_a;
|
||||
EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a));
|
||||
EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
|
||||
|
||||
if (should_swap)
|
||||
std::swap(opaque_a, opaque_b);
|
||||
EXPECT_LT(opaque_a, opaque_b);
|
||||
EXPECT_FALSE(opaque_b < opaque_a);
|
||||
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
|
||||
EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
|
||||
|
||||
EXPECT_LT(opaque_a, url::Origin::Create(GURL("http://www.google.com")));
|
||||
EXPECT_LT(opaque_b, url::Origin::Create(GURL("http://www.google.com")));
|
||||
|
||||
EXPECT_EQ(opaque_b, url::Origin::Resolve(GURL("about:blank"), opaque_b));
|
||||
EXPECT_EQ(opaque_b,
|
||||
url::Origin::Resolve(GURL("about:blank?hello#whee"), opaque_b));
|
||||
|
||||
const char* const urls[] = {
|
||||
"data:text/html,Hello!",
|
||||
@ -118,55 +214,30 @@ TEST_F(OriginTest, OpaqueOriginComparison) {
|
||||
for (auto* test_url : urls) {
|
||||
SCOPED_TRACE(test_url);
|
||||
GURL url(test_url);
|
||||
const url::Origin opaque_origin;
|
||||
|
||||
// no nonce mode of opaque origins
|
||||
// Opaque origins returned by Origin::Create().
|
||||
{
|
||||
Origin origin = Origin::Create(url);
|
||||
EXPECT_EQ("", origin.scheme());
|
||||
EXPECT_EQ("", origin.host());
|
||||
EXPECT_EQ(0, origin.port());
|
||||
EXPECT_TRUE(origin.unique());
|
||||
// An opaque Origin with no nonce is always cross-origin to itself.
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(origin));
|
||||
// A copy of |origin| should be cross-origin as well.
|
||||
Origin origin_copy = origin;
|
||||
EXPECT_EQ("", origin_copy.scheme());
|
||||
EXPECT_EQ("", origin_copy.host());
|
||||
EXPECT_EQ(0, origin_copy.port());
|
||||
EXPECT_TRUE(origin_copy.unique());
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(origin_copy));
|
||||
// And it should always be cross-origin to another opaque Origin.
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(opaque_origin));
|
||||
// As well as the default constructed Origin.
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(unique_origin));
|
||||
// Re-creating from the URL should also be cross-origin.
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(Origin::Create(url)));
|
||||
|
||||
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
|
||||
}
|
||||
|
||||
// opaque origins with a nonce
|
||||
{
|
||||
Origin origin = CreateCanonical(url);
|
||||
EXPECT_EQ("", origin.scheme());
|
||||
EXPECT_EQ("", origin.host());
|
||||
EXPECT_EQ(0, origin.port());
|
||||
EXPECT_TRUE(origin.unique());
|
||||
// An opaque Origin with a nonce is always same-origin to itself.
|
||||
EXPECT_TRUE(origin.IsSameOriginWith(origin));
|
||||
// An origin is always same-origin with itself.
|
||||
EXPECT_EQ(origin, origin);
|
||||
EXPECT_NE(origin, url::Origin());
|
||||
EXPECT_EQ(SchemeHostPort(), origin.GetTupleOrPrecursorTupleIfOpaque());
|
||||
// A copy of |origin| should be same-origin as well.
|
||||
Origin origin_copy = origin;
|
||||
EXPECT_EQ("", origin_copy.scheme());
|
||||
EXPECT_EQ("", origin_copy.host());
|
||||
EXPECT_EQ(0, origin_copy.port());
|
||||
EXPECT_TRUE(origin_copy.unique());
|
||||
EXPECT_TRUE(origin.IsSameOriginWith(origin_copy));
|
||||
// But it should always be cross origin to another opaque Origin.
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(opaque_origin));
|
||||
// As well as the default constructed Origin.
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(unique_origin));
|
||||
// Re-creating from the URL should also be cross origin.
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(CreateCanonical(url)));
|
||||
EXPECT_EQ(origin, origin_copy);
|
||||
// And it should always be cross-origin to another opaque Origin.
|
||||
EXPECT_NE(origin, opaque_origin);
|
||||
// Re-creating from the URL should also be cross-origin.
|
||||
EXPECT_NE(origin, Origin::Create(url));
|
||||
|
||||
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
|
||||
}
|
||||
@ -292,11 +363,36 @@ TEST_F(OriginTest, ConstructFromGURL) {
|
||||
EXPECT_EQ(test_case.expected_host, origin.host());
|
||||
EXPECT_EQ(test_case.expected_port, origin.port());
|
||||
EXPECT_FALSE(origin.unique());
|
||||
EXPECT_TRUE(origin.IsSameOriginWith(origin));
|
||||
EXPECT_FALSE(different_origin.IsSameOriginWith(origin));
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(different_origin));
|
||||
EXPECT_EQ(origin, origin);
|
||||
EXPECT_NE(different_origin, origin);
|
||||
EXPECT_NE(origin, different_origin);
|
||||
EXPECT_EQ(origin, Origin::Resolve(GURL("about:blank"), origin));
|
||||
EXPECT_EQ(origin, Origin::Resolve(GURL("about:blank?bar#foo"), origin));
|
||||
|
||||
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
|
||||
|
||||
url::Origin derived_opaque =
|
||||
Origin::Resolve(GURL("about:blank?bar#foo"), origin)
|
||||
.DeriveNewOpaqueOrigin();
|
||||
EXPECT_TRUE(derived_opaque.unique());
|
||||
EXPECT_NE(origin, derived_opaque);
|
||||
EXPECT_FALSE(derived_opaque.GetTupleOrPrecursorTupleIfOpaque().IsInvalid());
|
||||
EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(),
|
||||
derived_opaque.GetTupleOrPrecursorTupleIfOpaque());
|
||||
EXPECT_EQ(derived_opaque, derived_opaque);
|
||||
|
||||
url::Origin derived_opaque_via_data_url =
|
||||
Origin::Resolve(GURL("data:text/html,baz"), origin);
|
||||
EXPECT_TRUE(derived_opaque_via_data_url.unique());
|
||||
EXPECT_NE(origin, derived_opaque_via_data_url);
|
||||
EXPECT_FALSE(derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque()
|
||||
.IsInvalid());
|
||||
EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(),
|
||||
derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque());
|
||||
EXPECT_NE(derived_opaque, derived_opaque_via_data_url);
|
||||
EXPECT_NE(derived_opaque_via_data_url, derived_opaque);
|
||||
EXPECT_NE(derived_opaque.DeriveNewOpaqueOrigin(), derived_opaque);
|
||||
EXPECT_EQ(derived_opaque_via_data_url, derived_opaque_via_data_url);
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,6 +400,7 @@ TEST_F(OriginTest, Serialization) {
|
||||
struct TestCases {
|
||||
const char* const url;
|
||||
const char* const expected;
|
||||
const char* const expected_log;
|
||||
} cases[] = {
|
||||
{"http://192.168.9.1/", "http://192.168.9.1"},
|
||||
{"http://[2001:db8::1]/", "http://[2001:db8::1]"},
|
||||
@ -312,8 +409,10 @@ TEST_F(OriginTest, Serialization) {
|
||||
{"http://example.com:123/", "http://example.com:123"},
|
||||
{"https://example.com/", "https://example.com"},
|
||||
{"https://example.com:123/", "https://example.com:123"},
|
||||
{"file:///etc/passwd", "file://"},
|
||||
{"file://example.com/etc/passwd", "file://"},
|
||||
{"file:///etc/passwd", "file://", "file:// [internally: file://]"},
|
||||
{"file://example.com/etc/passwd", "file://",
|
||||
"file:// [internally: file://example.com]"},
|
||||
{"data:,", "null", "null [internally: (nonce TBD) anonymous]"},
|
||||
};
|
||||
|
||||
for (const auto& test_case : cases) {
|
||||
@ -326,44 +425,23 @@ TEST_F(OriginTest, Serialization) {
|
||||
|
||||
EXPECT_EQ(test_case.expected, serialized);
|
||||
|
||||
// The '<<' operator should produce the same serialization as Serialize().
|
||||
// The '<<' operator sometimes produces additional information.
|
||||
std::stringstream out;
|
||||
out << origin;
|
||||
EXPECT_EQ(test_case.expected, out.str());
|
||||
if (test_case.expected_log)
|
||||
EXPECT_EQ(test_case.expected_log, out.str());
|
||||
else
|
||||
EXPECT_EQ(test_case.expected, out.str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OriginTest, Comparison) {
|
||||
// These URLs are arranged in increasing order:
|
||||
const char* const urls[] = {
|
||||
"data:uniqueness",
|
||||
"http://a:80",
|
||||
"http://b:80",
|
||||
"https://a:80",
|
||||
"https://b:80",
|
||||
"http://a:81",
|
||||
"http://b:81",
|
||||
"https://a:81",
|
||||
"https://b:81",
|
||||
"data:uniqueness", "http://a:80", "http://b:80",
|
||||
"https://a:80", "https://b:80", "http://a:81",
|
||||
"http://b:81", "https://a:81", "https://b:81",
|
||||
};
|
||||
|
||||
{
|
||||
// Unlike below, pre-creation here isn't necessary, since the old creation
|
||||
// path doesn't populate a nonce. It makes for easier copy and paste though.
|
||||
std::vector<Origin> origins;
|
||||
for (const auto* test_url : urls)
|
||||
origins.push_back(CreateCanonical(GURL(test_url)));
|
||||
|
||||
for (size_t i = 0; i < origins.size(); i++) {
|
||||
const Origin& current = origins[i];
|
||||
for (size_t j = i; j < origins.size(); j++) {
|
||||
const Origin& to_compare = origins[j];
|
||||
EXPECT_EQ(i < j, current < to_compare) << i << " < " << j;
|
||||
EXPECT_EQ(j < i, to_compare < current) << j << " < " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the comparison logic still works when creating a canonical origin,
|
||||
// when any created opaque origins contain a nonce.
|
||||
{
|
||||
@ -371,8 +449,7 @@ TEST_F(OriginTest, Comparison) {
|
||||
// with each freshly-constructed Origin (that's not copied).
|
||||
std::vector<Origin> origins;
|
||||
for (const auto* test_url : urls)
|
||||
origins.push_back(CreateCanonical(GURL(test_url)));
|
||||
|
||||
origins.push_back(Origin::Create(GURL(test_url)));
|
||||
for (size_t i = 0; i < origins.size(); i++) {
|
||||
const Origin& current = origins[i];
|
||||
for (size_t j = i; j < origins.size(); j++) {
|
||||
@ -399,26 +476,44 @@ TEST_F(OriginTest, UnsafelyCreate) {
|
||||
};
|
||||
|
||||
for (const auto& test : cases) {
|
||||
SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
|
||||
<< test.port);
|
||||
Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port);
|
||||
EXPECT_EQ(test.scheme, origin.scheme());
|
||||
EXPECT_EQ(test.host, origin.host());
|
||||
EXPECT_EQ(test.port, origin.port());
|
||||
EXPECT_FALSE(origin.unique());
|
||||
EXPECT_TRUE(origin.IsSameOriginWith(origin));
|
||||
SCOPED_TRACE(testing::Message()
|
||||
<< test.scheme << "://" << test.host << ":" << test.port);
|
||||
base::Optional<url::Origin> origin =
|
||||
url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port);
|
||||
ASSERT_TRUE(origin);
|
||||
EXPECT_EQ(test.scheme, origin->scheme());
|
||||
EXPECT_EQ(test.host, origin->host());
|
||||
EXPECT_EQ(test.port, origin->port());
|
||||
EXPECT_FALSE(origin->unique());
|
||||
EXPECT_TRUE(origin->IsSameOriginWith(*origin));
|
||||
|
||||
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
|
||||
ExpectParsedUrlsEqual(GURL(origin->Serialize()), origin->GetURL());
|
||||
|
||||
base::UnguessableToken nonce = base::UnguessableToken::Create();
|
||||
base::Optional<url::Origin> opaque_origin =
|
||||
UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port, CreateNonce(nonce));
|
||||
ASSERT_TRUE(opaque_origin);
|
||||
EXPECT_TRUE(opaque_origin->unique());
|
||||
EXPECT_FALSE(*opaque_origin == origin);
|
||||
EXPECT_EQ(opaque_origin->GetTupleOrPrecursorTupleIfOpaque(),
|
||||
origin->GetTupleOrPrecursorTupleIfOpaque());
|
||||
EXPECT_EQ(opaque_origin,
|
||||
UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port, CreateNonce(nonce)));
|
||||
EXPECT_FALSE(*opaque_origin == origin->DeriveNewOpaqueOrigin());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
|
||||
url::AddStandardScheme("host-only", url::SCHEME_WITH_HOST);
|
||||
url::AddStandardScheme("host-port-only", url::SCHEME_WITH_HOST_AND_PORT);
|
||||
struct TestCases {
|
||||
const char* scheme;
|
||||
const char* host;
|
||||
uint16_t port = 80;
|
||||
} cases[] = {{"", "", 0},
|
||||
} cases[] = {{"", "", 33},
|
||||
{"data", "", 0},
|
||||
{"blob", "", 0},
|
||||
{"filesystem", "", 0},
|
||||
@ -433,50 +528,61 @@ TEST_F(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
|
||||
{"http", "example.com\n"},
|
||||
{"http", "example.com\r"},
|
||||
{"http", "example.com", 0},
|
||||
{"unknown-scheme", "example.com"},
|
||||
{"host-only", "\r", 0},
|
||||
{"host-only", "example.com", 22},
|
||||
{"host-port-only", "example.com", 0},
|
||||
{"file", ""}};
|
||||
|
||||
for (const auto& test : cases) {
|
||||
SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
|
||||
<< test.port);
|
||||
Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port);
|
||||
EXPECT_EQ("", origin.scheme());
|
||||
EXPECT_EQ("", origin.host());
|
||||
EXPECT_EQ(0, origin.port());
|
||||
EXPECT_TRUE(origin.unique());
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(origin));
|
||||
|
||||
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
|
||||
SCOPED_TRACE(testing::Message()
|
||||
<< test.scheme << "://" << test.host << ":" << test.port);
|
||||
EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port, CreateNonce()));
|
||||
EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port));
|
||||
}
|
||||
|
||||
// An empty scheme/host/port tuple is not a valid tuple origin.
|
||||
EXPECT_FALSE(
|
||||
url::Origin::UnsafelyCreateTupleOriginWithoutNormalization("", "", 0));
|
||||
|
||||
// Opaque origins with unknown precursors are allowed.
|
||||
base::UnguessableToken token = base::UnguessableToken::Create();
|
||||
base::Optional<url::Origin> anonymous_opaque =
|
||||
UnsafelyCreateOpaqueOriginWithoutNormalization("", "", 0,
|
||||
CreateNonce(token));
|
||||
ASSERT_TRUE(anonymous_opaque)
|
||||
<< "An invalid tuple is a valid input to "
|
||||
<< "UnsafelyCreateOpaqueOriginWithoutNormalization, so long as it is "
|
||||
<< "the canonical form of the invalid tuple.";
|
||||
EXPECT_TRUE(anonymous_opaque->unique());
|
||||
EXPECT_EQ(GetNonce(anonymous_opaque.value()), token);
|
||||
EXPECT_EQ(anonymous_opaque->GetTupleOrPrecursorTupleIfOpaque(),
|
||||
url::SchemeHostPort());
|
||||
}
|
||||
|
||||
TEST_F(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
|
||||
struct TestCases {
|
||||
const char* scheme;
|
||||
size_t scheme_length;
|
||||
const char* host;
|
||||
size_t host_length;
|
||||
base::StringPiece scheme;
|
||||
base::StringPiece host;
|
||||
uint16_t port = 80;
|
||||
} cases[] = {{"http\0more", 9, "example.com", 11},
|
||||
{"http\0", 5, "example.com", 11},
|
||||
{"\0http", 5, "example.com", 11},
|
||||
{"http", 4, "example.com\0not-example.com", 27},
|
||||
{"http", 4, "example.com\0", 12},
|
||||
{"http", 4, "\0example.com", 12}};
|
||||
} cases[] = {{{"http\0more", 9}, {"example.com", 11}},
|
||||
{{"http\0", 5}, {"example.com", 11}},
|
||||
{{"\0http", 5}, {"example.com", 11}},
|
||||
{{"http"}, {"example.com\0not-example.com", 27}},
|
||||
{{"http"}, {"example.com\0", 12}},
|
||||
{{"http"}, {"\0example.com", 12}},
|
||||
{{""}, {"\0", 1}, 0},
|
||||
{{"\0", 1}, {""}, 0}};
|
||||
|
||||
for (const auto& test : cases) {
|
||||
SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
|
||||
<< test.port);
|
||||
Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization(
|
||||
std::string(test.scheme, test.scheme_length),
|
||||
std::string(test.host, test.host_length), test.port);
|
||||
EXPECT_EQ("", origin.scheme());
|
||||
EXPECT_EQ("", origin.host());
|
||||
EXPECT_EQ(0, origin.port());
|
||||
EXPECT_TRUE(origin.unique());
|
||||
EXPECT_FALSE(origin.IsSameOriginWith(origin));
|
||||
|
||||
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
|
||||
SCOPED_TRACE(testing::Message()
|
||||
<< test.scheme << "://" << test.host << ":" << test.port);
|
||||
EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port));
|
||||
EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization(
|
||||
test.scheme, test.host, test.port, CreateNonce()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,6 +631,8 @@ TEST_F(OriginTest, DomainIs) {
|
||||
|
||||
EXPECT_EQ(test_case.expected_domain_is,
|
||||
origin.DomainIs(test_case.lower_ascii_domain));
|
||||
EXPECT_FALSE(
|
||||
origin.DeriveNewOpaqueOrigin().DomainIs(test_case.lower_ascii_domain));
|
||||
}
|
||||
|
||||
// If the URL is invalid, DomainIs returns false.
|
||||
|
@ -209,11 +209,6 @@ GURL SchemeHostPort::GetURL() const {
|
||||
return GURL(std::move(serialized), parsed, true);
|
||||
}
|
||||
|
||||
bool SchemeHostPort::Equals(const SchemeHostPort& other) const {
|
||||
return port_ == other.port() && scheme_ == other.scheme() &&
|
||||
host_ == other.host();
|
||||
}
|
||||
|
||||
bool SchemeHostPort::operator<(const SchemeHostPort& other) const {
|
||||
return std::tie(port_, scheme_, host_) <
|
||||
std::tie(other.port_, other.scheme_, other.host_);
|
||||
@ -258,4 +253,9 @@ std::string SchemeHostPort::SerializeInternal(url::Parsed* parsed) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const SchemeHostPort& scheme_host_port) {
|
||||
return out << scheme_host_port.Serialize();
|
||||
}
|
||||
|
||||
} // namespace url
|
||||
|
@ -49,9 +49,9 @@ struct Parsed;
|
||||
// these constructs.
|
||||
//
|
||||
// * SchemeHostPort has no notion of the Origin concept (RFC 6454), and in
|
||||
// particular, it has no notion of a "unique" Origin. If you need to take
|
||||
// uniqueness into account (and, if you're making security-relevant decisions
|
||||
// then you absolutely do), please use 'url::Origin' instead.
|
||||
// particular, it has no notion of an opaque Origin. If you need to take
|
||||
// opaque origins into account (and, if you're making security-relevant
|
||||
// decisions then you absolutely do), please use 'url::Origin' instead.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
@ -71,7 +71,7 @@ struct Parsed;
|
||||
// tuple.port(); // 443
|
||||
//
|
||||
// GURL url("https://example.com/");
|
||||
// tuple.Equals(url::SchemeHostPort(url)); // true
|
||||
// tuple == url::SchemeHostPort(url); // true
|
||||
class URL_EXPORT SchemeHostPort {
|
||||
public:
|
||||
// Creates an invalid (scheme, host, port) tuple, which represents an invalid
|
||||
@ -129,7 +129,7 @@ class URL_EXPORT SchemeHostPort {
|
||||
// While this string form resembles the Origin serialization specified in
|
||||
// Section 6.2 of RFC 6454, it is important to note that invalid
|
||||
// SchemeHostPort tuples serialize to the empty string, rather than being
|
||||
// serialized as a unique Origin.
|
||||
// serialized as would an opaque Origin.
|
||||
std::string Serialize() const;
|
||||
|
||||
// Efficiently returns what GURL(Serialize()) would return, without needing to
|
||||
@ -141,9 +141,14 @@ class URL_EXPORT SchemeHostPort {
|
||||
//
|
||||
// Note that this comparison is _not_ the same as an origin-based comparison.
|
||||
// In particular, invalid SchemeHostPort objects match each other (and
|
||||
// themselves). Unique origins, on the other hand, would not.
|
||||
bool Equals(const SchemeHostPort& other) const;
|
||||
|
||||
// themselves). Opaque origins, on the other hand, would not.
|
||||
bool operator==(const SchemeHostPort& other) const {
|
||||
return port_ == other.port() && scheme_ == other.scheme() &&
|
||||
host_ == other.host();
|
||||
}
|
||||
bool operator!=(const SchemeHostPort& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
// Allows SchemeHostPort to be used as a key in STL (for example, a std::set
|
||||
// or std::map).
|
||||
bool operator<(const SchemeHostPort& other) const;
|
||||
@ -156,6 +161,9 @@ class URL_EXPORT SchemeHostPort {
|
||||
uint16_t port_;
|
||||
};
|
||||
|
||||
URL_EXPORT std::ostream& operator<<(std::ostream& out,
|
||||
const SchemeHostPort& scheme_host_port);
|
||||
|
||||
} // namespace url
|
||||
|
||||
#endif // URL_SCHEME_HOST_PORT_H_
|
||||
|
@ -53,7 +53,7 @@ TEST_F(SchemeHostPortTest, Invalid) {
|
||||
EXPECT_EQ("", invalid.host());
|
||||
EXPECT_EQ(0, invalid.port());
|
||||
EXPECT_TRUE(invalid.IsInvalid());
|
||||
EXPECT_TRUE(invalid.Equals(invalid));
|
||||
EXPECT_EQ(invalid, invalid);
|
||||
|
||||
const char* urls[] = {
|
||||
"data:text/html,Hello!", "javascript:alert(1)",
|
||||
@ -77,9 +77,9 @@ TEST_F(SchemeHostPortTest, Invalid) {
|
||||
EXPECT_EQ("", tuple.host());
|
||||
EXPECT_EQ(0, tuple.port());
|
||||
EXPECT_TRUE(tuple.IsInvalid());
|
||||
EXPECT_TRUE(tuple.Equals(tuple));
|
||||
EXPECT_TRUE(tuple.Equals(invalid));
|
||||
EXPECT_TRUE(invalid.Equals(tuple));
|
||||
EXPECT_EQ(tuple, tuple);
|
||||
EXPECT_EQ(tuple, invalid);
|
||||
EXPECT_EQ(invalid, tuple);
|
||||
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
|
||||
}
|
||||
}
|
||||
@ -106,7 +106,7 @@ TEST_F(SchemeHostPortTest, ExplicitConstruction) {
|
||||
EXPECT_EQ(test.host, tuple.host());
|
||||
EXPECT_EQ(test.port, tuple.port());
|
||||
EXPECT_FALSE(tuple.IsInvalid());
|
||||
EXPECT_TRUE(tuple.Equals(tuple));
|
||||
EXPECT_EQ(tuple, tuple);
|
||||
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
|
||||
}
|
||||
}
|
||||
@ -142,7 +142,7 @@ TEST_F(SchemeHostPortTest, InvalidConstruction) {
|
||||
EXPECT_EQ("", tuple.host());
|
||||
EXPECT_EQ(0, tuple.port());
|
||||
EXPECT_TRUE(tuple.IsInvalid());
|
||||
EXPECT_TRUE(tuple.Equals(tuple));
|
||||
EXPECT_EQ(tuple, tuple);
|
||||
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
|
||||
}
|
||||
}
|
||||
@ -206,7 +206,7 @@ TEST_F(SchemeHostPortTest, GURLConstruction) {
|
||||
EXPECT_EQ(test.host, tuple.host());
|
||||
EXPECT_EQ(test.port, tuple.port());
|
||||
EXPECT_FALSE(tuple.IsInvalid());
|
||||
EXPECT_TRUE(tuple.Equals(tuple));
|
||||
EXPECT_EQ(tuple, tuple);
|
||||
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user