Skip to content

Commit f3179f7

Browse files
bmecktargos
authored andcommitted
policy: ensure workers do not read fs for policy
This prevents a main thread from rewriting the policy file and loading a worker that has a different policy from the main thread. PR-URL: #25710 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent ebcdbeb commit f3179f7

File tree

5 files changed

+138
-16
lines changed

5 files changed

+138
-16
lines changed

‎lib/internal/bootstrap/node.js‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ function startup(){
173173

174174
// TODO(joyeecheung): move this down further to get better snapshotting
175175
constexperimentalPolicy=getOptionValue('--experimental-policy');
176-
if(experimentalPolicy){
176+
if(isMainThread&&experimentalPolicy){
177177
process.emitWarning('Policies are experimental.',
178178
'ExperimentalWarning');
179179
const{ pathToFileURL,URL}=NativeModule.require('url');
@@ -364,8 +364,6 @@ function startup(){
364364
}
365365

366366
functionstartWorkerThreadExecution(){
367-
prepareUserCodeExecution();
368-
369367
// If we are in a worker thread, execute the script sent through the
370368
// message port.
371369
const{
@@ -382,7 +380,9 @@ function startWorkerThreadExecution(){
382380
debug(`[${threadId}] is setting up worker child environment`);
383381

384382
constport=getEnvMessagePort();
385-
port.on('message',createMessageHandler(port));
383+
port.on('message',createMessageHandler(
384+
port,
385+
prepareUserCodeExecution));
386386
port.start();
387387

388388
// Overwrite fatalException
@@ -476,7 +476,7 @@ function prepareUserCodeExecution(){
476476

477477
// For user code, we preload modules if `-r` is passed
478478
constpreloadModules=getOptionValue('--require');
479-
if(preloadModules){
479+
if(preloadModules.length){
480480
const{
481481
_preloadModules
482482
}=NativeModule.require('internal/modules/cjs/loader');

‎lib/internal/process/policy.js‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ const{
55
}=require('internal/errors').codes;
66
const{ Manifest }=require('internal/policy/manifest');
77
letmanifest;
8+
letmanifestSrc;
9+
letmanifestURL;
810

911
module.exports=Object.freeze({
1012
__proto__: null,
1113
setup(src,url){
14+
manifestSrc=src;
15+
manifestURL=url;
1216
if(src===null){
1317
manifest=null;
1418
return;
@@ -31,6 +35,20 @@ module.exports = Object.freeze({
3135
returnmanifest;
3236
},
3337

38+
getsrc(){
39+
if(typeofmanifestSrc==='undefined'){
40+
thrownewERR_MANIFEST_TDZ();
41+
}
42+
returnmanifestSrc;
43+
},
44+
45+
geturl(){
46+
if(typeofmanifestURL==='undefined'){
47+
thrownewERR_MANIFEST_TDZ();
48+
}
49+
returnmanifestURL;
50+
},
51+
3452
assertIntegrity(moduleURL,content){
3553
this.manifest.matchesIntegrity(moduleURL,content);
3654
}

‎lib/internal/process/worker_thread_only.js‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,24 @@ function initializeWorkerStdio(){
4343
};
4444
}
4545

46-
functioncreateMessageHandler(port){
46+
functioncreateMessageHandler(port,prepareUserCodeExecution){
4747
constpublicWorker=require('worker_threads');
4848

4949
returnfunction(message){
5050
if(message.type===messageTypes.LOAD_SCRIPT){
51-
const{ filename, doEval, workerData, publicPort, hasStdin }=message;
51+
const{
52+
filename,
53+
doEval,
54+
workerData,
55+
publicPort,
56+
manifestSrc,
57+
manifestURL,
58+
hasStdin
59+
}=message;
60+
if(manifestSrc){
61+
require('internal/process/policy').setup(manifestSrc,manifestURL);
62+
}
63+
prepareUserCodeExecution();
5264
publicWorker.parentPort=publicPort;
5365
publicWorker.workerData=workerData;
5466

‎lib/internal/worker.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const{
1212
ERR_INVALID_ARG_TYPE,
1313
}=require('internal/errors').codes;
1414
const{ validateString }=require('internal/validators');
15+
const{ getOptionValue }=require('internal/options');
1516

1617
const{
1718
drainMessagePort,
@@ -112,6 +113,9 @@ class Worker extends EventEmitter{
112113
doEval: !!options.eval,
113114
workerData: options.workerData,
114115
publicPort: port2,
116+
manifestSrc: getOptionValue('--experimental-policy') ?
117+
require('internal/process/policy').src :
118+
null,
115119
hasStdin: !!options.stdin
116120
},[port2]);
117121
// Actually start the new thread now that everything is in place.

‎test/parallel/test-policy-integrity.js‎

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ const parentFilepath = path.join(tmpdir.path, 'parent.js');
3333
constparentURL=pathToFileURL(parentFilepath);
3434
constparentBody='require(\'./dep.js\')';
3535

36+
constworkerSpawningFilepath=path.join(tmpdir.path,'worker_spawner.js');
37+
constworkerSpawningURL=pathToFileURL(workerSpawningFilepath);
38+
constworkerSpawningBody=`
39+
const{Worker } = require('worker_threads');
40+
// make sure this is gone to ensure we don't do another fs read of it
41+
// will error out if we do
42+
require('fs').unlinkSync(${JSON.stringify(policyFilepath)});
43+
const w = new Worker(${JSON.stringify(parentFilepath)});
44+
w.on('exit', process.exit);
45+
`;
46+
3647
constdepFilepath=path.join(tmpdir.path,'dep.js');
3748
constdepURL=pathToFileURL(depFilepath);
3849
constdepBody='';
@@ -49,8 +60,9 @@ if (!tmpdirURL.pathname.endsWith('/')){
4960
}
5061
functiontest({
5162
shouldFail =false,
63+
preload =[],
5264
entry,
53-
onerror,
65+
onerror=undefined,
5466
resources ={}
5567
}){
5668
constmanifest={
@@ -65,7 +77,9 @@ function test({
6577
}
6678
fs.writeFileSync(policyFilepath,JSON.stringify(manifest,null,2));
6779
const{ status }=spawnSync(process.execPath,[
68-
'--experimental-policy',policyFilepath,entry
80+
'--experimental-policy',policyFilepath,
81+
...preload.map((m)=>['-r',m]).flat(),
82+
entry
6983
]);
7084
if(shouldFail){
7185
assert.notStrictEqual(status,0);
@@ -74,13 +88,25 @@ function test({
7488
}
7589
}
7690

77-
const{ status }=spawnSync(process.execPath,[
78-
'--experimental-policy',policyFilepath,
79-
'--experimental-policy',policyFilepath
80-
],{
81-
stdio: 'pipe'
82-
});
83-
assert.notStrictEqual(status,0,'Should not allow multiple policies');
91+
{
92+
const{ status }=spawnSync(process.execPath,[
93+
'--experimental-policy',policyFilepath,
94+
'--experimental-policy',policyFilepath
95+
],{
96+
stdio: 'pipe'
97+
});
98+
assert.notStrictEqual(status,0,'Should not allow multiple policies');
99+
}
100+
{
101+
constenoentFilepath=path.join(tmpdir.path,'enoent');
102+
try{fs.unlinkSync(enoentFilepath);}catch{}
103+
const{ status }=spawnSync(process.execPath,[
104+
'--experimental-policy',enoentFilepath,'-e',''
105+
],{
106+
stdio: 'pipe'
107+
});
108+
assert.notStrictEqual(status,0,'Should not allow missing policies');
109+
}
84110

85111
test({
86112
shouldFail: true,
@@ -195,6 +221,21 @@ test({
195221
}
196222
}
197223
});
224+
test({
225+
shouldFail: false,
226+
preload: [depFilepath],
227+
entry: parentFilepath,
228+
resources: {
229+
[parentURL]: {
230+
body: parentBody,
231+
match: true,
232+
},
233+
[depURL]: {
234+
body: depBody,
235+
match: true,
236+
}
237+
}
238+
});
198239
test({
199240
shouldFail: true,
200241
entry: parentFilepath,
@@ -295,3 +336,50 @@ test({
295336
}
296337
}
297338
});
339+
test({
340+
shouldFail: true,
341+
entry: workerSpawningFilepath,
342+
resources: {
343+
[workerSpawningURL]: {
344+
body: workerSpawningBody,
345+
match: true,
346+
},
347+
}
348+
});
349+
test({
350+
shouldFail: false,
351+
entry: workerSpawningFilepath,
352+
resources: {
353+
[workerSpawningURL]: {
354+
body: workerSpawningBody,
355+
match: true,
356+
},
357+
[parentURL]: {
358+
body: parentBody,
359+
match: true,
360+
},
361+
[depURL]: {
362+
body: depBody,
363+
match: true,
364+
}
365+
}
366+
});
367+
test({
368+
shouldFail: false,
369+
entry: workerSpawningFilepath,
370+
preload: [parentFilepath],
371+
resources: {
372+
[workerSpawningURL]: {
373+
body: workerSpawningBody,
374+
match: true,
375+
},
376+
[parentURL]: {
377+
body: parentBody,
378+
match: true,
379+
},
380+
[depURL]: {
381+
body: depBody,
382+
match: true,
383+
}
384+
}
385+
});

0 commit comments

Comments
(0)