diff --git a/lib/child_process.js b/lib/child_process.js index c09fca512584ce..48870b35ad0f34 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -569,6 +569,7 @@ function normalizeSpawnArguments(file, args, options) { else validateObject(options, 'options'); + options = { __proto__: null, ...options }; let cwd = options.cwd; // Validate the cwd, if present. diff --git a/test/parallel/test-child-process-prototype-tampering.mjs b/test/parallel/test-child-process-prototype-tampering.mjs index 5657458f911521..d94c4bdbc61621 100644 --- a/test/parallel/test-child-process-prototype-tampering.mjs +++ b/test/parallel/test-child-process-prototype-tampering.mjs @@ -1,7 +1,7 @@ import * as common from '../common/index.mjs'; import * as fixtures from '../common/fixtures.mjs'; import { EOL } from 'node:os'; -import { strictEqual } from 'node:assert'; +import { strictEqual, notStrictEqual, throws } from 'node:assert'; import cp from 'node:child_process'; // TODO(LiviaMedeiros): test on different platforms @@ -57,3 +57,35 @@ for (const tamperedUID of [0, 1, 999, 1000, 0n, 'gwak']) { delete Object.prototype.execPath; } + +for (const shellCommandArgument of ['-L && echo "tampered"']) { + Object.prototype.shell = true; + const cmd = 'pwd'; + let cmdExitCode = ''; + + const program = cp.spawn(cmd, [shellCommandArgument], { cwd: expectedCWD }); + program.stderr.on('data', common.mustCall()); + program.stdout.on('data', common.mustNotCall()); + + program.on('exit', common.mustCall((code) => { + notStrictEqual(code, 0); + })); + + cp.execFile(cmd, [shellCommandArgument], { cwd: expectedCWD }, + common.mustCall((err) => { + notStrictEqual(err.code, 0); + }) + ); + + throws(() => { + cp.execFileSync(cmd, [shellCommandArgument], { cwd: expectedCWD }); + }, (e) => { + notStrictEqual(e.status, 0); + return true; + }); + + cmdExitCode = cp.spawnSync(cmd, [shellCommandArgument], { cwd: expectedCWD }).status; + notStrictEqual(cmdExitCode, 0); + + delete Object.prototype.shell; +}