Skip to content

Commit 882adc5

Browse files
authored
child_process: propagate config-file permission flags to children
Signed-off-by: RafaelGSS <[email protected]> PR-URL: #66559 Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Ulises Gascón <[email protected]>
1 parent 855e3f7 commit 882adc5

5 files changed

Lines changed: 92 additions & 0 deletions

File tree

‎lib/child_process.js‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const {
3838
ObjectPrototypeHasOwnProperty,
3939
PromiseWithResolvers,
4040
RegExpPrototypeExec,
41+
RegExpPrototypeSymbolReplace,
4142
SafeSet,
4243
StringPrototypeIncludes,
4344
StringPrototypeIndexOf,
@@ -75,6 +76,7 @@ const {
7576
} = require('internal/errors');
7677
const { clearTimeout, setTimeout } = require('timers');
7778
const { getValidatedPath } = require('internal/fs/utils');
79+
const { getOptionValue } = require('internal/options');
7880
const {
7981
validateAbortSignal,
8082
validateArray,
@@ -579,17 +581,37 @@ function copyPermissionModelFlagsToEnv(env, key, args) {
579581
const allowAllEnv = !permission.isAuditMode();
580582

581583
const flagsToCopy = getPermissionModelFlagsToCopy();
584+
const copiedFlags = new SafeSet();
582585
for (const arg of process.execArgv) {
583586
if (allowAllEnv && arg.startsWith('--allow-env')) {
584587
continue;
585588
}
586589
for (const flag of flagsToCopy) {
587590
if (arg.startsWith(flag)) {
591+
copiedFlags.add(flag);
588592
env[key] = `${env[key] ? env[key] + ' ' + arg : arg}`;
589593
}
590594
}
591595
}
592596

597+
// Flags set through --config-file are not part of process.execArgv.
598+
for (const flag of flagsToCopy) {
599+
if (copiedFlags.has(flag) || (allowAllEnv && flag === '--allow-env')) {
600+
continue;
601+
}
602+
const value = getOptionValue(flag);
603+
if (value === true) {
604+
env[key] = `${env[key] ? env[key] + ' ' : ''}${flag}`;
605+
} else if (ArrayIsArray(value)) {
606+
for (const item of value) {
607+
// Values may contain spaces or quotes (e.g. the implicitly allowed
608+
// entry point), so quote them as NODE_OPTIONS expects.
609+
const quoted = RegExpPrototypeSymbolReplace(/["\\]/g, item, '\\$&');
610+
env[key] = `${env[key] ? env[key] + ' ' : ''}"${flag}=${quoted}"`;
611+
}
612+
}
613+
}
614+
593615
if (allowAllEnv) {
594616
env[key] = `${env[key] ? env[key] + ' ' : ''}--allow-env=*`;
595617
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const { spawnSync } = require('child_process');
2+
const { status, stdout, stderr } = spawnSync(process.execPath, [
3+
'-p',
4+
`JSON.stringify([
5+
typeof process.permission,
6+
process.permission.has("fs.read"),
7+
process.permission.has("fs.write"),
8+
process.permission.has("child"),
9+
process.permission.has("worker"),
10+
])`,
11+
]);
12+
console.log(JSON.stringify({
13+
status,
14+
stdout: stdout.toString().trim(),
15+
stderr: stderr.toString(),
16+
}));
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"permission": {
3+
"allow-child-process": true,
4+
"allow-worker": true,
5+
"allow-fs-read": [
6+
"*"
7+
]
8+
}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"permission": {
3+
"permission": true,
4+
"allow-child-process": true,
5+
"allow-worker": true,
6+
"allow-fs-read": [
7+
"*",
8+
"/nonexistent/dir with \"quotes\" and \\backslash"
9+
]
10+
}
11+
}

‎test/parallel/test-permission-config-file.mjs‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,40 @@ describe('Permission model config file support', () => {
6464
}
6565
});
6666

67+
it('should propagate config file permissions to child processes', {
68+
skip: process.config.variables.node_without_node_options && 'missing NODE_OPTIONS support',
69+
}, async () => {
70+
const childTestPath = fixtures.path('permission/child-process-inherit-test.js');
71+
const expected = JSON.stringify(['object', true, false, true, true]);
72+
73+
// --permission set in the config file
74+
{
75+
const configPath = fixtures.path('permission/config-child-inherit.json');
76+
const result = await spawnPromisified(process.execPath, [
77+
`--config-file=${configPath}`,
78+
childTestPath,
79+
]);
80+
assert.strictEqual(result.code, 0, result.stderr);
81+
const child = JSON.parse(result.stdout);
82+
assert.strictEqual(child.status, 0, child.stderr);
83+
assert.strictEqual(child.stdout, expected);
84+
}
85+
86+
// --permission set in the command line
87+
{
88+
const configPath = fixtures.path('permission/config-child-inherit-allow-only.json');
89+
const result = await spawnPromisified(process.execPath, [
90+
'--permission',
91+
`--config-file=${configPath}`,
92+
childTestPath,
93+
]);
94+
assert.strictEqual(result.code, 0, result.stderr);
95+
const child = JSON.parse(result.stdout);
96+
assert.strictEqual(child.status, 0, child.stderr);
97+
assert.strictEqual(child.stdout, expected);
98+
}
99+
});
100+
67101
it('should load network and inspector permissions from config file', async () => {
68102
const configPath = fixtures.path('permission/config-net-inspector.json');
69103
const readOnlyConfigPath = fixtures.path('permission/config-fs-read-only.json');

0 commit comments

Comments
 (0)