Skip to content

Commit

Permalink
CORB: WPT tests for <script> tag interactions.
Browse files Browse the repository at this point in the history
Bug: 809261, 806996
Change-Id: Ia0f5acdc517f79aa9075447f8b543a141313e098
Reviewed-on: https://chromium-review.googlesource.com/917195
Commit-Queue: Łukasz Anforowicz <lukasza@chromium.org>
Reviewed-by: Nick Carter <nick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#536863}
  • Loading branch information
anforowicz authored and Commit Bot committed Feb 14, 2018
1 parent 6a306a3 commit 926617b
Show file tree
Hide file tree
Showing 15 changed files with 194 additions and 33 deletions.
110 changes: 87 additions & 23 deletions content/browser/loader/cross_origin_read_blocking_explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ All other kinds of requests may be CORB-eligible. This includes:
CSS' `background-image`, etc.
- [script-like destinations](https://fetch.spec.whatwg.org/#request-destination-script-like)
like `<script>`, `importScripts()`, `navigator.serviceWorker.register()`,
`audioWorkler.addModule()`, etc.
`audioWorklet.addModule()`, etc.
- "audio", "video" or "track"
- "font"
- "style"
Expand Down Expand Up @@ -218,6 +218,7 @@ helps meet the goal of protecting as many *documents* as possible.
> protection in the future).
CORB considers `text/plain` to be a *document*.
TODO: application/octet-stream.

> [lukasza@chromium.org] This seems like a one-off in the current
> implementation. Maybe `text/plain` should just be treated as
Expand Down Expand Up @@ -254,15 +255,20 @@ is sometimes wrong. For example, some HTTP servers return a JPEG image with
a `Content-Type` header incorrectly saying `text/html`.

To avoid breaking existing websites, CORB may attempt to confirm if the response
is really a *document* by sniffing the response body:
body really matches the CORB-protected Content-Type response header:

* CORB will only sniff to confirm the classification based on the `Content-Type`
header (i.e. if the `Content-Type` header is `text/json` then CORB will sniff
for JSON and will not sniff for HTML and/or XML).

