1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "url/origin.h"
12 #include <string_view>
16 #include "base/base64.h"
17 #include "base/check.h"
18 #include "base/check_op.h"
19 #include "base/containers/contains.h"
20 #include "base/containers/span.h"
21 #include "base/debug/crash_logging.h"
22 #include "base/pickle.h"
23 #include "base/strings/strcat.h"
24 #include "base/trace_event/base_tracing.h"
25 #include "base/trace_event/memory_usage_estimator.h"
26 #include "base/unguessable_token.h"
28 #include "url/scheme_host_port.h"
29 #include "url/url_constants.h"
30 #include "url/url_util.h"
34 Origin::Origin() : nonce_(Nonce()) {}
36 Origin Origin::Create(const GURL& url) {
42 if (url.SchemeIsFileSystem()) {
43 tuple = SchemeHostPort(*url.inner_url());
44 } else if (url.SchemeIsBlob()) {
45 // If we're dealing with a 'blob:' URL, https://url.spec.whatwg.org/#origin
46 // defines the origin as the origin of the URL which results from parsing
47 // the "path", which boils down to everything after the scheme. GURL's
48 // 'GetContent()' gives us exactly that.
49 tuple = SchemeHostPort(GURL(url.GetContent()));
51 tuple = SchemeHostPort(url);
53 // It's SchemeHostPort's responsibility to filter out unrecognized schemes;
54 // sanity check that this is happening.
55 DCHECK(!tuple.IsValid() || url.IsStandard() ||
56 base::Contains(GetLocalSchemes(), url.scheme_piece()) ||
57 AllowNonStandardSchemesForAndroidWebView());
62 return Origin(std::move(tuple));
65 Origin Origin::Resolve(const GURL& url, const Origin& base_origin) {
66 if (url.SchemeIs(kAboutScheme) || url.is_empty())
68 Origin result = Origin::Create(url);
71 return base_origin.DeriveNewOpaqueOrigin();
74 Origin::Origin(const Origin&) = default;
75 Origin& Origin::operator=(const Origin&) = default;
76 Origin::Origin(Origin&&) noexcept = default;
77 Origin& Origin::operator=(Origin&&) noexcept = default;
78 Origin::~Origin() = default;
81 absl::optional<Origin> Origin::UnsafelyCreateTupleOriginWithoutNormalization(
82 std::string_view scheme,
83 std::string_view host,
85 SchemeHostPort tuple(std::string(scheme), std::string(host), port,
86 SchemeHostPort::CHECK_CANONICALIZATION);
89 return Origin(std::move(tuple));
93 absl::optional<Origin> Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
94 std::string_view precursor_scheme,
95 std::string_view precursor_host,
96 uint16_t precursor_port,
97 const Origin::Nonce& nonce) {
98 SchemeHostPort precursor(std::string(precursor_scheme),
99 std::string(precursor_host), precursor_port,
100 SchemeHostPort::CHECK_CANONICALIZATION);
101 // For opaque origins, it is okay for the SchemeHostPort to be invalid;
102 // however, this should only arise when the arguments indicate the
103 // canonical representation of the invalid SchemeHostPort.
104 if (!precursor.IsValid() &&
105 !(precursor_scheme.empty() && precursor_host.empty() &&
106 precursor_port == 0)) {
107 return absl::nullopt;
109 return Origin(std::move(nonce), std::move(precursor));
113 Origin Origin::CreateFromNormalizedTuple(std::string scheme,
116 SchemeHostPort tuple(std::move(scheme), std::move(host), port,
117 SchemeHostPort::ALREADY_CANONICALIZED);
118 if (!tuple.IsValid())
120 return Origin(std::move(tuple));
124 Origin Origin::CreateOpaqueFromNormalizedPrecursorTuple(
125 std::string precursor_scheme,
126 std::string precursor_host,
127 uint16_t precursor_port,
128 const Origin::Nonce& nonce) {
129 SchemeHostPort precursor(std::move(precursor_scheme),
130 std::move(precursor_host), precursor_port,
131 SchemeHostPort::ALREADY_CANONICALIZED);
132 // For opaque origins, it is okay for the SchemeHostPort to be invalid.
133 return Origin(std::move(nonce), std::move(precursor));
136 std::string Origin::Serialize() const {
140 if (scheme() == kFileScheme)
143 return tuple_.Serialize();
146 GURL Origin::GetURL() const {
150 if (scheme() == kFileScheme)
151 return GURL("file:///");
153 return tuple_.GetURL();
156 const base::UnguessableToken* Origin::GetNonceForSerialization() const {
157 return nonce_ ? &nonce_->token() : nullptr;
160 bool Origin::IsSameOriginWith(const Origin& other) const {
161 // scheme/host/port must match, even for opaque origins where |tuple_| holds
162 // the precursor origin.
163 return std::tie(tuple_, nonce_) == std::tie(other.tuple_, other.nonce_);
166 bool Origin::IsSameOriginWith(const GURL& url) const {
170 // The `url::Origin::Create` call here preserves how IsSameOriginWith was used
171 // historically, even though in some scenarios it is not clearly correct:
172 // - Origin of about:blank and about:srcdoc cannot be correctly
173 // computed/recovered.
174 // - Ideally passing an invalid `url` would be a caller error (e.g. a DCHECK).
175 // - The caller intent is not always clear wrt handling the outer-vs-inner
176 // origins/URLs in blob: and filesystem: schemes.
177 return IsSameOriginWith(url::Origin::Create(url));
180 bool Origin::CanBeDerivedFrom(const GURL& url) const {
181 DCHECK(url.is_valid());
183 // For "no access" schemes, blink's SecurityOrigin will always create an
184 // opaque unique one. However, about: scheme is also registered as such but
185 // does not behave this way, therefore exclude it from this check.
186 if (base::Contains(url::GetNoAccessSchemes(), url.scheme()) &&
187 !url.SchemeIs(kAboutScheme)) {
188 // If |this| is not opaque, definitely return false as the expectation
189 // is for opaque origin.
193 // And if it is unique opaque origin, it definitely is fine. But if there
194 // is a precursor stored, we should fall through to compare the tuples.
195 if (!tuple_.IsValid())
199 SchemeHostPort url_tuple;
201 // Optimization for the common, success case: Scheme/Host/Port match on the
202 // precursor, and the URL is standard. Opaqueness does not matter as a tuple
203 // origin can always create an opaque tuple origin.
204 if (url.IsStandard()) {
205 // Note: if extra copies of the scheme and host are undesirable, this check
206 // can be implemented using StringPiece comparisons, but it has to account
207 // explicitly checks on port numbers.
208 if (url.SchemeIsFileSystem()) {
209 url_tuple = SchemeHostPort(*url.inner_url());
211 url_tuple = SchemeHostPort(url);
213 return url_tuple == tuple_;
215 // Blob URLs still contain an inner origin, however it is not accessible
216 // through inner_url(), therefore it requires specific case to handle it.
217 } else if (url.SchemeIsBlob()) {
218 // If |this| doesn't contain any precursor information, it is an unique
219 // opaque origin. It is valid case, as any browser-initiated navigation
220 // to about:blank or data: URL will result in a document with such
221 // origin and it is valid for it to create blob: URLs.
222 if (!tuple_.IsValid())
225 url_tuple = SchemeHostPort(GURL(url.GetContent()));
226 return url_tuple == tuple_;
229 // At this point, the URL has non-standard scheme.
230 DCHECK(!url.IsStandard());
232 // All about: URLs (about:blank, about:srcdoc) inherit their origin from
233 // the context which navigated them, which means that they can be in any
235 if (url.SchemeIs(kAboutScheme))
238 // All data: URLs commit in opaque origins, therefore |this| must be opaque
239 // if |url| has data: scheme.
240 if (url.SchemeIs(kDataScheme))
243 // If |this| does not have valid precursor tuple, it is unique opaque origin,
244 // which is what we expect non-standard schemes to get.
245 if (!tuple_.IsValid())
248 // However, when there is precursor present, the schemes must match.
249 return url.scheme() == tuple_.scheme();
252 bool Origin::DomainIs(std::string_view canonical_domain) const {
253 return !opaque() && url::DomainIs(tuple_.host(), canonical_domain);
256 bool Origin::operator<(const Origin& other) const {
257 return std::tie(tuple_, nonce_) < std::tie(other.tuple_, other.nonce_);
260 Origin Origin::DeriveNewOpaqueOrigin() const {
261 return Origin(Nonce(), tuple_);
264 std::string Origin::GetDebugString(bool include_nonce) const {
265 // Handle non-opaque origins first, as they are simpler.
267 std::string out = Serialize();
268 if (scheme() == kFileScheme)
269 base::StrAppend(&out, {" [internally: ", tuple_.Serialize(), "]"});
273 // For opaque origins, log the nonce and precursor as well. Without this,
274 // EXPECT_EQ failures between opaque origins are nearly impossible to
276 std::string out = base::StrCat({Serialize(), " [internally:"});
279 if (nonce_->raw_token().is_empty())
282 out += nonce_->raw_token().ToString();
285 if (!tuple_.IsValid())
286 base::StrAppend(&out, {" anonymous]"});
288 base::StrAppend(&out, {" derived from ", tuple_.Serialize(), "]"});
292 Origin::Origin(SchemeHostPort tuple) : tuple_(std::move(tuple)) {
294 DCHECK(tuple_.IsValid());
297 // Constructs an opaque origin derived from |precursor|.
298 Origin::Origin(const Nonce& nonce, SchemeHostPort precursor)
299 : tuple_(std::move(precursor)), nonce_(std::move(nonce)) {
301 // |precursor| is retained, but not accessible via scheme()/host()/port().
302 DCHECK_EQ("", scheme());
303 DCHECK_EQ("", host());
304 DCHECK_EQ(0U, port());
307 absl::optional<std::string> Origin::SerializeWithNonce() const {
308 return SerializeWithNonceImpl();
311 absl::optional<std::string> Origin::SerializeWithNonceAndInitIfNeeded() {
312 GetNonceForSerialization();
313 return SerializeWithNonceImpl();
316 // The pickle is saved in the following format, in order:
317 // string - tuple_.GetURL().spec().
318 // uint64_t (if opaque) - high bits of nonce if opaque. 0 if not initialized.
319 // uint64_t (if opaque) - low bits of nonce if opaque. 0 if not initialized.
320 absl::optional<std::string> Origin::SerializeWithNonceImpl() const {
321 if (!opaque() && !tuple_.IsValid())
322 return absl::nullopt;
325 pickle.WriteString(tuple_.Serialize());
326 if (opaque() && !nonce_->raw_token().is_empty()) {
327 pickle.WriteUInt64(nonce_->token().GetHighForSerialization());
328 pickle.WriteUInt64(nonce_->token().GetLowForSerialization());
329 } else if (opaque()) {
330 // Nonce hasn't been initialized.
331 pickle.WriteUInt64(0);
332 pickle.WriteUInt64(0);
335 base::span<const uint8_t> data(static_cast<const uint8_t*>(pickle.data()),
337 // Base64 encode the data to make it nicer to play with.
338 return base::Base64Encode(data);
342 absl::optional<Origin> Origin::Deserialize(const std::string& value) {
344 if (!base::Base64Decode(value, &data))
345 return absl::nullopt;
346 base::Pickle pickle(reinterpret_cast<char*>(&data[0]), data.size());
347 base::PickleIterator reader(pickle);
349 std::string pickled_url;
350 if (!reader.ReadString(&pickled_url))
351 return absl::nullopt;
352 GURL url(pickled_url);
354 // If only a tuple was serialized, then this origin is not opaque. For opaque
355 // origins, we expect two uint64's to be left in the pickle.
356 bool is_opaque = !reader.ReachedEnd();
358 // Opaque origins without a tuple are ok.
359 if (!is_opaque && !url.is_valid())
360 return absl::nullopt;
361 SchemeHostPort tuple(url);
363 // Possible successful early return if the pickled Origin was not opaque.
365 Origin origin(tuple);
367 return absl::nullopt; // Something went horribly wrong.
371 uint64_t nonce_high = 0;
372 if (!reader.ReadUInt64(&nonce_high))
373 return absl::nullopt;
375 uint64_t nonce_low = 0;
376 if (!reader.ReadUInt64(&nonce_low))
377 return absl::nullopt;
379 absl::optional<base::UnguessableToken> nonce_token =
380 base::UnguessableToken::Deserialize(nonce_high, nonce_low);
383 if (nonce_token.has_value()) {
384 // The serialized nonce wasn't empty, so copy it here.
385 nonce = Origin::Nonce(nonce_token.value());
388 origin.nonce_ = std::move(nonce);
389 origin.tuple_ = tuple;
393 void Origin::WriteIntoTrace(perfetto::TracedValue context) const {
394 std::move(context).WriteString(GetDebugString());
397 size_t Origin::EstimateMemoryUsage() const {
398 return base::trace_event::EstimateMemoryUsage(tuple_);
401 std::ostream& operator<<(std::ostream& out, const url::Origin& origin) {
402 out << origin.GetDebugString();
406 std::ostream& operator<<(std::ostream& out, const url::Origin::Nonce& nonce) {
407 // Subtle: don't let logging trigger lazy-generation of the token value.
408 if (nonce.raw_token().is_empty())
409 return (out << "(nonce TBD)");
411 return (out << nonce.raw_token());
414 bool IsSameOriginWith(const GURL& a, const GURL& b) {
415 return Origin::Create(a).IsSameOriginWith(Origin::Create(b));
418 Origin::Nonce::Nonce() = default;
419 Origin::Nonce::Nonce(const base::UnguessableToken& token) : token_(token) {
420 CHECK(!token_.is_empty());
423 const base::UnguessableToken& Origin::Nonce::token() const {
424 // Inspecting the value of a nonce triggers lazy-generation.
425 // TODO(dcheng): UnguessableToken::is_empty should go away -- what sentinel
426 // value to use instead?
427 if (token_.is_empty())
428 token_ = base::UnguessableToken::Create();
432 const base::UnguessableToken& Origin::Nonce::raw_token() const {
436 // Copying a Nonce triggers lazy-generation of the token.
437 Origin::Nonce::Nonce(const Origin::Nonce& other) : token_(other.token()) {}
439 Origin::Nonce& Origin::Nonce::operator=(const Origin::Nonce& other) {
440 // Copying a Nonce triggers lazy-generation of the token.
441 token_ = other.token();
445 // Moving a nonce does NOT trigger lazy-generation of the token.
446 Origin::Nonce::Nonce(Origin::Nonce&& other) noexcept : token_(other.token_) {
447 other.token_ = base::UnguessableToken(); // Reset |other|.
450 Origin::Nonce& Origin::Nonce::operator=(Origin::Nonce&& other) noexcept {
451 token_ = other.token_;
452 other.token_ = base::UnguessableToken(); // Reset |other|.
456 bool Origin::Nonce::operator<(const Origin::Nonce& other) const {
457 // When comparing, lazy-generation is required of both tokens, so that an
458 // ordering is established.
459 return token() < other.token();
462 bool Origin::Nonce::operator==(const Origin::Nonce& other) const {
463 // Equality testing doesn't actually require that the tokens be generated.
464 // If the tokens are both zero, equality only holds if they're the same
466 return (other.token_ == token_) && !(token_.is_empty() && (&other != this));
469 bool Origin::Nonce::operator!=(const Origin::Nonce& other) const {
470 return !(*this == other);
475 ScopedOriginCrashKey::ScopedOriginCrashKey(
476 base::debug::CrashKeyString* crash_key,
477 const url::Origin* value)
478 : scoped_string_value_(
480 value ? value->GetDebugString(false /* include_nonce */)
483 ScopedOriginCrashKey::~ScopedOriginCrashKey() = default;