mirror of
https://github.com/servo/servo.git
synced 2025-06-06 00:25:37 +00:00
Implement inner slot for cryptographic nonce (#36965)
Also update the `html/dom/reflection-metadata.html` test to handle the case where `nonce` does not reflect back to the attribute after an IDL change. Part of https://github.com/servo/servo/issues/4577 Fixes https://github.com/web-platform-tests/wpt/issues/43286 Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
parent
3aff272e14
commit
a24fce3ae7
14 changed files with 132 additions and 98 deletions
|
@ -4313,7 +4313,7 @@ impl Document {
|
|||
},
|
||||
Some(csp_list) => {
|
||||
let element = csp::Element {
|
||||
nonce: el.nonce_attribute_if_nonceable().map(Cow::Owned),
|
||||
nonce: el.nonce_value_if_nonceable().map(Cow::Owned),
|
||||
};
|
||||
csp_list.should_elements_inline_type_behavior_be_blocked(&element, type_, source)
|
||||
},
|
||||
|
|
|
@ -146,8 +146,8 @@ use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserve
|
|||
use crate::dom::mutationobserver::{Mutation, MutationObserver};
|
||||
use crate::dom::namednodemap::NamedNodeMap;
|
||||
use crate::dom::node::{
|
||||
BindContext, ChildrenMutation, LayoutNodeHelpers, Node, NodeDamage, NodeFlags, NodeTraits,
|
||||
ShadowIncluding, UnbindContext,
|
||||
BindContext, ChildrenMutation, CloneChildrenFlag, LayoutNodeHelpers, Node, NodeDamage,
|
||||
NodeFlags, NodeTraits, ShadowIncluding, UnbindContext,
|
||||
};
|
||||
use crate::dom::nodelist::NodeList;
|
||||
use crate::dom::promise::Promise;
|
||||
|
@ -2188,10 +2188,53 @@ impl Element {
|
|||
};
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
|
||||
pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
|
||||
self.ensure_rare_data().cryptographic_nonce = nonce;
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
|
||||
pub(crate) fn nonce_value(&self) -> String {
|
||||
match self.rare_data().as_ref() {
|
||||
None => String::new(),
|
||||
Some(rare_data) => rare_data.cryptographic_nonce.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
|
||||
pub(crate) fn update_nonce_post_connection(&self) {
|
||||
// Whenever an element including HTMLOrSVGElement becomes browsing-context connected,
|
||||
// the user agent must execute the following steps on the element:
|
||||
if !self.upcast::<Node>().is_connected_with_browsing_context() {
|
||||
return;
|
||||
}
|
||||
let global = self.owner_global();
|
||||
// Step 1: Let CSP list be element's shadow-including root's policy container's CSP list.
|
||||
let csp_list = match global.get_csp_list() {
|
||||
None => return,
|
||||
Some(csp_list) => csp_list,
|
||||
};
|
||||
// Step 2: If CSP list contains a header-delivered Content Security Policy,
|
||||
// and element has a nonce content attribute whose value is not the empty string, then:
|
||||
if !csp_list.contains_a_header_delivered_content_security_policy() ||
|
||||
self.get_string_attribute(&local_name!("nonce")).is_empty()
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Step 2.1: Let nonce be element's [[CryptographicNonce]].
|
||||
let nonce = self.nonce_value();
|
||||
// Step 2.2: Set an attribute value for element using "nonce" and the empty string.
|
||||
self.set_string_attribute(&local_name!("nonce"), "".into(), CanGc::note());
|
||||
// Step 2.3: Set element's [[CryptographicNonce]] to nonce.
|
||||
self.update_nonce_internal_slot(nonce);
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/CSP/#is-element-nonceable>
|
||||
pub(crate) fn nonce_attribute_if_nonceable(&self) -> Option<String> {
|
||||
pub(crate) fn nonce_value_if_nonceable(&self) -> Option<String> {
|
||||
// Step 1: If element does not have an attribute named "nonce", return "Not Nonceable".
|
||||
let nonce_attribute = self.get_attribute(&ns!(), &local_name!("nonce"))?;
|
||||
if !self.has_attribute(&local_name!("nonce")) {
|
||||
return None;
|
||||
}
|
||||
// Step 2: If element is a script element, then for each attribute of element’s attribute list:
|
||||
if self.downcast::<HTMLScriptElement>().is_some() {
|
||||
for attr in self.attrs().iter() {
|
||||
|
@ -2213,7 +2256,7 @@ impl Element {
|
|||
// TODO(https://github.com/servo/servo/issues/4577 and https://github.com/whatwg/html/issues/3257):
|
||||
// Figure out how to retrieve this information from the parser
|
||||
// Step 4: Return "Nonceable".
|
||||
Some(nonce_attribute.value().to_string().trim().to_owned())
|
||||
Some(self.nonce_value().trim().to_owned())
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#insert-adjacent
|
||||
|
@ -4197,6 +4240,31 @@ impl VirtualMethods for Element {
|
|||
self.tag_name.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn post_connection_steps(&self) {
|
||||
if let Some(s) = self.super_type() {
|
||||
s.post_connection_steps();
|
||||
}
|
||||
|
||||
self.update_nonce_post_connection();
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#nonce-attributes%3Aconcept-node-clone-ext>
|
||||
fn cloning_steps(
|
||||
&self,
|
||||
copy: &Node,
|
||||
maybe_doc: Option<&Document>,
|
||||
clone_children: CloneChildrenFlag,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
if let Some(s) = self.super_type() {
|
||||
s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
|
||||
}
|
||||
let elem = copy.downcast::<Element>().unwrap();
|
||||
if let Some(rare_data) = self.rare_data().as_ref() {
|
||||
elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
|
|
|
@ -645,13 +645,16 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
|
|||
Ok(internals)
|
||||
}
|
||||
|
||||
// FIXME: The nonce should be stored in an internal slot instead of an
|
||||
// attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce)
|
||||
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
|
||||
make_getter!(Nonce, "nonce");
|
||||
fn Nonce(&self) -> DOMString {
|
||||
self.as_element().nonce_value().into()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
|
||||
make_setter!(SetNonce, "nonce");
|
||||
fn SetNonce(&self, value: DOMString) {
|
||||
self.as_element()
|
||||
.update_nonce_internal_slot(value.to_string())
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
|
||||
fn Autofocus(&self) -> bool {
|
||||
|
@ -1138,6 +1141,15 @@ impl VirtualMethods for HTMLElement {
|
|||
},
|
||||
}
|
||||
},
|
||||
(&local_name!("nonce"), mutation) => match mutation {
|
||||
AttributeMutation::Set(_) => {
|
||||
let nonce = &**attr.value();
|
||||
element.update_nonce_internal_slot(nonce.to_owned());
|
||||
},
|
||||
AttributeMutation::Removed => {
|
||||
element.update_nonce_internal_slot("".to_owned());
|
||||
},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ use crate::dom::attr::Attr;
|
|||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
|
||||
use crate::dom::bindings::codegen::GenericBindings::HTMLElementBinding::HTMLElement_Binding::HTMLElementMethods;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::reflector::DomGlobal;
|
||||
|
@ -344,7 +343,7 @@ impl HTMLLinkElement {
|
|||
destination: Some(destination),
|
||||
integrity: String::new(),
|
||||
link_type: String::new(),
|
||||
cryptographic_nonce_metadata: self.upcast::<HTMLElement>().Nonce().into(),
|
||||
cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
|
||||
cross_origin: cors_setting_for_element(element),
|
||||
referrer_policy: referrer_policy_for_element(element),
|
||||
policy_container: document.policy_container().to_owned(),
|
||||
|
|
|
@ -781,7 +781,7 @@ impl HTMLScriptElement {
|
|||
};
|
||||
|
||||
// Step 24. Let cryptographic nonce be el's [[CryptographicNonce]] internal slot's value.
|
||||
let cryptographic_nonce = self.upcast::<HTMLElement>().Nonce().into();
|
||||
let cryptographic_nonce = self.upcast::<Element>().nonce_value();
|
||||
|
||||
// Step 25. If el has an integrity attribute, then let integrity metadata be that attribute's value.
|
||||
// Otherwise, let integrity metadata be the empty string.
|
||||
|
|
|
@ -75,4 +75,5 @@ pub(crate) struct ElementRareData {
|
|||
/// > Element objects have an internal [[RegisteredIntersectionObservers]] slot,
|
||||
/// > which is initialized to an empty list. This list holds IntersectionObserverRegistration records, which have:
|
||||
pub(crate) registered_intersection_observers: Vec<IntersectionObserverRegistration>,
|
||||
pub(crate) cryptographic_nonce: String,
|
||||
}
|
||||
|
|
|
@ -8,12 +8,13 @@ use js::rust::HandleObject;
|
|||
use script_bindings::str::DOMString;
|
||||
use stylo_dom::ElementState;
|
||||
|
||||
use crate::dom::attr::Attr;
|
||||
use crate::dom::bindings::codegen::Bindings::SVGElementBinding::SVGElementMethods;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::element::Element;
|
||||
use crate::dom::element::{AttributeMutation, Element};
|
||||
use crate::dom::node::{Node, NodeTraits};
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
@ -59,11 +60,33 @@ impl SVGElement {
|
|||
can_gc,
|
||||
)
|
||||
}
|
||||
|
||||
fn as_element(&self) -> &Element {
|
||||
self.upcast::<Element>()
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualMethods for SVGElement {
|
||||
fn super_type(&self) -> Option<&dyn VirtualMethods> {
|
||||
Some(self.upcast::<Element>() as &dyn VirtualMethods)
|
||||
Some(self.as_element() as &dyn VirtualMethods)
|
||||
}
|
||||
|
||||
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
|
||||
self.super_type()
|
||||
.unwrap()
|
||||
.attribute_mutated(attr, mutation, can_gc);
|
||||
let element = self.as_element();
|
||||
if let (&local_name!("nonce"), mutation) = (attr.local_name(), mutation) {
|
||||
match mutation {
|
||||
AttributeMutation::Set(_) => {
|
||||
let nonce = &**attr.value();
|
||||
element.update_nonce_internal_slot(nonce.to_owned());
|
||||
},
|
||||
AttributeMutation::Removed => {
|
||||
element.update_nonce_internal_slot(String::new());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,13 +108,16 @@ impl SVGElementMethods<crate::DomTypeHolder> for SVGElement {
|
|||
// <https://html.spec.whatwg.org/multipage/#globaleventhandlers>
|
||||
global_event_handlers!();
|
||||
|
||||
// FIXME: The nonce should be stored in an internal slot instead of an
|
||||
// attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce)
|
||||
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
|
||||
make_getter!(Nonce, "nonce");
|
||||
fn Nonce(&self) -> DOMString {
|
||||
self.as_element().nonce_value().into()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
|
||||
make_setter!(SetNonce, "nonce");
|
||||
fn SetNonce(&self, value: DOMString) {
|
||||
self.as_element()
|
||||
.update_nonce_internal_slot(value.to_string())
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
|
||||
fn Autofocus(&self) -> bool {
|
||||
|
|
2
tests/wpt/meta/MANIFEST.json
vendored
2
tests/wpt/meta/MANIFEST.json
vendored
|
@ -474524,7 +474524,7 @@
|
|||
[]
|
||||
],
|
||||
"reflection.js": [
|
||||
"b2c3b30aae36b390a60c05b39901826ba71e0b1a",
|
||||
"eeecd450fca8139e924affb298e7feb1a1fb46fb",
|
||||
[]
|
||||
],
|
||||
"render-blocking": {
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
[nonces.html]
|
||||
[Basic nonce tests for meh in HTML namespace]
|
||||
expected: FAIL
|
||||
|
||||
[Basic nonce tests for div in HTML namespace]
|
||||
expected: FAIL
|
||||
|
||||
[Basic nonce tests for script in HTML namespace]
|
||||
expected: FAIL
|
||||
|
||||
[Basic nonce tests for meh in SVG namespace]
|
||||
expected: FAIL
|
||||
|
||||
[Basic nonce tests for svg in SVG namespace]
|
||||
expected: FAIL
|
||||
|
||||
[Basic nonce tests for script in SVG namespace]
|
||||
expected: FAIL
|
||||
|
||||
[Basic nonce tests for style in HTML namespace]
|
||||
expected: FAIL
|
||||
|
||||
[Basic nonce tests for link in HTML namespace]
|
||||
expected: FAIL
|
|
@ -1,6 +1,3 @@
|
|||
[script-nonces-hidden-meta.sub.html]
|
||||
[Writing 'nonce' IDL attribute.]
|
||||
expected: FAIL
|
||||
|
||||
[createElement.nonce.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,30 +1,3 @@
|
|||
[script-nonces-hidden.html]
|
||||
[Reading 'nonce' content attribute and IDL attribute.]
|
||||
expected: FAIL
|
||||
|
||||
[Cloned node retains nonce.]
|
||||
expected: FAIL
|
||||
|
||||
[Cloned node retains nonce when inserted.]
|
||||
expected: FAIL
|
||||
|
||||
[Writing 'nonce' IDL attribute.]
|
||||
expected: FAIL
|
||||
|
||||
[Document-written script's nonce value.]
|
||||
expected: FAIL
|
||||
|
||||
[createElement.nonce.]
|
||||
expected: FAIL
|
||||
|
||||
[setAttribute('nonce') overwrites '.nonce' upon insertion.]
|
||||
expected: FAIL
|
||||
|
||||
[createElement.setAttribute.]
|
||||
expected: FAIL
|
||||
|
||||
[Custom elements expose the correct events.]
|
||||
expected: FAIL
|
||||
|
||||
[Nonces don't leak via CSS side-channels.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -2,9 +2,3 @@
|
|||
expected: TIMEOUT
|
||||
[Document-written script executes.]
|
||||
expected: NOTRUN
|
||||
|
||||
[createElement.nonce.]
|
||||
expected: FAIL
|
||||
|
||||
[Writing 'nonce' IDL attribute.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,22 +1,4 @@
|
|||
[svgscript-nonces-hidden.html]
|
||||
expected: TIMEOUT
|
||||
[Reading 'nonce' content attribute and IDL attribute.]
|
||||
expected: FAIL
|
||||
|
||||
[Cloned node retains nonce.]
|
||||
expected: FAIL
|
||||
|
||||
[Cloned node retains nonce when inserted.]
|
||||
expected: FAIL
|
||||
|
||||
[Document-written script executes.]
|
||||
expected: NOTRUN
|
||||
|
||||
[createElement.nonce.]
|
||||
expected: FAIL
|
||||
|
||||
[createElement.setAttribute.]
|
||||
expected: FAIL
|
||||
|
||||
[Writing 'nonce' IDL attribute.]
|
||||
expected: FAIL
|
||||
|
|
6
tests/wpt/tests/html/dom/reflection.js
vendored
6
tests/wpt/tests/html/dom/reflection.js
vendored
|
@ -967,6 +967,7 @@ ReflectionTests.reflects = function(data, idlName, idlObj, domName, domObj) {
|
|||
"previous value", "getAttribute()");
|
||||
ReflectionHarness.assertEquals(idlObj[idlName], previousIdl, "IDL get");
|
||||
} else {
|
||||
var previousValue = domObj.getAttribute(domName);
|
||||
idlObj[idlName] = idlTests[i];
|
||||
if (data.type == "boolean") {
|
||||
// Special case yay
|
||||
|
@ -976,6 +977,11 @@ ReflectionTests.reflects = function(data, idlName, idlObj, domName, domObj) {
|
|||
var expected = idlDomExpected[i] + "";
|
||||
if (data.isNullable && idlDomExpected[i] === null) {
|
||||
expected = null;
|
||||
} else if (idlName == "nonce") {
|
||||
// nonce doesn't reflect the value, as per /content-security-policy/nonce-hiding/
|
||||
// tests that confirm that retrieving the nonce value post IDL change does not
|
||||
// reflect back to the attribute (for security reasons)
|
||||
expected = previousValue;
|
||||
}
|
||||
ReflectionHarness.assertEquals(domObj.getAttribute(domName), expected,
|
||||
"getAttribute()");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue