Skip to content

Commit

Permalink
Move <script nonce> hiding to Element.
Browse files Browse the repository at this point in the history
We're evaluating a different approach to hiding the `nonce` content
attribute, moving the behavior change up to `HTMLElement` and `SVGElement`
rather than placing it on `{HTML,SVG}{Script,Style}Element`. This
patch adds `nonce` to `ElementRareData` in order to support that
approach, and wires up a new `NoncedElement` interface to the new
properties.

Still behind a flag while we're working out details.

Intent to Implement and Ship: https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/wu_fMIYkyaQ/85j16Cg6BAAJ

BUG=680419

Review-Url: https://codereview.chromium.org/2801243002
Cr-Commit-Position: refs/heads/master@{#472215}
  • Loading branch information
mikewest authored and chromium-wpt-export-bot committed May 16, 2017
1 parent 9cc4d78 commit 891e76d
Show file tree
Hide file tree
Showing 6 changed files with 476 additions and 0 deletions.
116 changes: 116 additions & 0 deletions content-security-policy/_unapproved/script-nonces-hidden-meta.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<meta http-equiv="content-security-policy" content="script-src 'nonce-abc'; img-src 'none'">

<body>
<!-- Basics -->
<script nonce="abc" id="testScript">
document.currentScript.setAttribute('executed', 'yay');
</script>

<script nonce="abc">
var script = document.querySelector('#testScript');

test(t => {
// Query Selector
assert_equals(document.querySelector('body [nonce]'), script);
assert_equals(document.querySelector('body [nonce=""]'), null);
assert_equals(document.querySelector('body [nonce=abc]'), script);

assert_equals(script.getAttribute('nonce'), 'abc');
assert_equals(script.nonce, 'abc');
}, "Reading 'nonce' content attribute and IDL attribute.");

// Clone node.
test(t => {
script.setAttribute('executed', 'boo');
var s2 = script.cloneNode();
assert_equals(s2.nonce, 'abc', 'IDL attribute');
assert_equals(s2.getAttribute('nonce'), 'abc');
}, "Cloned node retains nonce.");

async_test(t => {
var s2 = script.cloneNode();
document.head.appendChild(s2);
window.addEventListener('load', t.step_func_done(_ => {
assert_equals(s2.nonce, 'abc');
assert_equals(s2.getAttribute('nonce'), 'abc');

// The cloned script won't execute, as its 'already started' flag is set.
assert_equals(s2.getAttribute('executed'), 'boo');
}));
}, "Cloned node retains nonce when inserted.");

// Set the content attribute to 'foo'
test(t => {
script.setAttribute('nonce', 'foo');
assert_equals(script.getAttribute('nonce'), 'foo');
assert_equals(script.nonce, 'abc');
}, "Writing 'nonce' content attribute.");

// Set the IDL attribute to 'bar'
test(t => {
script.nonce = 'bar';
assert_equals(script.nonce, 'bar');
assert_equals(script.getAttribute('nonce'), 'foo');
}, "Writing 'nonce' IDL attribute.");

// Fragment parser.
var documentWriteTest = async_test("Document-written script executes.");
document.write(`<script nonce='abc'>
documentWriteTest.done();
test(t => {
var script = document.currentScript;
assert_equals(script.getAttribute('nonce'), 'abc');
assert_equals(script.nonce, 'abc');
}, "Document-written script's nonce value.");
</scr` + `ipt>`);

// Create node.
async_test(t => {
var s = document.createElement('script');
s.innerText = script.innerText;
s.nonce = 'abc';
document.head.appendChild(s);

window.addEventListener('load', t.step_func_done(_ => {
assert_equals(s.nonce, 'abc');
assert_equals(s.getAttribute('nonce'), null);
assert_equals(s.getAttribute('executed'), 'yay');
}));
}, "createElement.nonce.");

// Create node.
async_test(t => {
var s = document.createElement('script');
s.innerText = script.innerText;
s.setAttribute('nonce', 'abc');
assert_equals(s.getAttribute('nonce'), 'abc', "Pre-insertion content");
assert_equals(s.nonce, '', "Pre-insertion IDL");
document.head.appendChild(s);

window.addEventListener('load', t.step_func_done(_ => {
assert_equals(s.nonce, 'abc', "Post-insertion IDL");
assert_equals(s.getAttribute('nonce'), 'abc', "Post-insertion content");
assert_equals(s.getAttribute('executed'), 'yay');
}));
}, "createElement.setAttribute.");
</script>

<!-- CSS Leakage -->
<style>
#cssTest { display: block; }
#cssTest[nonce=abc] { background: url(/security/resources/abe.png); }
</style>
<script nonce="abc" id="cssTest">
async_test(t => {
requestAnimationFrame(t.step_func_done(_ => {
var script = document.querySelector('#cssTest');
var style = getComputedStyle(script);
assert_equals(style['display'], 'block');
assert_equals(style['background-image'], "url(\"http://web-platform.test:8001/security/resources/abe.png\")");
}));
}, "Nonces leak via CSS side-channels.");
</script>
114 changes: 114 additions & 0 deletions content-security-policy/_unapproved/script-nonces-hidden.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<!DOCTYPE html>
<script src="/resources/testharness.js" nonce="abc"></script>
<script src="/resources/testharnessreport.js" nonce="abc"></script>

<!-- `Content-Security-Policy: script-src 'nonce-abc'; img-src 'none'` delivered via headers -->

<body>
<!-- Basics -->
<script nonce="abc" id="testScript">
document.currentScript.setAttribute('executed', 'yay');
</script>

<script nonce="abc">
var script = document.querySelector('#testScript');

test(t => {
// Query Selector
assert_equals(document.querySelector('body [nonce]'), script);
assert_equals(document.querySelector('body [nonce=""]'), script);
assert_equals(document.querySelector('body [nonce=abc]'), null);

assert_equals(script.getAttribute('nonce'), '');
assert_equals(script.nonce, 'abc');
}, "Reading 'nonce' content attribute and IDL attribute.");

// Clone node.
test(t => {
script.setAttribute('executed', 'boo');
var s2 = script.cloneNode();
assert_equals(s2.nonce, 'abc', 'IDL attribute');
assert_equals(s2.getAttribute('nonce'), '');
}, "Cloned node retains nonce.");

async_test(t => {
var s2 = script.cloneNode();
document.head.appendChild(s2);
window.addEventListener('load', t.step_func_done(_ => {
assert_equals(s2.nonce, 'abc');
assert_equals(s2.getAttribute('nonce'), '');

// The cloned script won't execute, as its 'already started' flag is set.
assert_equals(s2.getAttribute('executed'), 'boo');
}));
}, "Cloned node retains nonce when inserted.");

// Set the content attribute to 'foo'
test(t => {
script.setAttribute('nonce', 'foo');
assert_equals(script.getAttribute('nonce'), 'foo');
assert_equals(script.nonce, 'abc');
}, "Writing 'nonce' content attribute.");

// Set the IDL attribute to 'bar'
test(t => {
script.nonce = 'bar';
assert_equals(script.nonce, 'bar');
assert_equals(script.getAttribute('nonce'), 'foo');
}, "Writing 'nonce' IDL attribute.");

// Fragment parser.
var documentWriteTest = async_test("Document-written script executes.");
document.write(`<script nonce='abc'>
documentWriteTest.done();
test(t => {
var script = document.currentScript;
assert_equals(script.getAttribute('nonce'), '');
assert_equals(script.nonce, 'abc');
}, "Document-written script's nonce value.");
</scr` + `ipt>`);

// Create node.
async_test(t => {
var s = document.createElement('script');
s.innerText = script.innerText;
s.nonce = 'abc';
document.head.appendChild(s);

window.addEventListener('load', t.step_func_done(_ => {
assert_equals(s.nonce, 'abc');
assert_equals(s.getAttribute('nonce'), null);
}));
}, "createElement.nonce.");

// Create node.
async_test(t => {
var s = document.createElement('script');
s.innerText = script.innerText;
s.setAttribute('nonce', 'abc');
assert_equals(s.getAttribute('nonce'), 'abc', "Pre-insertion content");
assert_equals(s.nonce, '', "Pre-insertion IDL");
document.head.appendChild(s);

window.addEventListener('load', t.step_func_done(_ => {
assert_equals(s.nonce, 'abc', "Post-insertion IDL");
assert_equals(s.getAttribute('nonce'), '', "Post-insertion content");
}));
}, "createElement.setAttribute.");
</script>

<!-- CSS Leakage -->
<style>
#cssTest { display: block; }
#cssTest[nonce=abc] { background: url(/security/resources/abe.png); }
</style>
<script nonce="abc" id="cssTest">
async_test(t => {
requestAnimationFrame(t.step_func_done(_ => {
var script = document.querySelector('#cssTest');
var style = getComputedStyle(script);
assert_equals(style['display'], 'block');
assert_equals(style['background-image'], 'none');
}));
}, "Nonces don't leak via CSS side-channels.");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Content-Security-Policy: script-src 'nonce-abc'; img-src 'none'
122 changes: 122 additions & 0 deletions content-security-policy/_unapproved/svgscript-nonces-hidden-meta.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<meta http-equiv="content-security-policy" content="script-src 'nonce-abc'; img-src 'none'">

<body>
<!-- Basics -->
<svg xmlns="http://www.w3.org/2000/svg">
<script nonce="abc" id="testScript">
document.currentScript.setAttribute('executed', 'yay');
</script>
</svg>

<script nonce="abc">
var script = document.querySelector('#testScript');

test(t => {
// Query Selector
assert_equals(document.querySelector('[nonce]'), script);
assert_equals(document.querySelector('[nonce=""]'), null);
assert_equals(document.querySelector('[nonce=abc]'), script);

assert_equals(script.getAttribute('nonce'), 'abc');
assert_equals(script.nonce, 'abc');
}, "Reading 'nonce' content attribute and IDL attribute.");

// Clone node.
test(t => {
script.setAttribute('executed', 'boo');
var s2 = script.cloneNode();
assert_equals(s2.nonce, 'abc', 'IDL attribute');
assert_equals(s2.getAttribute('nonce'), 'abc');
}, "Cloned node retains nonce.");

async_test(t => {
var s2 = script.cloneNode();
document.head.appendChild(s2);
window.addEventListener('load', t.step_func_done(_ => {
assert_equals(s2.nonce, 'abc');
assert_equals(s2.getAttribute('nonce'), 'abc');

// The cloned script won't execute, as its 'already started' flag is set.
assert_equals(s2.getAttribute('executed'), 'boo');
}));
}, "Cloned node retains nonce when inserted.");

// Set the content attribute to 'foo'
test(t => {
script.setAttribute('nonce', 'foo');
assert_equals(script.getAttribute('nonce'), 'foo');
assert_equals(script.nonce, 'abc');
}, "Writing 'nonce' content attribute.");

// Set the IDL attribute to 'bar'
test(t => {
script.nonce = 'bar';
assert_equals(script.nonce, 'bar');
assert_equals(script.getAttribute('nonce'), 'foo');
}, "Writing 'nonce' IDL attribute.");

// Fragment parser.
var documentWriteTest = async_test("Document-written script executes.");
document.write(`<svg xmlns="http://www.w3.org/2000/svg"><script nonce='abc'>
documentWriteTest.done();
test(t => {
var script = document.currentScript;
assert_equals(script.getAttribute('nonce'), 'abc');
assert_equals(script.nonce, 'abc');
}, "Document-written script's nonce value.");
</scr` + `ipt></svg>`);

// Create node.
async_test(t => {
var s = document.createElement('svg');
var innerScript = document.createElement('innerScript');
innerScript.innerText = script.innerText;
innerScript.nonce = 'abc';
s.appendChild(innerScript);
document.body.appendChild(s);

window.addEventListener('load', t.step_func_done(_ => {
assert_equals(innerScript.nonce, 'abc');
assert_equals(innerScript.getAttribute('nonce'), null, 'innerScript.getAttribute nonce');
}));
}, "createElement.nonce.");

// Create node.
async_test(t => {
var s = document.createElement('svg');
var innerScript = document.createElement('script');
innerScript.innerText = script.innerText;
innerScript.setAttribute('nonce', 'abc');
assert_equals(innerScript.getAttribute('nonce'), 'abc', "Pre-insertion content");
assert_equals(innerScript.nonce, '', "Pre-insertion IDL");
s.appendChild(innerScript);
document.body.appendChild(s);

window.addEventListener('load', t.step_func_done(_ => {
assert_equals(innerScript.nonce, 'abc', "Post-insertion IDL");
assert_equals(innerScript.getAttribute('nonce'), 'abc', "Post-insertion content");
}));
}, "createElement.setAttribute.");
</script>

<!-- CSS Leakage -->
<style>
#cssTest { display: block; }
#cssTest[nonce=abc] { background: url(/security/resources/abe.png); }
</style>
<svg xmlns="http://www.w3.org/2000/svg">
<script nonce="abc" id="cssTest">
async_test(t => {
requestAnimationFrame(t.step_func_done(_ => {
var script = document.querySelector('#cssTest');
var style = getComputedStyle(script);
assert_equals(style['display'], 'block');
assert_equals(style['background-image'], "url(\"http://web-platform.test:8001/security/resources/abe.png\")");
}));
}, "Nonces don't leak via CSS side-channels.");
</script>
</svg>
Loading

0 comments on commit 891e76d

Please sign in to comment.