* CORB will only sniff to confirm if the response body is a HTML, XML or JSON
*document* (and won't attempt to sniff content of other types of *documents*
like PDFs and/or ZIP archives).
* If some syntax elements are shared between CORB-protected and
non-CORB-protected MIME types, then these elements have to be ignored by CORB
sniffing. For example, when sniffing for (CORB-protected) HTML, CORB ignores
and skips HTML comments, because
[they can also be present](http://www.ecma-international.org/ecma-262/6.0/#sec-html-like-comments)
in (non-CORB-protected) JavaScript. This is different from the
[HTML sniffing rules](https://mimesniff.spec.whatwg.org/#rules-for-identifying-an-unknown-mime-type),
used in other contexts.

> [lukasza@chromium.org] It is not practical to try teaching CORB about sniffing
> all possible types of *documents* like `application/pdf`, `application/zip`,
Expand Down Expand Up @@ -357,6 +363,17 @@ HTML's `<canvas>`, etc.
> testing on major websites indicates that most CORB-blocked images are tracking
> pixels and therefore blocking them won't have any observable effect.
> [lukasza@chromium.org] Earlier attempts to block nosniff images with
> incompatible MIME types
> [failed](https://github.com/whatwg/fetch/issues/395).
> We think that CORB will succeed, because
> 1) it will only block a subset of CORB-protected MIME types
> (e.g. it won't block `application/octet-stream` quoted
> in a
> [Firefox bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1302539))
> 2) CORB is an important response to the recent announcement of new
> side-channel attacks like Spectre.

### How CORB interacts with other multimedia?

Expand All @@ -370,27 +387,69 @@ TODO.

### How CORB interacts with scripts?

TODO.

* TODO: **Correctly-labeled HTML document**
* Observable via syntax errors
[GlobalEventHandlers.onerror](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror)
* Add discussion that some JSON parser breakers result in syntax errors and
some don't.
* Resource used in a `<script>` tag: TODO.
CORB should have no observable impact on `<script>` tags except for cases where
a CORB-protected, non-JavaScript resource labeled with its correct MIME type is
loaded as a script - in these cases the resource will usually result in a syntax
error, but CORB-protected response's empty body will result in no error.

* TODO: **Mislabeled script (with sniffing)**
* Non-observable due to sniffing
Examples:

* TODO: **Mislabeled script (nosniff)**
* QUESTION: Will nosniff prevent non-CORB from working? (if yes - great: CORB
has no observable effects in this scenario).
* **Correctly-labeled HTML document**
* Resource used in a `<script>` tag:
* Body: an HTML document
* `Content-Type: text/html`
* No `X-Content-Type-Options` header
* Expected behavior: **observable difference** via
[GlobalEventHandlers.onerror](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror).
Most CORB-protected resources would result in a syntax error when parsed as
JavaScript. The syntax error goes away after CORB blocks a response,
because the empty body of the blocked response parses fine as JavaScript.
* WPT test: `fetch/corb/script-html-correctly-labeled.tentative.sub.html`

> [lukasza@chromium.org] In theory, using a non-empty response in CORB-blocked
> responses might reintroduce the lost syntax error. We didn't go down that
> path, because
> 1) using a non-empty response would be inconsistent with other parts of the
> Fetch spec (like
> [opaque filtered response](https://fetch.spec.whatwg.org/#concept-filtered-response-opaque)).
> 2) retaining the presence of the syntax error might require changing the
> contents of a CORB-blocked response body depending on whether the original
> response body would have caused a syntax error or not. This would add
> extra complexity that seems undesirable both for CORB implementors and for
> web developers.
* **Mislabeled script (with sniffing)**
* Resource used in a `<script>` tag:
* Body: a proper JavaScript script
* `Content-Type: text/html`
* No `X-Content-Type-Options` header
* Expected behavior: **no difference** in behavior with and without CORB.
CORB will sniff that the response body is *not* actually a CORB-protected
type and therefore will allow the response. Note that CORB sniffing is
resilient to the fact that some syntax elements are shared across HTML
and JavaScript (e.g.
[comments](http://www.ecma-international.org/ecma-262/6.0/#sec-html-like-comments)).
* WPT test: `fetch/corb/script-js-mislabeled-as-html.sub.html`

* **Mislabeled script (nosniff)**
* Resource used in a `<script>` tag:
* Body: a proper JavaScript script
* `Content-Type: text/html`
* `X-Content-Type-Options: nosniff`
* Expected behavior: **no observable difference** in behavior with and without CORB.
Both with and without CORB, the script will not execute, because the
`nosniff` response header response will cause the response to be blocked
when its MIME type (`text/html` in the example) is not a
[JavaScript MIME type](https://html.spec.whatwg.org/multipage/scripting.html#javascript-mime-type)
(this behavior is required by the
[Fetch spec](https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?)).
* WPT test: `fetch/corb/script-js-mislabeled-as-html-nosniff.sub.html`

In addition to the HTML `<script>` tag, the examples above should apply to other
web features that consume JavaScript including
[script-like destinations](https://fetch.spec.whatwg.org/#request-destination-script-like)
like `importScripts()`, `navigator.serviceWorker.register()`,
`audioWorkler.addModule()`, etc.
`audioWorklet.addModule()`, etc.


### How CORB interacts with stylesheets?
Expand All @@ -405,14 +464,19 @@ TODO.
* Non-observable due to sniffing?

* TODO: **Mislabeled stylesheet (nosniff)**
* QUESTION: Will nosniff prevent non-CORB from working? (if yes - great: CORB
has no observable effects in this scenario).
* Expected behavior: **no observable difference** in behavior with and without CORB.
Both with and without CORB, the stylesheet will not load, because the
`nosniff` response header response will cause the response to be blocked
when its MIME type (`text/html` in the example) is not `text/css`
(this behavior is required by the
[Fetch spec](https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?)).

* TODO: **Correctly-labeled stylesheet with JSON parser breaker**
* Non-observable, because JSON-parser-breaking is not performed for text/css

* TODO: **Polyglot HTML/CSS labeled as text/html**.
* QUESTION: text/html will be rejected even without CORB?
* QUESTION: cross-origin text/html will be rejected even without CORB and
without nosniff?
* Example:
```css
<!DOCTYPE html>
Expand Down Expand Up @@ -559,6 +623,6 @@ CORB demo page: https://anforowicz.github.io/xsdb-demo/index.html
* https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
* https://fetch.spec.whatwg.org/#http-cors-protocol
* https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff
* https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?
* https://fetch.spec.whatwg.org/#x-content-type-options-header
* https://tools.ietf.org/html/rfc7233#section-4 (Responses to a Range Request)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# TODO(lukasza): Remove these once CORB is enabled by default.
crbug.com/802835 external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Pass ]
crbug.com/802835 external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Pass ]

# https://crbug.com/794631: Recent regression.
crbug.com/794631 virtual/unified-autoplay/external/wpt/feature-policy/autoplay-allowed-by-feature-policy-attribute.https.sub.html [ Failure ]
Expand Down
2 changes: 2 additions & 0 deletions third_party/WebKit/LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -3282,3 +3282,5 @@ crbug.com/794338 media/video-rotation.html [ Skip ]
# TODO(lukasza): Remove these once CORB is enabled by default.
crbug.com/802835 external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Failure ]
crbug.com/802835 virtual/outofblink-cors/external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Failure ]
crbug.com/802835 external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Failure ]
crbug.com/802835 virtual/outofblink-cors/external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Failure ]
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<!doctype html>
<meta charset="utf-8">
<title>CORB should not block text/css with a JSON parser breaker</title>
<link rel="stylesheet" type="text/css"
href="http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/css-with-json-parser-breaker.css">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>

<!-- www1 is cross-origin, so the HTTP response is CORB-eligible -->
<link rel="stylesheet" type="text/css"
href="http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/css-with-json-parser-breaker.css">

<body>
<h1 id="header">Header example</h1>
<p>Paragraph body</p>
</body>

<script>
test(function() {
var style = getComputedStyle(document.getElementById('header'));
assert_equals(style.getPropertyValue('color'), 'rgb(255, 0, 0)');
}, "CORB should not block text/css with a JSON parser breaker");
var style = getComputedStyle(document.getElementById('header'));
assert_equals(style.getPropertyValue('color'), 'rgb(255, 0, 0)');
done();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
window.has_executed_script = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Content-Type: text/html
X-Content-Type-Options: nosniff
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
window.has_executed_script = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Content-Type: text/html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<!-- Test verifies that html fed to a <script> tag won't report a syntax
error after CORB blocks the response (an empty response body injected
by CORB won't have any JavaScript syntax errors).
-->
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>
<script>
setup({allow_uncaught_exception : true});
async_test(function(t) {
var script = document.createElement("script")

// Without CORB, the html document would cause a syntax error when parsed as
// JavaScript, but with CORB there should be no errors (because CORB will
// replace the response body with an empty body).
script.onload = t.step_func_done(function(){})
addEventListener("error",function(e) {
t.step(function() {
assert_unreached("Empty body of a CORS-blocked response shouldn't trigger syntax errors.");
t.done();
})
});

// www1 is cross-origin, so the HTTP response is CORB-eligible.
script.src = 'http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/html-correctly-labeled.html';
document.body.appendChild(script)
});
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<!-- Test verifies that script mislabeled as html won't execute with and without CORB
if the nosniff response header is present.
The expected behavior is covered by the Fetch spec at
https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?
See also the following tests:
- fetch/nosniff/importscripts.html
- fetch/nosniff/script.html
- fetch/nosniff/worker.html
-->
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>

<script>
window.has_executed_script = false;
</script>

<!-- www1 is cross-origin, so the HTTP response is CORB-eligible -->
<script src="http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/js-mislabeled-as-html-nosniff.js">
</script>

<script>
// Verify what observable effects the <script> tag above had.
// Assertion should hold with and without CORB:
assert_false(window.has_executed_script,
'The cross-origin script should not be executed');
done();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<!-- Test verifies that script mislabeled as html will execute with and without
CORB (CORB should allow the script after sniffing).
-->
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>

<script>
window.has_executed_script = false;
</script>

<!-- www1 is cross-origin, so the HTTP response is CORB-eligible -->
<script src="http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/js-mislabeled-as-html.js">
</script>

<script>
// Verify what observable effects the <script> tag above had.
// Assertion should hold with and without CORB:
assert_true(window.has_executed_script,
'The cross-origin script should execute');
done();
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const get_url = (mime, outcome) => {
return url
}

[null, "", "x", "x/x"].forEach(function(mime) {
[null, "", "x", "x/x", "text/html", "text/json"].forEach(function(mime) {
try {
importScripts(get_url(mime))
} catch(e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script>
var log = function() {}, // see comment below
p = function() {}, // see comment below
fails = [null, "", "x", "x/x"],
fails = [null, "", "x", "x/x", "text/html", "text/json"],
passes = ["text/javascript", "text/ecmascript", "text/ecmascript;blah"]

// Ideally we'd also check whether the scripts in fact execute, but that would involve
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<script src=/resources/testharnessreport.js></script>
<div id=log></div>
<script>
var fails = [null, "", "x", "x/x"],
var fails = [null, "", "x", "x/x", "text/html", "text/json"],
passes = ["text/css", "text/css;charset=utf-8", "text/css;blah"]

const get_url = (mime) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div id=log></div>
<script>
var workers = [],
fails = ["", "?type=", "?type=x", "?type=x/x"],
fails = ["", "?type=", "?type=x", "?type=x/x", "?type=text/html", "?type=text/json"],
passes = ["?type=text/javascript", "?type=text/ecmascript", "?type=text/ecmascript;yay"]

fails.forEach(function(urlpart) {
Expand Down

0 comments on commit 926617b

Please sign in to comment.