Skip to content

Commit 05d533e

Browse files
panvaaduh95
authored andcommitted
worker: convert importScripts arguments first
Web IDL converts every argument before entering the method algorithm. Perform all USVString conversions before rejecting module workers or parsing URLs, so later conversions can throw or revoke blob URLs first. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: #66354 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
1 parent 230cd0c commit 05d533e

2 files changed

Lines changed: 73 additions & 2 deletions

File tree

‎lib/internal/webworker.js‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,11 @@ class WorkerGlobalScope extends EventTarget {
507507
importScripts(...urls) {
508508
validateThisInternalField(this, kLocation, 'WorkerGlobalScope');
509509
const prefix = "Failed to execute 'importScripts' on 'WorkerGlobalScope'";
510+
// Web IDL converts every argument before running the method steps.
511+
for (let i = 0; i < urls.length; i++) {
512+
urls[i] = converters.USVString(
513+
urls[i], { prefix, context: `Argument ${i + 1}` });
514+
}
510515
// "To import scripts into worker global scope, given a
511516
// WorkerGlobalScope object worker global scope, a list of scalar value
512517
// strings urls, and an optional perform the fetch hook performFetch:"
@@ -525,8 +530,7 @@ class WorkerGlobalScope extends EventTarget {
525530
const urlRecords = [];
526531
// "For each url of urls:"
527532
for (let i = 0; i < urls.length; i++) {
528-
const url = converters.USVString(
529-
urls[i], { prefix, context: `Argument ${i + 1}` });
533+
const url = urls[i];
530534
// "Let urlRecord be the result of encoding-parsing a URL given url,
531535
// relative to settings object."
532536
const urlRecord = URLParse(url, this[kLocation].href);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Flags: --experimental-web-worker
2+
'use strict';
3+
4+
const common = require('../common');
5+
const assert = require('node:assert');
6+
7+
for (const type of ['classic', 'module']) {
8+
const source = `
9+
const results = [];
10+
const conversions = [];
11+
const sentinel = new Error('conversion');
12+
try {
13+
importScripts('https://[', {
14+
toString() { conversions.push('converted'); throw sentinel; }
15+
});
16+
} catch (error) {
17+
results.push([conversions, error === sentinel]);
18+
}
19+
const order = [];
20+
try {
21+
importScripts(
22+
{ toString() { order.push(1); return 'https://['; } },
23+
{ toString() { order.push(2); return 'data:text/javascript,'; } }
24+
);
25+
} catch (error) {
26+
results.push([order, error.name]);
27+
}
28+
postMessage(results);
29+
`;
30+
const worker = new Worker(`data:text/javascript,${encodeURIComponent(source)}`, { type });
31+
worker.onerror = common.mustNotCall('worker failed');
32+
worker.onmessage = common.mustCall(({ data }) => {
33+
worker.terminate();
34+
assert.deepStrictEqual(data, [
35+
[['converted'], true],
36+
[[1, 2], type === 'module' ? 'TypeError' : 'SyntaxError'],
37+
]);
38+
});
39+
}
40+
41+
if (common.hasCrypto) {
42+
// URL parsing must capture blob entries after all arguments are converted.
43+
const source = `
44+
self.ran = false;
45+
const url = URL.createObjectURL(new Blob(['self.ran = true'], {
46+
type: 'text/javascript'
47+
}));
48+
let errorName;
49+
try {
50+
importScripts(url, {
51+
toString() {
52+
URL.revokeObjectURL(url);
53+
return 'data:text/javascript,';
54+
}
55+
});
56+
} catch (error) {
57+
errorName = error.name;
58+
}
59+
postMessage([self.ran, errorName]);
60+
`;
61+
const worker = new Worker(`data:text/javascript,${encodeURIComponent(source)}`);
62+
worker.onerror = common.mustNotCall('worker failed');
63+
worker.onmessage = common.mustCall(({ data }) => {
64+
worker.terminate();
65+
assert.deepStrictEqual(data, [false, 'NetworkError']);
66+
});
67+
}

0 commit comments

Comments
 (0)