Bug 1564656 [wpt PR 17426] - Add Preload + SRI web platform tests for script + style destinations, a=testonly

Automatic update from web-platform-tests
Add Preload + SRI web platform tests for script + style destinations

More preload destinations will be added after this CL, as well as the
main Preload + SRI implementation, to keep the changes here minimal.

R=kouhei@chromium.org, yhirano@chromium.org, yoavweiss@chromium.org

Bug: 677022
Change-Id: I596ac006874fb02bcd330422bd6496a05d388d45
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1669954
Commit-Queue: Dominic Farolino <dom@chromium.org>
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672808}

--

wpt-commits: f0c2701197a8dc5682b0361698b73a0be9fd983c
wpt-pr: 17426
This commit is contained in:
Dominic Farolino 2019-07-19 18:15:58 +00:00 коммит произвёл James Graham
Родитель 2a662aaffe
Коммит 681c329519
3 изменённых файлов: 391 добавлений и 6 удалений

Просмотреть файл

@ -0,0 +1,284 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Subresource Integrity</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/sriharness.js"></script>
<script src="/common/utils.js"></script>
<script src="/subresource-integrity/sri-test-helpers.sub.js"></script>
<div id="log"></div>
<div id="container"></div>
<script>
// This is a list of information for each preload destination. The information
// is used in a loop iterating over the below tests, so that each test is run
// for each destination.
const preload_destination_info = [
{
destination: 'script', ext: '.js', supports_sri: true,
sha256: 'sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA=',
sha384: 'sha384-cINXh+nCzEHPWzXS7eoT+vYMBpyqczOybRLNU3XAButFWCRhHT5hLByIbPRqIm2f',
sha512: 'sha512-KZdenhzBd7X7Q/vmaOSyvFz1CGdoVt26xzCZjlkU9lfBEK+V/ougGys7iYDi0+tOHIQSQa87bIqx95R7GU7I9Q=='
},
{
destination: 'style', ext: '.css', supports_sri: true,
sha256: 'sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=',
sha384: 'sha384-wDAWxH4tOWBwAwHfBn9B7XuNmFxHTMeigAMwn0iVQ0zq3FtmYMLxihcGnU64CwcX',
sha512: 'sha512-9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2HSZX4l6w=='
},
// TODO(domfarolino): Add more destinations.
];
for (const info of preload_destination_info) {
const {destination, ext, supports_sri, sha256, sha384, sha512} = info;
// Preload + Subresource Integrity tests. These tests work by passing some
// destination-specific information (defined in |preload_destination_info|)
// to the below tests, which do the following:
// Create a <link rel="preload"> for the given destination, with the
// specified `integrity`. After this has either loaded or failed to load,
// the subresource element corresponding to |destination| will be created,
// attempting to re-use the preloaded resource. `integrity` may be specified
// on the subresource elements that support SRI as well. The subresource
// will either load or fail to load, and the result will be compared with an
// expectation passed to the test.
SRIPreloadTest(
true, /* preload_sri_success */
true, /* subresource_sri_success */
`Same-origin ${destination} with correct sha256 hash.`, /* name */
destination, /* destination */
same_origin_prefix + destination + ext + `?${token()}`, /* resource_url (for preload + subresource) */
{integrity: sha256}, /* link_attrs */
{} /* subresource_attrs */
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with correct sha384 hash.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: sha384},
{}
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with correct sha512 hash.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: sha512},
{}
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with empty integrity.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{},
{}
)
SRIPreloadTest(
false,
false,
`Same-origin ${destination} with incorrect hash.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"},
{}
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with multiple sha256 hashes, including correct.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: `${sha256} sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead`},
{}
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with multiple sha256 hashes, including unknown algorithm.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: `${sha256} foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead`},
{}
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with sha256 mismatch, sha512 match`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: `${sha512} sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead`},
{}
)
SRIPreloadTest(
false,
false,
`Same-origin ${destination} with sha256 match, sha512 mismatch`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: `sha512-deadbeefspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg== ${sha256}`},
{}
)
SRIPreloadTest(
true,
true,
`<crossorigin='anonymous'> ${destination} with correct hash, ACAO: *`,
destination,
xorigin_prefix + destination + ext + `?${token()}` + anonymous,
{integrity: sha256, crossOrigin: 'anonymous'},
{crossOrigin: "anonymous"}
)
SRIPreloadTest(
false,
false,
`<crossorigin='anonymous'> ${destination} with incorrect hash, ACAO: *`,
destination,
xorigin_prefix + destination + ext + `?${token()}` + anonymous,
{integrity: "sha256-sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead", crossOrigin: "anonymous"},
{crossOrigin: "anonymous"}
)
SRIPreloadTest(
true,
true,
`<crossorigin='use-credentials'> ${destination} with correct hash, CORS-eligible`,
destination,
xorigin_prefix + destination + ext + `?${token()}` + use_credentials,
{integrity: sha256, crossOrigin: "use-credentials"},
{crossOrigin: "use-credentials"}
)
SRIPreloadTest(
false,
false,
`<crossorigin='use-credentials'> ${destination} with incorrect hash CORS-eligible`,
destination,
xorigin_prefix + destination + ext + `?${token()}` + use_credentials,
{integrity: "sha256-deadbeef2S+pTRZgiw3DWrhC6JLDlt2zRyGpwH7unU8=", crossOrigin: "use-credentials"},
{crossOrigin: "use-credentials"}
)
SRIPreloadTest(
false,
false,
`<crossorigin='anonymous'> ${destination} with CORS-ineligible resource`,
destination,
// not piping ACAO header makes this CORS-ineligible
xorigin_prefix + destination + ext + `?${token()}`,
{integrity: sha256, crossOrigin: "anonymous"},
{crossOrigin: "anonymous"}
)
SRIPreloadTest(
false,
false,
`Cross-origin ${destination}, not CORS request, with correct hash`,
destination,
xorigin_prefix + destination + ext + `?${token()}` + anonymous,
{integrity: sha256},
{}
)
SRIPreloadTest(
false,
false,
`Cross-origin ${destination}, not CORS request, with hash mismatch`,
destination,
xorigin_prefix + destination + ext + `?${token()}` + anonymous,
{integrity: "sha256-deadbeef01Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0="},
{}
)
SRIPreloadTest(
true,
true,
`Cross-origin ${destination}, empty integrity`,
destination,
xorigin_prefix + destination + ext + `?${token()}` + anonymous,
{},
{}
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with correct hash, options.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: `${sha256}?foo=bar?spam=eggs`},
{}
)
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with unknown algorithm only.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: "foo666-8aBiAJl3ukQwSJ6eTs5wl6hGjnOtyXjcTRdAf89uIfY="},
{}
)
// The below tests are specific to subresource destinations that support
// SRI. See |supports_sri|.
if (supports_sri) {
SRIPreloadTest(
true,
true,
`Same-origin ${destination} with matching digest re-uses preload with matching digest.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: sha256},
{integrity: sha256}
)
SRIPreloadTest(
true,
false,
`Same-origin ${destination} with non-matching digest does not re-use preload with matching digest.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: sha256},
{integrity: "sha256-deadbeefQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="}
)
SRIPreloadTest(
false,
true,
`Same-origin ${destination} with matching digest does not re-use preload with non-matching digest.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: "sha256-deadbeefQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="},
{integrity: sha256}
)
SRIPreloadTest(
false,
false,
`Same-origin ${destination} with non-matching digest does not re-use preload with non-matching digest.`,
destination,
same_origin_prefix + destination + ext + `?${token()}`,
{integrity: "sha256-deadbeefQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="},
{integrity: "sha256-deaddeadbeefYHFvsYdWumweeFAw0hJDTFt9seErghA="}
)
} // if.
} // for-of.
</script>

Просмотреть файл

@ -1,3 +1,5 @@
// TODO(domfarolino): Refactor SRIScriptTest to just be a function instead of a
// constructor, since there is no need to produce another object.
var SRIScriptTest = function(pass, name, src, integrityValue, crossoriginValue, nonce) {
this.pass = pass;
this.name = "Script: " + name;
@ -32,6 +34,98 @@ SRIScriptTest.prototype.execute = function() {
document.body.appendChild(e);
};
function buildElementFromDestination(resource_url, destination, attrs) {
// Assert: |destination| is a valid destination.
let element;
// The below switch is responsible for:
// 1. Creating the correct subresource element
// 2. Setting said element's href, src, or fetch-instigating property
// appropriately.
switch (destination) {
case "script":
element = document.createElement(destination);
element.src = resource_url;
break;
case "style":
element = document.createElement('link');
element.rel = 'stylesheet';
element.href = resource_url;
break;
case "image":
element = document.createElement('img');
element.src = resource_url;
break;
default:
assert_unreached("INVALID DESTINATION");
}
// Apply the rest of the attributes, if any.
for (const [attr_name, attr_val] of Object.entries(attrs)) {
element[attr_name] = attr_val;
}
return element;
}
const SRIPreloadTest = (preload_sri_success, subresource_sri_success, name,
destination, resource_url, link_attrs,
subresource_attrs) => {
const test = async_test(name);
const link = document.createElement('link');
// Early-fail in UAs that do not support `preload` links.
test.step_func(() => {
assert_true(link.relList.supports('preload'), "Clever message here.");
})();
// Build up the link.
link.rel = 'preload';
link.as = destination;
link.href = resource_url;
for (const [attr_name, attr_val] of Object.entries(link_attrs)) {
link[attr_name] = attr_val; // This may override `rel` to modulepreload.
}
// Preload + subresource success and failure loading functions.
const valid_preload_failed = test.step_func(() =>
{ assert_unreached("Valid preload fired error handler.") });
const invalid_preload_succeeded = test.step_func(() =>
{ assert_unreached("Invalid preload load succeeded.") });
const valid_subresource_failed = test.step_func(() =>
{ assert_unreached("Valid subresource fired error handler.") });
const invalid_subresource_succeeded = test.step_func(() =>
{ assert_unreached("Invalid subresource load succeeded.") });
const subresource_pass = test.step_func(() => { test.done(); });
const preload_pass = test.step_func(() => {
const subresource_element = buildElementFromDestination(
resource_url,
destination,
subresource_attrs
);
if (subresource_sri_success) {
subresource_element.onload = subresource_pass;
subresource_element.onerror = valid_subresource_failed;
} else {
subresource_element.onload = invalid_subresource_succeeded;
subresource_element.onerror = subresource_pass;
}
document.body.append(subresource_element);
});
if (preload_sri_success) {
link.onload = preload_pass;
link.onerror = valid_preload_failed;
} else {
link.onload = invalid_preload_succeeded;
link.onerror = preload_pass;
}
document.head.append(link);
}
// <link> tests
// Style tests must be done synchronously because they rely on the presence
// and absence of global style, which can affect later tests. Thus, instead
@ -63,6 +157,8 @@ SRIStyleTest.prototype.execute = function() {
var div = document.createElement("div");
div.className = "testdiv";
var e = document.createElement("link");
// The link relation is guaranteed to not be "preload" or "modulepreload".
this.attrs.rel = this.attrs.rel || "stylesheet";
for (var key in this.attrs) {
if (this.attrs.hasOwnProperty(key)) {

Просмотреть файл

@ -9,19 +9,24 @@
// Thus, we only want the Access-Control-Allow-Origin header to have
// the port if it's not port 80 or 443, since the user agent will elide the
// ports in those cases.
const main_domain = "{{domains[]}}";
const www_domain = "{{domains[www]}}";
const default_port = (location.protocol === "https:") ? "{{ports[https][0]}}" :
"{{ports[http][0]}}";
const main_domain = '{{domains[]}}';
const www_domain = '{{domains[www]}}';
const default_port = (location.protocol === 'https:') ? '{{ports[https][0]}}' :
'{{ports[http][0]}}';
const port_string = (default_port !== "80" && default_port !== "443") ?
`:${default_port}` : "";
const port_string = (default_port !== '80' && default_port !== '443') ?
`:${default_port}` : '';
const www_host_and_port = www_domain + port_string;
// General resource prefixes.
const same_origin_prefix = '/subresource-integrity/';
const xorigin_prefix = `${location.protocol}//${www_host_and_port}/subresource-integrity/`;
// General resource suffixes, for piping CORS headers.
const anonymous = '&pipe=header(Access-Control-Allow-Origin,*)';
const use_credentials = "&pipe=header(Access-Control-Allow-Credentials,true)|" +
"header(Access-Control-Allow-Origin," + location.origin + ")";
// Note that all of these style URLs have query parameters started, so any
// additional parameters should be appended starting with '&'.
const xorigin_anon_style = location.protocol