Commit 62ff0189 authored by Daniel Cheng's avatar Daniel Cheng Committed by Commit Bot

Add nonce for tracking identity of unique url::Origin objects.

The nonce allows a unique origin to be same origin with itself, as well
as copies of itself. blink::SecurityOrigin and IPC are not yet updated
to propagate the necessary information through all the layers.

Bug: 768460
Change-Id: I1bfce592f3fa576dff18eb7fe2071742e82d1768
Reviewed-on: https://chromium-review.googlesource.com/745986
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Reviewed-by: default avatarNick Carter <nick@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#586129}
parent 3ce1fba2
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
namespace url { namespace url {
Origin::Origin() : unique_(true) {} Origin::Origin() {}
Origin Origin::Create(const GURL& url) { Origin Origin::Create(const GURL& url) {
if (!url.is_valid() || (!url.IsStandard() && !url.SchemeIsBlob())) if (!url.is_valid() || (!url.IsStandard() && !url.SchemeIsBlob()))
...@@ -43,15 +43,36 @@ Origin Origin::Create(const GURL& url) { ...@@ -43,15 +43,36 @@ Origin Origin::Create(const GURL& url) {
return Origin(std::move(tuple)); return Origin(std::move(tuple));
} }
Origin::Origin(SchemeHostPort tuple) // Note: this is very similar to Create(const GURL&), but opaque origins are
: tuple_(std::move(tuple)), unique_(false) { // created with CreateUniqueOpaque() rather than the default constructor.
DCHECK(!tuple_.IsInvalid()); Origin Origin::CreateCanonical(const GURL& url) {
if (!url.is_valid() || (!url.IsStandard() && !url.SchemeIsBlob()))
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);
}
if (tuple.IsInvalid())
return CreateUniqueOpaque();
return Origin(std::move(tuple));
} }
Origin::Origin(const Origin&) = default; Origin::Origin(const Origin& other) = default;
Origin& Origin::operator=(const Origin&) = default; Origin& Origin::operator=(const Origin& other) = default;
Origin::Origin(Origin&&) = default; Origin::Origin(Origin&& other) = default;
Origin& Origin::operator=(Origin&&) = default; Origin& Origin::operator=(Origin&& other) = default;
Origin::~Origin() = default; Origin::~Origin() = default;
...@@ -94,24 +115,30 @@ GURL Origin::GetURL() const { ...@@ -94,24 +115,30 @@ GURL Origin::GetURL() const {
if (scheme() == kFileScheme) if (scheme() == kFileScheme)
return GURL("file:///"); return GURL("file:///");
GURL tuple_url(tuple_.GetURL()); return tuple_.GetURL();
return tuple_url;
} }
bool Origin::IsSameOriginWith(const Origin& other) const { bool Origin::IsSameOriginWith(const Origin& other) const {
if (unique_ || other.unique_) return tuple_.Equals(other.tuple_) &&
return false; (!unique() || (nonce_ && nonce_ == other.nonce_));
return tuple_.Equals(other.tuple_);
} }
bool Origin::DomainIs(base::StringPiece canonical_domain) const { bool Origin::DomainIs(base::StringPiece canonical_domain) const {
return !unique_ && url::DomainIs(tuple_.host(), canonical_domain); return !unique() && url::DomainIs(tuple_.host(), canonical_domain);
} }
bool Origin::operator<(const Origin& other) const { bool Origin::operator<(const Origin& other) const {
return tuple_ < other.tuple_; return std::tie(tuple_, nonce_) < std::tie(other.tuple_, other.nonce_);
}
Origin Origin::CreateUniqueOpaque() {
return Origin(ConstructAsOpaque::kTag);
}
Origin::Origin(ConstructAsOpaque) : nonce_(base::UnguessableToken::Create()) {}
Origin::Origin(SchemeHostPort tuple) : tuple_(std::move(tuple)) {
DCHECK(!tuple_.IsInvalid());
} }
std::ostream& operator<<(std::ostream& out, const url::Origin& origin) { std::ostream& operator<<(std::ostream& out, const url::Origin& origin) {
......
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
#include <string> #include <string>
#include "base/debug/alias.h" #include "base/debug/alias.h"
#include "base/optional.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/unguessable_token.h"
#include "url/scheme_host_port.h" #include "url/scheme_host_port.h"
#include "url/third_party/mozilla/url_parse.h" #include "url/third_party/mozilla/url_parse.h"
#include "url/url_canon.h" #include "url/url_canon.h"
...@@ -22,7 +25,10 @@ class GURL; ...@@ -22,7 +25,10 @@ class GURL;
namespace url { namespace url {
// An Origin is a tuple of (scheme, host, port), as described in RFC 6454. // 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
// //
// TL;DR: If you need to make a security-relevant decision, use 'url::Origin'. // 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 // If you only need to extract the bits of a URL which are relevant for a
...@@ -30,31 +36,48 @@ namespace url { ...@@ -30,31 +36,48 @@ namespace url {
// //
// STL;SDR: If you aren't making actual network connections, use 'url::Origin'. // STL;SDR: If you aren't making actual network connections, use 'url::Origin'.
// //
// 'Origin', like 'SchemeHostPort', is composed of a tuple of (scheme, host,
// port), but contains a number of additional concepts which make it appropriate
// for use as a security boundary and access control mechanism between contexts.
//
// This class ought to be used when code needs to determine if two resources // This class ought to be used when code needs to determine if two resources
// are "same-origin", and when a canonical serialization of an origin is // are "same-origin", and when a canonical serialization of an origin is
// required. Note that some origins are "unique", meaning that they are not // required. Note that the canonical serialization of an origin *must not* be
// same-origin with any other origin (including themselves). // used to determine if two resources are same-origin.
//
// A tuple origin, like 'SchemeHostPort', is composed of a tuple of (scheme,
// host, port), but contains a number of additional concepts which make it
// appropriate for use as a security boundary and access control mechanism
// 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.
//
// IMPORTANT: Since opaque origins always serialize as the string "null", it is
// *never* safe to use the serialization for security checks!
//
// A tuple origin and an opaque origin are never same-origin.
// //
// There are a few subtleties to note: // There are a few subtleties to note:
// //
// * Invalid and non-standard GURLs are parsed as unique origins. This includes // * 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.
//
// * Invalid and non-standard GURLs are parsed as opaque origins. This includes
// non-hierarchical URLs like 'data:text/html,...' and 'javascript:alert(1)'. // non-hierarchical URLs like 'data:text/html,...' and 'javascript:alert(1)'.
// //
// * GURLs with schemes of 'filesystem' or 'blob' parse the origin out of the // * GURLs with schemes of 'filesystem' or 'blob' parse the origin out of the
// internals of the URL. That is, 'filesystem:https://example.com/temporary/f' // internals of the URL. That is, 'filesystem:https://example.com/temporary/f'
// is parsed as ('https', 'example.com', 443). // is parsed as ('https', 'example.com', 443).
// //
// * Unique origins all serialize to the string "null"; this means that the
// serializations of two unique origins are identical to each other, though
// the origins themselves are not "the same". This means that origins'
// serializations must not be relied upon for security checks.
//
// * GURLs with a 'file' scheme are tricky. They are parsed as ('file', '', 0), // * GURLs with a 'file' scheme are tricky. They are parsed as ('file', '', 0),
// but their behavior may differ from embedder to embedder. // but their behavior may differ from embedder to embedder.
// TODO(dcheng): This behavior is not consistent with Blink's notion of file
// URLs, which always creates an opaque origin.
// //
// * The host component of an IPv6 address includes brackets, just like the URL // * The host component of an IPv6 address includes brackets, just like the URL
// representation. // representation.
...@@ -78,16 +101,20 @@ namespace url { ...@@ -78,16 +101,20 @@ namespace url {
// } // }
class URL_EXPORT Origin { class URL_EXPORT Origin {
public: public:
// Creates a unique Origin. // Creates an opaque and always unique Origin. The returned Origin is
// always cross-origin to any Origin, including itself.
Origin(); Origin();
// Creates an Origin from |url|, as described at // Creates an Origin from |url|, as described at
// https://url.spec.whatwg.org/#origin, with the following additions: // https://url.spec.whatwg.org/#origin, with the following additions:
// //
// 1. If |url| is invalid or non-standard, a unique Origin is constructed. // 1. If |url| is invalid or non-standard, an opaque Origin is constructed.
// 2. 'filesystem' URLs behave as 'blob' URLs (that is, the origin is parsed // 2. 'filesystem' URLs behave as 'blob' URLs (that is, the origin is parsed
// out of everything in the URL which follows the scheme). // out of everything in the URL which follows the scheme).
// 3. 'file' URLs all parse as ("file", "", 0). // 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); static Origin Create(const GURL& url);
// Copyable and movable. // Copyable and movable.
...@@ -97,8 +124,8 @@ class URL_EXPORT Origin { ...@@ -97,8 +124,8 @@ class URL_EXPORT Origin {
Origin& operator=(Origin&&); Origin& operator=(Origin&&);
// Creates an Origin from a |scheme|, |host|, and |port|. All the parameters // Creates an Origin from a |scheme|, |host|, and |port|. All the parameters
// must be valid and canonicalized. Do not use this method to create unique // must be valid and canonicalized. Do not use this method to create opaque
// origins. Use Origin() for that. // origins. Use Origin() or Origin::CreateOpaque() for that.
// //
// This constructor should be used in order to pass 'Origin' objects back and // This constructor should be used in order to pass 'Origin' objects back and
// forth over IPC (as transitioning through GURL would risk potentially // forth over IPC (as transitioning through GURL would risk potentially
...@@ -119,12 +146,17 @@ class URL_EXPORT Origin { ...@@ -119,12 +146,17 @@ class URL_EXPORT Origin {
~Origin(); ~Origin();
// For unique origins, these return ("", "", 0). // For opaque origins, these return ("", "", 0).
const std::string& scheme() const { return tuple_.scheme(); } const std::string& scheme() const {
const std::string& host() const { return tuple_.host(); } return !unique() ? tuple_.scheme() : base::EmptyString();
uint16_t port() const { return tuple_.port(); } }
const std::string& host() const {
return !unique() ? tuple_.host() : base::EmptyString();
}
uint16_t port() const { return !unique() ? tuple_.port() : 0; }
bool unique() const { return unique_; } // TODO(dcheng): Rename this to opaque().
bool unique() const { return tuple_.IsInvalid(); }
// An ASCII serialization of the Origin as per Section 6.2 of RFC 6454, with // 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://". // the addition that all Origins with a 'file' scheme serialize to "file://".
...@@ -157,11 +189,49 @@ class URL_EXPORT Origin { ...@@ -157,11 +189,49 @@ class URL_EXPORT Origin {
bool operator<(const Origin& other) const; bool operator<(const Origin& other) const;
private: private:
// |tuple| must be valid, implying that the created Origin is never unique. 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 c = a;
// url::Origin d = b;
//
// |a| and |c| are same-origin, since |c| was copied from |a|. |b| and |d| are
// same-origin as well, since |d| was copied from |b|. All other combinations
// 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|.
//
// 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();
// 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&);
enum class ConstructAsOpaque { kTag };
explicit Origin(ConstructAsOpaque);
// |tuple| must be valid, implying that the created Origin is never an opaque
// origin.
explicit Origin(SchemeHostPort tuple); 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.
SchemeHostPort tuple_; SchemeHostPort tuple_;
bool unique_;
// 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_;
}; };
URL_EXPORT std::ostream& operator<<(std::ostream& out, const Origin& origin); URL_EXPORT std::ostream& operator<<(std::ostream& out, const Origin& origin);
......
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
#include "url/gurl.h" #include "url/gurl.h"
#include "url/origin.h" #include "url/origin.h"
namespace { namespace url {
void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) { void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) {
EXPECT_EQ(a, b); EXPECT_EQ(a, b);
const url::Parsed& a_parsed = a.parsed_for_possibly_invalid_spec(); const Parsed& a_parsed = a.parsed_for_possibly_invalid_spec();
const url::Parsed& b_parsed = b.parsed_for_possibly_invalid_spec(); const Parsed& b_parsed = b.parsed_for_possibly_invalid_spec();
EXPECT_EQ(a_parsed.scheme.begin, b_parsed.scheme.begin); EXPECT_EQ(a_parsed.scheme.begin, b_parsed.scheme.begin);
EXPECT_EQ(a_parsed.scheme.len, b_parsed.scheme.len); EXPECT_EQ(a_parsed.scheme.len, b_parsed.scheme.len);
EXPECT_EQ(a_parsed.username.begin, b_parsed.username.begin); EXPECT_EQ(a_parsed.username.begin, b_parsed.username.begin);
...@@ -35,14 +35,37 @@ void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) { ...@@ -35,14 +35,37 @@ void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) {
EXPECT_EQ(a_parsed.ref.len, b_parsed.ref.len); EXPECT_EQ(a_parsed.ref.len, b_parsed.ref.len);
} }
TEST(OriginTest, UniqueOriginComparison) { class OriginTest : public ::testing::Test {
url::Origin unique_origin; protected:
Origin CreateUniqueOpaque() { return Origin::CreateUniqueOpaque(); }
Origin CreateCanonical(const GURL& url) {
return Origin::CreateCanonical(url);
}
};
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.scheme());
EXPECT_EQ("", unique_origin.host()); EXPECT_EQ("", unique_origin.host());
EXPECT_EQ(0, unique_origin.port()); EXPECT_EQ(0, unique_origin.port());
EXPECT_TRUE(unique_origin.unique()); EXPECT_TRUE(unique_origin.unique());
EXPECT_FALSE(unique_origin.IsSameOriginWith(unique_origin)); EXPECT_FALSE(unique_origin.IsSameOriginWith(unique_origin));
// 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));
// The default constructed Origin and the opaque Origin should always be
// cross origin to each other.
EXPECT_FALSE(opaque_origin.IsSameOriginWith(unique_origin));
const char* const urls[] = {"data:text/html,Hello!", const char* const urls[] = {"data:text/html,Hello!",
"javascript:alert(1)", "javascript:alert(1)",
"about:blank", "about:blank",
...@@ -53,20 +76,62 @@ TEST(OriginTest, UniqueOriginComparison) { ...@@ -53,20 +76,62 @@ TEST(OriginTest, UniqueOriginComparison) {
for (auto* test_url : urls) { for (auto* test_url : urls) {
SCOPED_TRACE(test_url); SCOPED_TRACE(test_url);
GURL url(test_url); GURL url(test_url);
url::Origin origin = url::Origin::Create(url);
EXPECT_EQ("", origin.scheme());
EXPECT_EQ("", origin.host());
EXPECT_EQ(0, origin.port());
EXPECT_TRUE(origin.unique());
EXPECT_FALSE(origin.IsSameOriginWith(origin));
EXPECT_FALSE(unique_origin.IsSameOriginWith(origin));
EXPECT_FALSE(origin.IsSameOriginWith(unique_origin));
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL()); // no nonce mode of opaque origins
{
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));
// 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)));
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
}
} }
} }
TEST(OriginTest, ConstructFromTuple) { TEST_F(OriginTest, ConstructFromTuple) {
struct TestCases { struct TestCases {
const char* const scheme; const char* const scheme;
const char* const host; const char* const host;
...@@ -82,7 +147,7 @@ TEST(OriginTest, ConstructFromTuple) { ...@@ -82,7 +147,7 @@ TEST(OriginTest, ConstructFromTuple) {
scope_message << test_case.scheme << "://" << test_case.host << ":" scope_message << test_case.scheme << "://" << test_case.host << ":"
<< test_case.port; << test_case.port;
SCOPED_TRACE(scope_message); SCOPED_TRACE(scope_message);
url::Origin origin = url::Origin::CreateFromNormalizedTuple( Origin origin = Origin::CreateFromNormalizedTuple(
test_case.scheme, test_case.host, test_case.port); test_case.scheme, test_case.host, test_case.port);
EXPECT_EQ(test_case.scheme, origin.scheme()); EXPECT_EQ(test_case.scheme, origin.scheme());
...@@ -91,9 +156,9 @@ TEST(OriginTest, ConstructFromTuple) { ...@@ -91,9 +156,9 @@ TEST(OriginTest, ConstructFromTuple) {
} }
} }
TEST(OriginTest, ConstructFromGURL) { TEST_F(OriginTest, ConstructFromGURL) {
url::Origin different_origin = Origin different_origin =
url::Origin::Create(GURL("https://not-in-the-list.test/")); Origin::Create(GURL("https://not-in-the-list.test/"));
struct TestCases { struct TestCases {
const char* const url; const char* const url;
...@@ -146,7 +211,7 @@ TEST(OriginTest, ConstructFromGURL) { ...@@ -146,7 +211,7 @@ TEST(OriginTest, ConstructFromGURL) {
SCOPED_TRACE(test_case.url); SCOPED_TRACE(test_case.url);
GURL url(test_case.url); GURL url(test_case.url);
EXPECT_TRUE(url.is_valid()); EXPECT_TRUE(url.is_valid());
url::Origin origin = url::Origin::Create(url); Origin origin = Origin::Create(url);
EXPECT_EQ(test_case.expected_scheme, origin.scheme()); EXPECT_EQ(test_case.expected_scheme, origin.scheme());
EXPECT_EQ(test_case.expected_host, origin.host()); EXPECT_EQ(test_case.expected_host, origin.host());
EXPECT_EQ(test_case.expected_port, origin.port()); EXPECT_EQ(test_case.expected_port, origin.port());
...@@ -159,7 +224,7 @@ TEST(OriginTest, ConstructFromGURL) { ...@@ -159,7 +224,7 @@ TEST(OriginTest, ConstructFromGURL) {
} }
} }
TEST(OriginTest, Serialization) { TEST_F(OriginTest, Serialization) {
struct TestCases { struct TestCases {
const char* const url; const char* const url;
const char* const expected; const char* const expected;
...@@ -179,7 +244,7 @@ TEST(OriginTest, Serialization) { ...@@ -179,7 +244,7 @@ TEST(OriginTest, Serialization) {
SCOPED_TRACE(test_case.url); SCOPED_TRACE(test_case.url);
GURL url(test_case.url); GURL url(test_case.url);
EXPECT_TRUE(url.is_valid()); EXPECT_TRUE(url.is_valid());
url::Origin origin = url::Origin::Create(url); Origin origin = Origin::Create(url);
std::string serialized = origin.Serialize(); std::string serialized = origin.Serialize();
ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL()); ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL());
...@@ -192,7 +257,7 @@ TEST(OriginTest, Serialization) { ...@@ -192,7 +257,7 @@ TEST(OriginTest, Serialization) {
} }
} }
TEST(OriginTest, Comparison) { TEST_F(OriginTest, Comparison) {
// These URLs are arranged in increasing order: // These URLs are arranged in increasing order:
const char* const urls[] = { const char* const urls[] = {
"data:uniqueness", "data:uniqueness",
...@@ -206,19 +271,44 @@ TEST(OriginTest, Comparison) { ...@@ -206,19 +271,44 @@ TEST(OriginTest, Comparison) {
"https://b:81", "https://b:81",
}; };
for (size_t i = 0; i < arraysize(urls); i++) { {
GURL current_url(urls[i]); // Unlike below, pre-creation here isn't necessary, since the old creation
url::Origin current = url::Origin::Create(current_url); // path doesn't populate a nonce. It makes for easier copy and paste though.
for (size_t j = i; j < arraysize(urls); j++) { std::vector<Origin> origins;
GURL compare_url(urls[j]); for (const auto* test_url : urls)
url::Origin to_compare = url::Origin::Create(compare_url); origins.push_back(CreateCanonical(GURL(test_url)));
EXPECT_EQ(i < j, current < to_compare) << i << " < " << j;
EXPECT_EQ(j < i, to_compare < current) << j << " < " << i; 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.
{
// Pre-create the origins, as the internal nonce for unique origins changes
// 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)));
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;
}
} }
} }
} }
TEST(OriginTest, UnsafelyCreate) { TEST_F(OriginTest, UnsafelyCreate) {
struct TestCase { struct TestCase {
const char* scheme; const char* scheme;
const char* host; const char* host;
...@@ -235,7 +325,7 @@ TEST(OriginTest, UnsafelyCreate) { ...@@ -235,7 +325,7 @@ TEST(OriginTest, UnsafelyCreate) {
for (const auto& test : cases) { for (const auto& test : cases) {
SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":" SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
<< test.port); << test.port);
url::Origin origin = url::Origin::UnsafelyCreateOriginWithoutNormalization( Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization(
test.scheme, test.host, test.port); test.scheme, test.host, test.port);
EXPECT_EQ(test.scheme, origin.scheme()); EXPECT_EQ(test.scheme, origin.scheme());
EXPECT_EQ(test.host, origin.host()); EXPECT_EQ(test.host, origin.host());
...@@ -247,7 +337,7 @@ TEST(OriginTest, UnsafelyCreate) { ...@@ -247,7 +337,7 @@ TEST(OriginTest, UnsafelyCreate) {
} }
} }
TEST(OriginTest, UnsafelyCreateUniqueOnInvalidInput) { TEST_F(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
struct TestCases { struct TestCases {
const char* scheme; const char* scheme;
const char* host; const char* host;
...@@ -272,7 +362,7 @@ TEST(OriginTest, UnsafelyCreateUniqueOnInvalidInput) { ...@@ -272,7 +362,7 @@ TEST(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
for (const auto& test : cases) { for (const auto& test : cases) {
SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":" SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
<< test.port); << test.port);
url::Origin origin = url::Origin::UnsafelyCreateOriginWithoutNormalization( Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization(
test.scheme, test.host, test.port); test.scheme, test.host, test.port);
EXPECT_EQ("", origin.scheme()); EXPECT_EQ("", origin.scheme());
EXPECT_EQ("", origin.host()); EXPECT_EQ("", origin.host());
...@@ -284,7 +374,7 @@ TEST(OriginTest, UnsafelyCreateUniqueOnInvalidInput) { ...@@ -284,7 +374,7 @@ TEST(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
} }
} }
TEST(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) { TEST_F(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
struct TestCases { struct TestCases {
const char* scheme; const char* scheme;
size_t scheme_length; size_t scheme_length;
...@@ -301,7 +391,7 @@ TEST(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) { ...@@ -301,7 +391,7 @@ TEST(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
for (const auto& test : cases) { for (const auto& test : cases) {
SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":" SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
<< test.port); << test.port);
url::Origin origin = url::Origin::UnsafelyCreateOriginWithoutNormalization( Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization(
std::string(test.scheme, test.scheme_length), std::string(test.scheme, test.scheme_length),
std::string(test.host, test.host_length), test.port); std::string(test.host, test.host_length), test.port);
EXPECT_EQ("", origin.scheme()); EXPECT_EQ("", origin.scheme());
...@@ -314,7 +404,7 @@ TEST(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) { ...@@ -314,7 +404,7 @@ TEST(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
} }
} }
TEST(OriginTest, DomainIs) { TEST_F(OriginTest, DomainIs) {
const struct { const struct {
const char* url; const char* url;
const char* lower_ascii_domain; const char* lower_ascii_domain;
...@@ -355,7 +445,7 @@ TEST(OriginTest, DomainIs) { ...@@ -355,7 +445,7 @@ TEST(OriginTest, DomainIs) {
<< ")"); << ")");
GURL url(test_case.url); GURL url(test_case.url);
ASSERT_TRUE(url.is_valid()); ASSERT_TRUE(url.is_valid());
url::Origin origin = url::Origin::Create(url); Origin origin = Origin::Create(url);
EXPECT_EQ(test_case.expected_domain_is, EXPECT_EQ(test_case.expected_domain_is,
origin.DomainIs(test_case.lower_ascii_domain)); origin.DomainIs(test_case.lower_ascii_domain));
...@@ -364,17 +454,17 @@ TEST(OriginTest, DomainIs) { ...@@ -364,17 +454,17 @@ TEST(OriginTest, DomainIs) {
// If the URL is invalid, DomainIs returns false. // If the URL is invalid, DomainIs returns false.
GURL invalid_url("google.com"); GURL invalid_url("google.com");
ASSERT_FALSE(invalid_url.is_valid()); ASSERT_FALSE(invalid_url.is_valid());
EXPECT_FALSE(url::Origin::Create(invalid_url).DomainIs("google.com")); EXPECT_FALSE(Origin::Create(invalid_url).DomainIs("google.com"));
// Unique origins. // Unique origins.
EXPECT_FALSE(url::Origin().DomainIs("")); EXPECT_FALSE(Origin().DomainIs(""));
EXPECT_FALSE(url::Origin().DomainIs("com")); EXPECT_FALSE(Origin().DomainIs("com"));
} }
TEST(OriginTest, DebugAlias) { TEST_F(OriginTest, DebugAlias) {
url::Origin origin1 = url::Origin::Create(GURL("https://foo.com/bar")); Origin origin1 = Origin::Create(GURL("https://foo.com/bar"));
DEBUG_ALIAS_FOR_ORIGIN(origin1_debug_alias, origin1); DEBUG_ALIAS_FOR_ORIGIN(origin1_debug_alias, origin1);
EXPECT_STREQ("https://foo.com", origin1_debug_alias); EXPECT_STREQ("https://foo.com", origin1_debug_alias);
} }
} // namespace } // namespace url
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment