Skip to content

Commit

Permalink
[CSP] Factorize SVGElement & MHTMLElement nonce hiding.
Browse files Browse the repository at this point in the history
According to:
whatwg/html#2373
html and svg Element are hiding their nonce when there are at least one
Content-Security-Policy defined from an HTTP header.

The two implementation:
- HTMLElement::InsertedInto
- SVGElement::InsertedInto

were hidding the nonce slightly differently. To prevent further
divergence, factorize this implementation into Element::HideNonce() and
call it from both places.

Bug: 1053496
Change-Id: I3cbad88f70c61591bef060d4188c82388e6001d2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2078536
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Reviewed-by: Fredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#746837}
  • Loading branch information
ArthurSonzogni authored and Commit Bot committed Mar 4, 2020
1 parent 4c9f5da commit 8b2cf55
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 39 deletions.
10 changes: 10 additions & 0 deletions third_party/blink/renderer/core/dom/element.cc
Expand Up @@ -4805,6 +4805,16 @@ Node* Element::InsertAdjacent(const String& where,
return nullptr;
}

void Element::HideNonce() {
const AtomicString& nonce_value = FastGetAttribute(html_names::kNonceAttr);
if (nonce_value.IsEmpty())
return;
if (!InActiveDocument())
return;
if (GetDocument().GetContentSecurityPolicy()->HasHeaderDeliveredPolicy())
setAttribute(html_names::kNonceAttr, g_empty_atom);
}

ElementIntersectionObserverData* Element::IntersectionObserverData() const {
if (HasRareData())
return GetElementRareData()->IntersectionObserverData();
Expand Down
11 changes: 11 additions & 0 deletions third_party/blink/renderer/core/dom/element.h
Expand Up @@ -1013,6 +1013,17 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable {

virtual void ParserDidSetAttributes() {}

// The "nonce" attribute is hidden when:
// 1) The Content-Security-Policy is delivered from the HTTP headers.
// 2) The Element is part of the active document.
// See https://github.com/whatwg/html/pull/2373
//
// This applies to the element of the HTML and SVG namespaces.
//
// This function clears the "nonce" attribute whenever conditions (1) and (2)
// are met.
void HideNonce();

private:
void ScrollLayoutBoxBy(const ScrollToOptions*);
void ScrollLayoutBoxTo(const ScrollToOptions*);
Expand Down
5 changes: 1 addition & 4 deletions third_party/blink/renderer/core/html/html_element.cc
Expand Up @@ -1213,11 +1213,8 @@ Node::InsertionNotificationRequest HTMLElement::InsertedInto(
// Process the superclass first to ensure that `InActiveDocument()` is
// updated.
Element::InsertedInto(insertion_point);
HideNonce();

if (GetDocument().GetContentSecurityPolicy()->HasHeaderDeliveredPolicy() &&
InActiveDocument() && FastHasAttribute(html_names::kNonceAttr)) {
setAttribute(html_names::kNonceAttr, g_empty_atom);
}
if (IsFormAssociatedCustomElement())
EnsureElementInternals().InsertedInto(insertion_point);

Expand Down
10 changes: 1 addition & 9 deletions third_party/blink/renderer/core/svg/svg_element.cc
Expand Up @@ -296,16 +296,8 @@ AffineTransform SVGElement::CalculateTransform(
Node::InsertionNotificationRequest SVGElement::InsertedInto(
ContainerNode& root_parent) {
Element::InsertedInto(root_parent);
HideNonce();
UpdateRelativeLengthsInformation();

const AtomicString& nonce_value = FastGetAttribute(html_names::kNonceAttr);
if (!nonce_value.IsEmpty()) {
setNonce(nonce_value);
if (InActiveDocument() &&
GetDocument().GetContentSecurityPolicy()->HasHeaderDeliveredPolicy()) {
setAttribute(html_names::kNonceAttr, g_empty_atom);
}
}
return kInsertionDone;
}

Expand Down
Expand Up @@ -3,30 +3,62 @@
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>
<script>
[["meh", ""],
["div", ""],
["script", ""],
["meh", "http://www.w3.org/2000/svg"],
["svg", "http://www.w3.org/2000/svg"],
["script", "http://www.w3.org/2000/svg"]].forEach(([localName, namespace]) => {
test(t => {
const element = namespace === "" ? document.createElement(localName) : document.createElementNS(namespace, localName);
t.add_cleanup(() => element.remove());
assert_equals(element.nonce, "", "Initial IDL attribute value");
element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
assert_equals(element.getAttribute("nonce"), "x", "Content attribute is modified after content attribute set");
document.body.appendChild(element);
assert_equals(element.nonce, "x", "IDL attribute is unchanged after element insertion");
assert_equals(element.getAttribute("nonce"), "", "Content attribute is changed after element insertion");
}, `Basic nonce tests for ${localName} in ${namespace === "" ? "HTML" : "SVG"} namespace`);

test(t => {
const element = namespace === "" ? document.createElement(localName) : document.createElementNS(namespace, localName);
element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
element.removeAttribute("nonce");
assert_equals(element.nonce, "", "IDL attribute is empty after content attribute removal");
}, `Ensure that removal of content attribute does not affect IDL attribute for ${localName} in ${namespace === "" ? "HTML" : "SVG"} namespace`);
});
const namespace_url= {
"HTML": "http://www.w3.org/1999/xhtml",
"SVG": "http://www.w3.org/2000/svg",
}
const test_cases = [
["meh" , "HTML"],
["div" , "HTML"],
["script" , "HTML"],
["meh" , "SVG"],
["svg" , "SVG"],
["script" , "SVG"],
];

test_cases.forEach(([localName, namespace]) => {
test(t => {
const element = document.createElementNS(namespace_url[namespace], localName);
t.add_cleanup(() => element.remove());
assert_equals(element.nonce, "", "Initial IDL attribute value");
assert_equals(element.getAttribute("nonce"), null, "Initial content attribute");

element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
assert_equals(element.getAttribute("nonce"), "x", "Content attribute is modified after content attribute set");

document.body.appendChild(element);
assert_equals(element.nonce, "x", "IDL attribute is unchanged after element insertion");
assert_equals(element.getAttribute("nonce"), "", "Content attribute is changed after element insertion");
}, `Basic nonce tests for ${localName} in ${namespace} namespace`);

test(t => {
const element = document.createElementNS(namespace_url[namespace], localName);
t.add_cleanup(() => element.remove());
element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");

element.removeAttribute("nonce");
assert_equals(element.nonce, "", "IDL attribute is empty after content attribute removal");
}, `Ensure that removal of content attribute does not affect IDL attribute for ${localName} in ${namespace} namespace`);

test(t => {
const element = document.createElementNS(namespace_url[namespace], localName);
t.add_cleanup(() => element.remove());
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), null);

element.setAttribute("nonce", "");
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), "");

document.body.appendChild(element);
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), "");

element.removeAttribute("nonce");
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), null);
}, `Test empty nonces for ${localName} in ${namespace} namespace`);
});
</script>

0 comments on commit 8b2cf55

Please sign in to comment.