diff --git a/lib/child_process.js b/lib/child_process.js index 6b411f43f07..668b000398f 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -568,6 +568,11 @@ function hasPermissionFlagInEnv(nodeOptions) { ); } +function quoteNodeOption(flag, value) { + const quoted = RegExpPrototypeSymbolReplace(/["\\]/g, value, '\\$&'); + return `"${flag}=${quoted}"`; +} + function copyPermissionModelFlagsToEnv(env, key, args) { // Do not override if permission was already passed to file if (args.includes('--permission') || args.includes('--permission-audit') || @@ -582,15 +587,33 @@ function copyPermissionModelFlagsToEnv(env, key, args) { const flagsToCopy = getPermissionModelFlagsToCopy(); const copiedFlags = new SafeSet(); - for (const arg of process.execArgv) { + const execArgv = process.execArgv; + for (let i = 0; i < execArgv.length; i++) { + const arg = execArgv[i]; if (allowAllEnv && arg.startsWith('--allow-env')) { continue; } for (const flag of flagsToCopy) { - if (arg.startsWith(flag)) { - copiedFlags.add(flag); - env[key] = `${env[key] ? env[key] + ' ' + arg : arg}`; + if (!arg.startsWith(flag)) { + continue; + } + copiedFlags.add(flag); + if (ArrayIsArray(getOptionValue(flag))) { + let value; + if (arg === flag && i + 1 < execArgv.length) { + value = execArgv[++i]; + if (value[0] === '\\' && value[1] === '-') { + value = StringPrototypeSlice(value, 1); + } + } else if (arg[flag.length] === '=') { + value = StringPrototypeSlice(arg, flag.length + 1); + } + if (value !== undefined) { + env[key] = `${env[key] ? env[key] + ' ' : ''}${quoteNodeOption(flag, value)}`; + break; + } } + env[key] = `${env[key] ? env[key] + ' ' + arg : arg}`; } } @@ -604,10 +627,7 @@ function copyPermissionModelFlagsToEnv(env, key, args) { env[key] = `${env[key] ? env[key] + ' ' : ''}${flag}`; } else if (ArrayIsArray(value)) { for (const item of value) { - // Values may contain spaces or quotes (e.g. the implicitly allowed - // entry point), so quote them as NODE_OPTIONS expects. - const quoted = RegExpPrototypeSymbolReplace(/["\\]/g, item, '\\$&'); - env[key] = `${env[key] ? env[key] + ' ' : ''}"${flag}=${quoted}"`; + env[key] = `${env[key] ? env[key] + ' ' : ''}${quoteNodeOption(flag, item)}`; } } } diff --git a/test/parallel/test-permission-child-process-inherit-flags.js b/test/parallel/test-permission-child-process-inherit-flags.js index 36e89bfb1c7..808be87c3ec 100644 --- a/test/parallel/test-permission-child-process-inherit-flags.js +++ b/test/parallel/test-permission-child-process-inherit-flags.js @@ -100,6 +100,51 @@ const childProcess = require('child_process'); assert.strictEqual(worker, 'false'); } +{ + const tmpdir = require('../common/tmpdir'); + const dir = tmpdir.resolve('inherit flags dir'); + const file = `${dir}/file.txt`; + + const grandchildSource = ` + console.log(process.permission.has('fs.read')); + console.log(process.permission.has('fs.read', ${JSON.stringify(file)})); + console.log(process.permission.has('fs.read', ${JSON.stringify(__filename)})); + `; + const childSource = ` + const { status, stdout, stderr } = require('child_process').spawnSync( + process.execPath, ['-e', ${JSON.stringify(grandchildSource)}]); + console.log(JSON.stringify({ + status, + stderr: stderr.toString(), + permissions: stdout.toString().trim().split('\\n'), + })); + `; + + function run(args) { + const { status, stdout, stderr } = childProcess.spawnSync( + process.execPath, + ['--permission', '--allow-child-process', ...args, '-e', childSource], + ); + assert.strictEqual(status, 0, stderr.toString()); + const grandchild = JSON.parse(stdout.toString()); + assert.strictEqual(grandchild.status, 0, grandchild.stderr); + return grandchild.permissions; + } + + assert.deepStrictEqual(run(['--allow-fs-read', '*']), + ['true', 'true', 'true']); + + assert.deepStrictEqual(run(['--allow-fs-read', `${dir}/*`]), + ['false', 'true', 'false']); + + assert.deepStrictEqual(run([`--allow-fs-read=${dir}/*`]), + ['false', 'true', 'false']); + + assert.deepStrictEqual( + run(['--allow-fs-read', `${dir}/*`, `--allow-fs-read=${__filename}`]), + ['false', 'true', 'true']); +} + { assert.strictEqual(process.env.NODE_OPTIONS, undefined); }