mirror of
https://github.com/servo/servo.git
synced 2025-10-01 17:19:16 +01:00
html: Allow legacy referrer policies only for <meta> referrer (#39506)
Follow the HTML specification and allow to use legacy referrer policies (never/default/always/origin-when-crossorigin) only with 'meta' referrer. See https://html.spec.whatwg.org/multipage/#meta-referrer (step 5) While for another HTML elements with 'referrerpolicy' content attribute (https://html.spec.whatwg.org/multipage/#referrer-policy-attribute) and for 'Referrer-Policy' HTTP header (https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-header-dfn) the referrer policy should be determine from the standard policy tokens (https://w3c.github.io/webappsec-referrer-policy/#referrer-policy). So unknown policy values (legacy from meta-referrer) will be ignored and determine as 'ReferrerPolicy::EmptyString'. Testing: No changes Fixes: #36833 Signed-off-by: Andrei Volykhin <andrei.volykhin@gmail.com>
This commit is contained in:
parent
f724651e1a
commit
6a1a3aea08
8 changed files with 59 additions and 45 deletions
|
@ -22,7 +22,6 @@ use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessa
|
||||||
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||||
use content_security_policy::{CspList, PolicyDisposition};
|
use content_security_policy::{CspList, PolicyDisposition};
|
||||||
use cookie::Cookie;
|
use cookie::Cookie;
|
||||||
use cssparser::match_ignore_ascii_case;
|
|
||||||
use data_url::mime::Mime;
|
use data_url::mime::Mime;
|
||||||
use devtools_traits::ScriptToDevtoolsControlMsg;
|
use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
@ -6003,21 +6002,6 @@ fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/webappsec-referrer-policy/#determine-policy-for-token>
|
|
||||||
pub(crate) fn determine_policy_for_token(token: &str) -> ReferrerPolicy {
|
|
||||||
match_ignore_ascii_case! { token,
|
|
||||||
"never" | "no-referrer" => ReferrerPolicy::NoReferrer,
|
|
||||||
"no-referrer-when-downgrade" => ReferrerPolicy::NoReferrerWhenDowngrade,
|
|
||||||
"origin" => ReferrerPolicy::Origin,
|
|
||||||
"same-origin" => ReferrerPolicy::SameOrigin,
|
|
||||||
"strict-origin" => ReferrerPolicy::StrictOrigin,
|
|
||||||
"default" | "strict-origin-when-cross-origin" => ReferrerPolicy::StrictOriginWhenCrossOrigin,
|
|
||||||
"origin-when-cross-origin" => ReferrerPolicy::OriginWhenCrossOrigin,
|
|
||||||
"always" | "unsafe-url" => ReferrerPolicy::UnsafeUrl,
|
|
||||||
_ => ReferrerPolicy::EmptyString,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specifies the type of focus event that is sent to a pipeline
|
/// Specifies the type of focus event that is sent to a pipeline
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub(crate) enum FocusType {
|
pub(crate) enum FocusType {
|
||||||
|
|
|
@ -106,7 +106,7 @@ use crate::dom::customelementregistry::{
|
||||||
CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementState,
|
CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementState,
|
||||||
is_valid_custom_element_name,
|
is_valid_custom_element_name,
|
||||||
};
|
};
|
||||||
use crate::dom::document::{Document, LayoutDocumentHelpers, determine_policy_for_token};
|
use crate::dom::document::{Document, LayoutDocumentHelpers};
|
||||||
use crate::dom::documentfragment::DocumentFragment;
|
use crate::dom::documentfragment::DocumentFragment;
|
||||||
use crate::dom::domrect::DOMRect;
|
use crate::dom::domrect::DOMRect;
|
||||||
use crate::dom::domrectlist::DOMRectList;
|
use crate::dom::domrectlist::DOMRectList;
|
||||||
|
@ -5481,7 +5481,7 @@ pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString
|
||||||
pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
|
pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
|
||||||
element
|
element
|
||||||
.get_attribute(&ns!(), &local_name!("referrerpolicy"))
|
.get_attribute(&ns!(), &local_name!("referrerpolicy"))
|
||||||
.map(|attribute| determine_policy_for_token(&attribute.value()))
|
.map(|attribute| ReferrerPolicy::from(&**attribute.value()))
|
||||||
.unwrap_or(element.owner_document().get_referrer_policy())
|
.unwrap_or(element.owner_document().get_referrer_policy())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ use crate::dom::bindings::inheritance::Castable;
|
||||||
use crate::dom::bindings::reflector::DomGlobal;
|
use crate::dom::bindings::reflector::DomGlobal;
|
||||||
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
|
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
|
||||||
use crate::dom::bindings::str::{DOMString, USVString};
|
use crate::dom::bindings::str::{DOMString, USVString};
|
||||||
use crate::dom::document::{Document, determine_policy_for_token};
|
use crate::dom::document::Document;
|
||||||
use crate::dom::domtokenlist::DOMTokenList;
|
use crate::dom::domtokenlist::DOMTokenList;
|
||||||
use crate::dom::element::{
|
use crate::dom::element::{
|
||||||
AttributeMutation, Element, LayoutElementHelpers, reflect_referrer_policy_attribute,
|
AttributeMutation, Element, LayoutElementHelpers, reflect_referrer_policy_attribute,
|
||||||
|
@ -336,7 +336,7 @@ impl HTMLIFrameElement {
|
||||||
// Note: despite not being explicitly stated in the spec steps, this falls back to
|
// Note: despite not being explicitly stated in the spec steps, this falls back to
|
||||||
// document's referrer policy here because it satisfies the expectations that when unset,
|
// document's referrer policy here because it satisfies the expectations that when unset,
|
||||||
// the iframe should inherit the referrer policy of its parent
|
// the iframe should inherit the referrer policy of its parent
|
||||||
let referrer_policy = match determine_policy_for_token(referrer_policy_token.str()) {
|
let referrer_policy = match ReferrerPolicy::from(referrer_policy_token.str()) {
|
||||||
ReferrerPolicy::EmptyString => document.get_referrer_policy(),
|
ReferrerPolicy::EmptyString => document.get_referrer_policy(),
|
||||||
policy => policy,
|
policy => policy,
|
||||||
};
|
};
|
||||||
|
|
|
@ -59,7 +59,7 @@ use crate::dom::bindings::reflector::DomGlobal;
|
||||||
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
|
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
|
||||||
use crate::dom::bindings::str::{DOMString, USVString};
|
use crate::dom::bindings::str::{DOMString, USVString};
|
||||||
use crate::dom::csp::{GlobalCspReporting, Violation};
|
use crate::dom::csp::{GlobalCspReporting, Violation};
|
||||||
use crate::dom::document::{Document, determine_policy_for_token};
|
use crate::dom::document::Document;
|
||||||
use crate::dom::element::{
|
use crate::dom::element::{
|
||||||
AttributeMutation, CustomElementCreationMode, Element, ElementCreator, LayoutElementHelpers,
|
AttributeMutation, CustomElementCreationMode, Element, ElementCreator, LayoutElementHelpers,
|
||||||
cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
|
cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
|
||||||
|
@ -1872,15 +1872,10 @@ impl VirtualMethods for HTMLImageElement {
|
||||||
// The element's referrerpolicy attribute's state is changed.
|
// The element's referrerpolicy attribute's state is changed.
|
||||||
let referrer_policy_state_changed = match mutation {
|
let referrer_policy_state_changed = match mutation {
|
||||||
AttributeMutation::Removed | AttributeMutation::Set(None) => {
|
AttributeMutation::Removed | AttributeMutation::Set(None) => {
|
||||||
let referrer_policy = determine_policy_for_token(&attr.value());
|
ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
|
||||||
|
|
||||||
referrer_policy != ReferrerPolicy::EmptyString
|
|
||||||
},
|
},
|
||||||
AttributeMutation::Set(Some(old_value)) => {
|
AttributeMutation::Set(Some(old_value)) => {
|
||||||
let new_referrer_policy = determine_policy_for_token(&attr.value());
|
ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
|
||||||
let old_referrer_policy = determine_policy_for_token(old_value);
|
|
||||||
|
|
||||||
new_referrer_policy != old_referrer_policy
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use compositing_traits::viewport_description::ViewportDescription;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use html5ever::{LocalName, Prefix, local_name, ns};
|
use html5ever::{LocalName, Prefix, local_name, ns};
|
||||||
use js::rust::HandleObject;
|
use js::rust::HandleObject;
|
||||||
|
use net_traits::ReferrerPolicy;
|
||||||
use servo_config::pref;
|
use servo_config::pref;
|
||||||
use style::str::HTML_SPACE_CHARACTERS;
|
use style::str::HTML_SPACE_CHARACTERS;
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||||
use crate::dom::bindings::inheritance::Castable;
|
use crate::dom::bindings::inheritance::Castable;
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
use crate::dom::bindings::str::DOMString;
|
use crate::dom::bindings::str::DOMString;
|
||||||
use crate::dom::document::{Document, determine_policy_for_token};
|
use crate::dom::document::Document;
|
||||||
use crate::dom::element::{AttributeMutation, Element};
|
use crate::dom::element::{AttributeMutation, Element};
|
||||||
use crate::dom::html::htmlelement::HTMLElement;
|
use crate::dom::html::htmlelement::HTMLElement;
|
||||||
use crate::dom::html::htmlheadelement::HTMLHeadElement;
|
use crate::dom::html::htmlheadelement::HTMLHeadElement;
|
||||||
|
@ -97,28 +98,32 @@ impl HTMLMetaElement {
|
||||||
// From spec: For historical reasons, unlike other standard metadata names, the processing model for referrer
|
// From spec: For historical reasons, unlike other standard metadata names, the processing model for referrer
|
||||||
// is not responsive to element removals, and does not use tree order. Only the most-recently-inserted or
|
// is not responsive to element removals, and does not use tree order. Only the most-recently-inserted or
|
||||||
// most-recently-modified meta element in this state has an effect.
|
// most-recently-modified meta element in this state has an effect.
|
||||||
// 1. If element is not in a document tree, then return.
|
// Step 1. If element is not in a document tree, then return.
|
||||||
let meta_node = self.upcast::<Node>();
|
let meta_node = self.upcast::<Node>();
|
||||||
if !meta_node.is_in_a_document_tree() {
|
if !meta_node.is_in_a_document_tree() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. If element does not have a name attribute whose value is an ASCII case-insensitive match for "referrer",
|
// Step 2. If element does not have a name attribute whose value is an ASCII
|
||||||
// then return.
|
// case-insensitive match for "referrer", then return.
|
||||||
if self.upcast::<Element>().get_name() != Some(atom!("referrer")) {
|
if self.upcast::<Element>().get_name() != Some(atom!("referrer")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. If element does not have a content attribute, or that attribute's value is the empty string, then return.
|
// Step 3. If element does not have a content attribute, or that attribute's value is the
|
||||||
let content = self
|
// empty string, then return.
|
||||||
|
if let Some(content) = self
|
||||||
.upcast::<Element>()
|
.upcast::<Element>()
|
||||||
.get_attribute(&ns!(), &local_name!("content"));
|
.get_attribute(&ns!(), &local_name!("content"))
|
||||||
if let Some(attr) = content {
|
.filter(|attr| !attr.value().is_empty())
|
||||||
let attr = attr.value();
|
{
|
||||||
let attr_val = attr.trim();
|
// Step 4. Let value be the value of element's content attribute, converted to ASCII
|
||||||
if !attr_val.is_empty() {
|
// lowercase.
|
||||||
doc.set_referrer_policy(determine_policy_for_token(attr_val));
|
// Step 5. If value is one of the values given in the first column of the following
|
||||||
}
|
// table, then set value to the value given in the second column:
|
||||||
|
// Step 6. If value is a referrer policy, then set element's node document's policy
|
||||||
|
// container's referrer policy to policy.
|
||||||
|
doc.set_referrer_policy(ReferrerPolicy::from_with_legacy(&content.value()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ use crate::dom::bindings::refcounted::Trusted;
|
||||||
use crate::dom::bindings::reflector::DomGlobal;
|
use crate::dom::bindings::reflector::DomGlobal;
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
use crate::dom::csp::{GlobalCspReporting, Violation};
|
use crate::dom::csp::{GlobalCspReporting, Violation};
|
||||||
use crate::dom::document::{Document, determine_policy_for_token};
|
use crate::dom::document::Document;
|
||||||
use crate::dom::element::Element;
|
use crate::dom::element::Element;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::medialist::MediaList;
|
use crate::dom::medialist::MediaList;
|
||||||
|
@ -100,7 +100,7 @@ impl LinkProcessingOptions {
|
||||||
// Step 4. If attribs["referrerpolicy"] exists and is an ASCII case-insensitive match for
|
// Step 4. If attribs["referrerpolicy"] exists and is an ASCII case-insensitive match for
|
||||||
// some referrer policy, then set options's referrer policy to that referrer policy.
|
// some referrer policy, then set options's referrer policy to that referrer policy.
|
||||||
if let Some(referrer_policy) = link_object.value_for_key_in_link_header("referrerpolicy") {
|
if let Some(referrer_policy) = link_object.value_for_key_in_link_header("referrerpolicy") {
|
||||||
self.referrer_policy = determine_policy_for_token(referrer_policy);
|
self.referrer_policy = ReferrerPolicy::from(referrer_policy);
|
||||||
}
|
}
|
||||||
// Step 5. If attribs["nonce"] exists, then set options's nonce to attribs["nonce"].
|
// Step 5. If attribs["nonce"] exists, then set options's nonce to attribs["nonce"].
|
||||||
if let Some(nonce) = link_object.value_for_key_in_link_header("nonce") {
|
if let Some(nonce) = link_object.value_for_key_in_link_header("nonce") {
|
||||||
|
|
|
@ -24,7 +24,7 @@ use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
|
|
||||||
use crate::dom::bindings::reflector::DomGlobal;
|
use crate::dom::bindings::reflector::DomGlobal;
|
||||||
use crate::dom::bindings::trace::{CustomTraceable, JSTraceable};
|
use crate::dom::bindings::trace::{CustomTraceable, JSTraceable};
|
||||||
use crate::dom::document::{Document, determine_policy_for_token};
|
use crate::dom::document::Document;
|
||||||
use crate::dom::html::htmlscriptelement::script_fetch_request;
|
use crate::dom::html::htmlscriptelement::script_fetch_request;
|
||||||
use crate::dom::processingoptions::determine_cors_settings_for_token;
|
use crate::dom::processingoptions::determine_cors_settings_for_token;
|
||||||
use crate::fetch::create_a_potential_cors_request;
|
use crate::fetch::create_a_potential_cors_request;
|
||||||
|
@ -263,7 +263,7 @@ impl PrefetchSink {
|
||||||
|
|
||||||
fn get_referrer_policy(&self, tag: &Tag, name: LocalName) -> ReferrerPolicy {
|
fn get_referrer_policy(&self, tag: &Tag, name: LocalName) -> ReferrerPolicy {
|
||||||
self.get_attr(tag, name)
|
self.get_attr(tag, name)
|
||||||
.map(|attr| determine_policy_for_token(&attr.value))
|
.map(|attr| ReferrerPolicy::from(&*attr.value))
|
||||||
.unwrap_or(self.referrer_policy)
|
.unwrap_or(self.referrer_policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,19 @@ pub enum ReferrerPolicy {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReferrerPolicy {
|
impl ReferrerPolicy {
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#meta-referrer>
|
||||||
|
pub fn from_with_legacy(value: &str) -> Self {
|
||||||
|
// Step 5. If value is one of the values given in the first column of the following table,
|
||||||
|
// then set value to the value given in the second column:
|
||||||
|
match value.to_ascii_lowercase().as_str() {
|
||||||
|
"never" => ReferrerPolicy::NoReferrer,
|
||||||
|
"default" => ReferrerPolicy::StrictOriginWhenCrossOrigin,
|
||||||
|
"always" => ReferrerPolicy::UnsafeUrl,
|
||||||
|
"origin-when-crossorigin" => ReferrerPolicy::OriginWhenCrossOrigin,
|
||||||
|
_ => ReferrerPolicy::from(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header>
|
/// <https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header>
|
||||||
pub fn parse_header_for_response(headers: &Option<Serde<HeaderMap>>) -> Self {
|
pub fn parse_header_for_response(headers: &Option<Serde<HeaderMap>>) -> Self {
|
||||||
// Step 4. Return policy.
|
// Step 4. Return policy.
|
||||||
|
@ -145,6 +158,23 @@ impl ReferrerPolicy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for ReferrerPolicy {
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#referrer-policy-attribute>
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
match value.to_ascii_lowercase().as_str() {
|
||||||
|
"no-referrer" => ReferrerPolicy::NoReferrer,
|
||||||
|
"no-referrer-when-downgrade" => ReferrerPolicy::NoReferrerWhenDowngrade,
|
||||||
|
"origin" => ReferrerPolicy::Origin,
|
||||||
|
"same-origin" => ReferrerPolicy::SameOrigin,
|
||||||
|
"strict-origin" => ReferrerPolicy::StrictOrigin,
|
||||||
|
"strict-origin-when-cross-origin" => ReferrerPolicy::StrictOriginWhenCrossOrigin,
|
||||||
|
"origin-when-cross-origin" => ReferrerPolicy::OriginWhenCrossOrigin,
|
||||||
|
"unsafe-url" => ReferrerPolicy::UnsafeUrl,
|
||||||
|
_ => ReferrerPolicy::EmptyString,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for ReferrerPolicy {
|
impl Display for ReferrerPolicy {
|
||||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let string = match self {
|
let string = match self {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue