Skip to content

Commit 5cb2eed

Browse files
KhafraDevronag
authored andcommitted
wpt: add integrity.sub.any.js
1 parent 75c3ce3 commit 5cb2eed

File tree

6 files changed

+100
-8
lines changed

6 files changed

+100
-8
lines changed

lib/fetch/util.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,11 @@ function bytesMatch (bytes, metadataList) {
532532

533533
// 4. Let metadata be the result of getting the strongest
534534
// metadata from parsedMetadata.
535-
// Note: this will only work for SHA- algorithms and it's lazy *at best*.
536-
const metadata = parsedMetadata.sort((c, d) => d.algo.localeCompare(c.algo))
535+
const list = parsedMetadata.sort((c, d) => d.algo.localeCompare(c.algo))
536+
// get the strongest algorithm
537+
const strongest = list[0].algo
538+
// get all entries that use the strongest algorithm; ignore weaker
539+
const metadata = list.filter((item) => item.algo === strongest)
537540

538541
// 5. For each item in metadata:
539542
for (const item of metadata) {
@@ -559,10 +562,9 @@ function bytesMatch (bytes, metadataList) {
559562
}
560563

561564
// https://w3c.github.io/webappsec-subresource-integrity/#grammardef-hash-with-options
562-
// hash-algo is defined in Content Security Policy 2 Section 4.2
563-
// base64-value is similary defined there
564-
// VCHAR is defined https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1
565-
const parseHashWithOptions = /((?<algo>sha256|sha384|sha512)-(?<hash>[A-z0-9+/]{1}.*={1,2}))( +[\x21-\x7e]?)?/i
565+
// https://www.w3.org/TR/CSP2/#source-list-syntax
566+
// https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1
567+
const parseHashWithOptions = /((?<algo>sha256|sha384|sha512)-(?<hash>[A-z0-9+/]{1}.*={0,2}))( +[\x21-\x7e]?)?/i
566568

567569
/**
568570
* @see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata

test/wpt/runner/runner/runner.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ export class WPTRunner extends EventEmitter {
8181
const workerPath = fileURLToPath(join(import.meta.url, '../worker.mjs'))
8282

8383
for (const test of this.#files) {
84-
const code = readFileSync(test, 'utf-8')
84+
const code = test.includes('.sub.')
85+
? handlePipes(readFileSync(test, 'utf-8'), this.#url)
86+
: readFileSync(test, 'utf-8')
8587
const meta = this.resolveMeta(code, test)
8688

8789
const worker = new Worker(workerPath, {

test/wpt/runner/runner/util.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export function handlePipes (code, url) {
110110
// "The port number of servers, by protocol e.g. {{ports[http][0]}}
111111
// for the first (and, depending on setup, possibly only) http server"
112112
case 'ports': {
113-
return server.port
113+
return `${server.port}/`
114114
}
115115
default: {
116116
throw new TypeError(`Unknown substitute "${sub}".`)

test/wpt/server/server.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const server = createServer(async (req, res) => {
3131
const fullUrl = new URL(req.url, `http://localhost:${server.address().port}`)
3232

3333
switch (fullUrl.pathname) {
34+
case '/fetch/api/resources/top.txt':
3435
case '/mimesniff/mime-types/resources/generated-mime-types.json':
3536
case '/mimesniff/mime-types/resources/mime-types.json':
3637
case '/interfaces/dom.idl':
@@ -222,6 +223,11 @@ const server = createServer(async (req, res) => {
222223
case '/fetch/connection-pool/resources/network-partition-key.py': {
223224
return route(req, res, fullUrl)
224225
}
226+
case '/resources/top.txt': {
227+
return createReadStream(join(tests, 'fetch/api/', fullUrl.pathname))
228+
.on('end', () => res.end())
229+
.pipe(res)
230+
}
225231
default: {
226232
res.statusCode = 200
227233
res.end('body')

test/wpt/status/fetch.status.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,10 @@
6262
"response.headers.get('foo-test') expects 1, 2, 3",
6363
"response.headers.get('heya') expects , \\x0B\f, 1, , , 2"
6464
]
65+
},
66+
"integrity.sub.any.js": {
67+
"fail": [
68+
"Empty string integrity for opaque response"
69+
]
6570
}
6671
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// META: global=window,dedicatedworker,sharedworker
2+
// META: script=../resources/utils.js
3+
4+
function integrity(desc, url, integrity, initRequestMode, shouldPass) {
5+
var fetchRequestInit = {'integrity': integrity}
6+
if (!!initRequestMode && initRequestMode !== "") {
7+
fetchRequestInit.mode = initRequestMode;
8+
}
9+
10+
if (shouldPass) {
11+
promise_test(function(test) {
12+
return fetch(url, fetchRequestInit).then(function(resp) {
13+
if (initRequestMode !== "no-cors") {
14+
assert_equals(resp.status, 200, "Response's status is 200");
15+
} else {
16+
assert_equals(resp.status, 0, "Opaque response's status is 0");
17+
assert_equals(resp.type, "opaque");
18+
}
19+
});
20+
}, desc);
21+
} else {
22+
promise_test(function(test) {
23+
return promise_rejects_js(test, TypeError, fetch(url, fetchRequestInit));
24+
}, desc);
25+
}
26+
}
27+
28+
const topSha256 = "sha256-KHIDZcXnR2oBHk9DrAA+5fFiR6JjudYjqoXtMR1zvzk=";
29+
const topSha384 = "sha384-MgZYnnAzPM/MjhqfOIMfQK5qcFvGZsGLzx4Phd7/A8fHTqqLqXqKo8cNzY3xEPTL";
30+
const topSha512 = "sha512-D6yns0qxG0E7+TwkevZ4Jt5t7Iy3ugmAajG/dlf6Pado1JqTyneKXICDiqFIkLMRExgtvg8PlxbKTkYfRejSOg==";
31+
const invalidSha256 = "sha256-dKUcPOn/AlUjWIwcHeHNqYXPlvyGiq+2dWOdFcE+24I=";
32+
const invalidSha512 = "sha512-oUceBRNxPxnY60g/VtPCj2syT4wo4EZh2CgYdWy9veW8+OsReTXoh7dizMGZafvx9+QhMS39L/gIkxnPIn41Zg==";
33+
34+
const path = dirname(location.pathname) + RESOURCES_DIR + "top.txt";
35+
const url = path;
36+
const corsUrl =
37+
`http://{{host}}:{{ports[http][1]}}${path}?pipe=header(Access-Control-Allow-Origin,*)`;
38+
const corsUrl2 = `https://{{host}}:{{ports[https][0]}}${path}`
39+
40+
integrity("Empty string integrity", url, "", /* initRequestMode */ undefined,
41+
/* shouldPass */ true);
42+
integrity("SHA-256 integrity", url, topSha256, /* initRequestMode */ undefined,
43+
/* shouldPass */ true);
44+
integrity("SHA-384 integrity", url, topSha384, /* initRequestMode */ undefined,
45+
/* shouldPass */ true);
46+
integrity("SHA-512 integrity", url, topSha512, /* initRequestMode */ undefined,
47+
/* shouldPass */ true);
48+
integrity("Invalid integrity", url, invalidSha256,
49+
/* initRequestMode */ undefined, /* shouldPass */ false);
50+
integrity("Multiple integrities: valid stronger than invalid", url,
51+
invalidSha256 + " " + topSha384, /* initRequestMode */ undefined,
52+
/* shouldPass */ true);
53+
integrity("Multiple integrities: invalid stronger than valid",
54+
url, invalidSha512 + " " + topSha384, /* initRequestMode */ undefined,
55+
/* shouldPass */ false);
56+
integrity("Multiple integrities: invalid as strong as valid", url,
57+
invalidSha512 + " " + topSha512, /* initRequestMode */ undefined,
58+
/* shouldPass */ true);
59+
integrity("Multiple integrities: both are valid", url,
60+
topSha384 + " " + topSha512, /* initRequestMode */ undefined,
61+
/* shouldPass */ true);
62+
integrity("Multiple integrities: both are invalid", url,
63+
invalidSha256 + " " + invalidSha512, /* initRequestMode */ undefined,
64+
/* shouldPass */ false);
65+
integrity("CORS empty integrity", corsUrl, "", /* initRequestMode */ undefined,
66+
/* shouldPass */ true);
67+
integrity("CORS SHA-512 integrity", corsUrl, topSha512,
68+
/* initRequestMode */ undefined, /* shouldPass */ true);
69+
integrity("CORS invalid integrity", corsUrl, invalidSha512,
70+
/* initRequestMode */ undefined, /* shouldPass */ false);
71+
72+
integrity("Empty string integrity for opaque response", corsUrl2, "",
73+
/* initRequestMode */ "no-cors", /* shouldPass */ true);
74+
integrity("SHA-* integrity for opaque response", corsUrl2, topSha512,
75+
/* initRequestMode */ "no-cors", /* shouldPass */ false);
76+
77+
done();

0 commit comments

Comments
 (0)