servo/components/url/origin.rs
Sebastian C 76edcff202
Check all ancestor navigable trustworthiness for mixed content (#36157)
Propagate through documents a flag that represents if any of the
ancestor navigables has a potentially trustworthy origin.

The "potentially trustworthy origin" concept appears to have gotten
confused in a couple of places and we were instead testing if a URL had
"potentially trustworthy" properties.

The main test for the ancestor navigables is
[mixed-content/nested-iframes](https://github.com/web-platform-tests/wpt/blob/master/mixed-content/nested-iframes.window.js)

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by
`[X]` when the step is complete, and replace `___` with appropriate
data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #36108 

<!-- Either: -->
- [X] There are tests for these changes

---------

Signed-off-by: Sebastian C <sebsebmc@gmail.com>
2025-04-05 05:38:24 +00:00

209 lines
6.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::net::IpAddr;
use std::rc::Rc;
use malloc_size_of::malloc_size_of_is_0;
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use url::{Host, Origin};
use uuid::Uuid;
/// The origin of an URL
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum ImmutableOrigin {
/// A globally unique identifier
Opaque(OpaqueOrigin),
/// Consists of the URL's scheme, host and port
Tuple(String, Host, u16),
}
impl ImmutableOrigin {
pub fn new(origin: Origin) -> ImmutableOrigin {
match origin {
Origin::Opaque(_) => ImmutableOrigin::new_opaque(),
Origin::Tuple(scheme, host, port) => ImmutableOrigin::Tuple(scheme, host, port),
}
}
pub fn same_origin(&self, other: &MutableOrigin) -> bool {
self == other.immutable()
}
pub fn same_origin_domain(&self, other: &MutableOrigin) -> bool {
!other.has_domain() && self == other.immutable()
}
/// Creates a new opaque origin that is only equal to itself.
pub fn new_opaque() -> ImmutableOrigin {
ImmutableOrigin::Opaque(OpaqueOrigin::Opaque(servo_rand::random_uuid()))
}
// For use in mixed security context tests because data: URL workers inherit contexts
pub fn new_opaque_data_url_worker() -> ImmutableOrigin {
ImmutableOrigin::Opaque(OpaqueOrigin::SecureWorkerFromDataUrl(
servo_rand::random_uuid(),
))
}
pub fn scheme(&self) -> Option<&str> {
match *self {
ImmutableOrigin::Opaque(_) => None,
ImmutableOrigin::Tuple(ref scheme, _, _) => Some(&**scheme),
}
}
pub fn host(&self) -> Option<&Host> {
match *self {
ImmutableOrigin::Opaque(_) => None,
ImmutableOrigin::Tuple(_, ref host, _) => Some(host),
}
}
pub fn port(&self) -> Option<u16> {
match *self {
ImmutableOrigin::Opaque(_) => None,
ImmutableOrigin::Tuple(_, _, port) => Some(port),
}
}
pub fn into_url_origin(self) -> Origin {
match self {
ImmutableOrigin::Opaque(_) => Origin::new_opaque(),
ImmutableOrigin::Tuple(scheme, host, port) => Origin::Tuple(scheme, host, port),
}
}
/// Return whether this origin is a (scheme, host, port) tuple
/// (as opposed to an opaque origin).
pub fn is_tuple(&self) -> bool {
match *self {
ImmutableOrigin::Opaque(..) => false,
ImmutableOrigin::Tuple(..) => true,
}
}
/// <https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy>
pub fn is_potentially_trustworthy(&self) -> bool {
// 1. If origin is an opaque origin return "Not Trustworthy"
if matches!(self, ImmutableOrigin::Opaque(_)) {
return false;
}
if let ImmutableOrigin::Tuple(scheme, host, _) = self {
// 3. If origins scheme is either "https" or "wss", return "Potentially Trustworthy"
if scheme == "https" || scheme == "wss" {
return true;
}
// 6. If origins scheme is "file", return "Potentially Trustworthy".
if scheme == "file" {
return true;
}
// 4. If origins host matches one of the CIDR notations 127.0.0.0/8 or ::1/128,
// return "Potentially Trustworthy".
if let Ok(ip_addr) = host.to_string().parse::<IpAddr>() {
return ip_addr.is_loopback();
}
// 5. If the user agent conforms to the name resolution rules in
// [let-localhost-be-localhost] and one of the following is true:
// * origins host is "localhost" or "localhost."
// * origins host ends with ".localhost" or ".localhost."
// then return "Potentially Trustworthy".
if let Host::Domain(domain) = host {
if domain == "localhost" || domain.ends_with(".localhost") {
return true;
}
}
}
// 9. Return "Not Trustworthy".
false
}
/// <https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin>
pub fn ascii_serialization(&self) -> String {
self.clone().into_url_origin().ascii_serialization()
}
}
/// Opaque identifier for URLs that have file or other schemes
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum OpaqueOrigin {
Opaque(Uuid),
// Workers created from `data:` urls will have opaque origins but need to be treated
// as inheriting the secure context they were created in. This tracks that the origin
// was created in such a context
SecureWorkerFromDataUrl(Uuid),
}
malloc_size_of_is_0!(OpaqueOrigin);
/// A representation of an [origin](https://html.spec.whatwg.org/multipage/#origin-2).
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct MutableOrigin(Rc<(ImmutableOrigin, RefCell<Option<Host>>)>);
malloc_size_of_is_0!(MutableOrigin);
impl MutableOrigin {
pub fn new(origin: ImmutableOrigin) -> MutableOrigin {
MutableOrigin(Rc::new((origin, RefCell::new(None))))
}
pub fn immutable(&self) -> &ImmutableOrigin {
&(self.0).0
}
pub fn is_tuple(&self) -> bool {
self.immutable().is_tuple()
}
pub fn scheme(&self) -> Option<&str> {
self.immutable().scheme()
}
pub fn host(&self) -> Option<&Host> {
self.immutable().host()
}
pub fn port(&self) -> Option<u16> {
self.immutable().port()
}
pub fn same_origin(&self, other: &MutableOrigin) -> bool {
self.immutable() == other.immutable()
}
pub fn same_origin_domain(&self, other: &MutableOrigin) -> bool {
if let Some(ref self_domain) = *(self.0).1.borrow() {
if let Some(ref other_domain) = *(other.0).1.borrow() {
self_domain == other_domain &&
self.immutable().scheme() == other.immutable().scheme()
} else {
false
}
} else {
self.immutable().same_origin_domain(other)
}
}
pub fn domain(&self) -> Option<Host> {
(self.0).1.borrow().clone()
}
pub fn set_domain(&self, domain: Host) {
*(self.0).1.borrow_mut() = Some(domain);
}
pub fn has_domain(&self) -> bool {
(self.0).1.borrow().is_some()
}
pub fn effective_domain(&self) -> Option<Host> {
self.immutable()
.host()
.map(|host| self.domain().unwrap_or_else(|| host.clone()))
}
}