test: add escapePOSIXShell util

PR-URL: https://github.com/nodejs/node/pull/55125
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
This commit is contained in:
Antoine du Hamel 2024-09-29 22:44:52 +02:00 committed by GitHub
parent 858bce5698
commit 99e0d0d218
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 273 additions and 343 deletions

View File

@ -27,11 +27,12 @@ if (common.isWindows)
const assert = require('assert'); const assert = require('assert');
const exec = require('child_process').exec; const exec = require('child_process').exec;
let cmdline = `ulimit -c 0; ${process.execPath}`; const cmdline =
cmdline += ' --max-old-space-size=16 --max-semi-space-size=4'; common.escapePOSIXShell`ulimit -c 0; "${
cmdline += ' -e "a = []; for (i = 0; i < 1e9; i++) { a.push({}) }"'; process.execPath
}" --max-old-space-size=16 --max-semi-space-size=4 -e "a = []; for (i = 0; i < 1e9; i++) { a.push({}) }"`;
exec(cmdline, function(err, stdout, stderr) { exec(...cmdline, common.mustCall((err, stdout, stderr) => {
if (!err) { if (!err) {
console.log(stdout); console.log(stdout);
console.log(stderr); console.log(stderr);
@ -39,4 +40,4 @@ exec(cmdline, function(err, stdout, stderr) {
} }
assert(common.nodeProcessAborted(err.code, err.signal)); assert(common.nodeProcessAborted(err.code, err.signal));
}); }));

View File

@ -62,12 +62,14 @@ assert.ok(!arg);
let program = process.execPath; let program = process.execPath;
let args = [ let args = [
'--abort-on-uncaught-exception', __filename, 'test_callback_abort' ]; '--abort-on-uncaught-exception', __filename, 'test_callback_abort' ];
const options = { encoding: 'utf8' }; let options = {};
if (!common.isWindows) { if (!common.isWindows) {
program = `ulimit -c 0 && exec ${program} ${args.join(' ')}`; [program, options] = common.escapePOSIXShell`ulimit -c 0 && exec "${program}" ${args[0]} "${args[1]}" ${args[2]}`;
args = []; args = [];
options.shell = true; options.shell = true;
} }
options.encoding = 'utf8';
const child = spawnSync(program, args, options); const child = spawnSync(program, args, options);
if (common.isWindows) { if (common.isWindows) {
assert.strictEqual(child.status, 134); assert.strictEqual(child.status, 134);

View File

@ -112,6 +112,33 @@ Creates a 10 MiB file of all null characters.
Indicates if there is more than 1gb of total memory. Indicates if there is more than 1gb of total memory.
### ``escapePOSIXShell`shell command` ``
Escapes values in a string template literal to pass them as env variable. On Windows, this function
does not escape anything (which is fine for most paths, as `"` is not a valid
char in a path on Windows), so for tests that must pass on Windows, you should
use it only to escape paths, inside double quotes.
This function is meant to be used for tagged template strings.
```js
const { escapePOSIXShell } = require('../common');
const fixtures = require('../common/fixtures');
const { execSync } = require('node:child_process');
const origin = fixtures.path('origin');
const destination = fixtures.path('destination');
execSync(...escapePOSIXShell`cp "${origin}" "${destination}"`);
// When you need to specify specific options, and/or additional env variables:
const [cmd, opts] = escapePOSIXShell`cp "${origin}" "${destination}"`;
console.log(typeof cmd === 'string'); // true
console.log(opts === undefined || typeof opts.env === 'object'); // true
execSync(cmd, { ...opts, stdio: 'ignore' });
execSync(cmd, { stdio: 'ignore', env: { ...opts?.env, KEY: 'value' } });
```
When possible, avoid using a shell; that way, there's no need to escape values.
### `expectsError(validator[, exact])` ### `expectsError(validator[, exact])`
* `validator` [\<Object>][<Object>] | [\<RegExp>][<RegExp>] | * `validator` [\<Object>][<Object>] | [\<RegExp>][<RegExp>] |

View File

@ -249,15 +249,13 @@ const PIPE = (() => {
// `$node --abort-on-uncaught-exception $file child` // `$node --abort-on-uncaught-exception $file child`
// the process aborts. // the process aborts.
function childShouldThrowAndAbort() { function childShouldThrowAndAbort() {
let testCmd = ''; const escapedArgs = escapePOSIXShell`"${process.argv[0]}" --abort-on-uncaught-exception "${process.argv[1]}" child`;
if (!isWindows) { if (!isWindows) {
// Do not create core files, as it can take a lot of disk space on // Do not create core files, as it can take a lot of disk space on
// continuous testing and developers' machines // continuous testing and developers' machines
testCmd += 'ulimit -c 0 && '; escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0];
} }
testCmd += `"${process.argv[0]}" --abort-on-uncaught-exception `; const child = exec(...escapedArgs);
testCmd += `"${process.argv[1]}" child`;
const child = exec(testCmd);
child.on('exit', function onExit(exitCode, signal) { child.on('exit', function onExit(exitCode, signal) {
const errMsg = 'Test should have aborted ' + const errMsg = 'Test should have aborted ' +
`but instead exited with exit code ${exitCode}` + `but instead exited with exit code ${exitCode}` +
@ -888,6 +886,32 @@ function spawnPromisified(...args) {
}); });
} }
/**
* Escape values in a string template literal. On Windows, this function
* does not escape anything (which is fine for paths, as `"` is not a valid char
* in a path on Windows), so you should use it only to escape paths or other
* values on tests which are skipped on Windows.
* This function is meant to be used for tagged template strings.
* @returns {[string, object | undefined]} An array that can be passed as
* arguments to `exec` or `execSync`.
*/
function escapePOSIXShell(cmdParts, ...args) {
if (common.isWindows) {
// On Windows, paths cannot contain `"`, so we can return the string unchanged.
return [String.raw({ raw: cmdParts }, ...args)];
}
// On POSIX shells, we can pass values via the env, as there's a standard way for referencing a variable.
const env = { ...process.env };
let cmd = cmdParts[0];
for (let i = 0; i < args.length; i++) {
const envVarName = `ESCAPED_${i}`;
env[envVarName] = args[i];
cmd += '${' + envVarName + '}' + cmdParts[i + 1];
}
return [cmd, { env }];
};
function getPrintedStackTrace(stderr) { function getPrintedStackTrace(stderr) {
const lines = stderr.split('\n'); const lines = stderr.split('\n');
@ -951,6 +975,7 @@ const common = {
childShouldThrowAndAbort, childShouldThrowAndAbort,
createZeroFilledFile, createZeroFilledFile,
defaultAutoSelectFamilyAttemptTimeout, defaultAutoSelectFamilyAttemptTimeout,
escapePOSIXShell,
expectsError, expectsError,
expectRequiredModule, expectRequiredModule,
expectWarning, expectWarning,

View File

@ -11,6 +11,7 @@ const {
childShouldThrowAndAbort, childShouldThrowAndAbort,
createZeroFilledFile, createZeroFilledFile,
enoughTestMem, enoughTestMem,
escapePOSIXShell,
expectsError, expectsError,
expectWarning, expectWarning,
getArrayBufferViews, getArrayBufferViews,
@ -64,6 +65,7 @@ export {
createRequire, createRequire,
createZeroFilledFile, createZeroFilledFile,
enoughTestMem, enoughTestMem,
escapePOSIXShell,
expectsError, expectsError,
expectWarning, expectWarning,
getArrayBufferViews, getArrayBufferViews,

View File

@ -27,7 +27,8 @@ ChildProcess.prototype.spawn = function() {
}; };
function createChild(options, callback) { function createChild(options, callback) {
const cmd = `"${process.execPath}" "${__filename}" child`; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`;
options = { ...options, env: { ...opts?.env, ...options.env } };
return cp.exec(cmd, options, common.mustCall(callback)); return cp.exec(cmd, options, common.mustCall(callback));
} }

View File

@ -13,7 +13,8 @@ if (process.argv[2] === 'child') {
const expectedStdout = `${stdoutData}\n`; const expectedStdout = `${stdoutData}\n`;
const expectedStderr = `${stderrData}\n`; const expectedStderr = `${stderrData}\n`;
function run(options, callback) { function run(options, callback) {
const cmd = `"${process.execPath}" "${__filename}" child`; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`;
options = { ...options, env: { ...opts?.env, ...options.env } };
cp.exec(cmd, options, common.mustSucceed((stdout, stderr) => { cp.exec(cmd, options, common.mustSucceed((stdout, stderr) => {
callback(stdout, stderr); callback(stdout, stderr);

View File

@ -14,14 +14,15 @@ function runChecks(err, stdio, streamName, expected) {
// On non-Windows, we can pass the path via the env; `"` is not a valid char on // On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path. // Windows, so we can simply pass the path.
const execNode = (args, optionsOrCallback, callback) => { const execNode = (args, optionsOrCallback, callback) => {
const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" `;
let options = optionsOrCallback; let options = optionsOrCallback;
if (typeof optionsOrCallback === 'function') { if (typeof optionsOrCallback === 'function') {
options = undefined; options = undefined;
callback = optionsOrCallback; callback = optionsOrCallback;
} }
return cp.exec( return cp.exec(
`"${common.isWindows ? process.execPath : '$NODE'}" ${args}`, cmd + args,
common.isWindows ? options : { ...options, env: { ...process.env, NODE: process.execPath } }, { ...opts, ...options },
callback, callback,
); );
}; };

View File

@ -12,8 +12,7 @@ if (process.argv[2] === 'child') {
console.log(stdoutData); console.log(stdoutData);
console.error(stderrData); console.error(stderrData);
} else { } else {
const cmd = `"${process.execPath}" "${__filename}" child`; const child = cp.exec(...common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`, common.mustSucceed((stdout, stderr) => {
const child = cp.exec(cmd, common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, expectedStdout); assert.strictEqual(stdout, expectedStdout);
assert.strictEqual(stderr, expectedStderr); assert.strictEqual(stderr, expectedStderr);
})); }));

View File

@ -18,9 +18,10 @@ if (process.argv[2] === 'child') {
return; return;
} }
const cmd = `"${process.execPath}" "${__filename}" child`; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`;
cp.exec(cmd, { cp.exec(cmd, {
...opts,
timeout: kExpiringParentTimer, timeout: kExpiringParentTimer,
}, common.mustCall((err, stdout, stderr) => { }, common.mustCall((err, stdout, stderr) => {
console.log('[stdout]', stdout.trim()); console.log('[stdout]', stdout.trim());

View File

@ -18,10 +18,11 @@ if (process.argv[2] === 'child') {
return; return;
} }
const cmd = `"${process.execPath}" "${__filename}" child`; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`;
// Test with a different kill signal. // Test with a different kill signal.
cp.exec(cmd, { cp.exec(cmd, {
...opts,
timeout: kExpiringParentTimer, timeout: kExpiringParentTimer,
killSignal: 'SIGKILL' killSignal: 'SIGKILL'
}, common.mustCall((err, stdout, stderr) => { }, common.mustCall((err, stdout, stderr) => {

View File

@ -22,10 +22,11 @@ if (process.argv[2] === 'child') {
return; return;
} }
const cmd = `"${process.execPath}" "${__filename}" child`; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`;
cp.exec(cmd, { cp.exec(cmd, {
timeout: kTimeoutNotSupposedToExpire ...opts,
timeout: kTimeoutNotSupposedToExpire,
}, common.mustSucceed((stdout, stderr) => { }, common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout.trim(), 'child stdout'); assert.strictEqual(stdout.trim(), 'child stdout');
assert.strictEqual(stderr.trim(), 'child stderr'); assert.strictEqual(stderr.trim(), 'child stderr');

View File

@ -1,5 +1,5 @@
'use strict'; 'use strict';
require('../common'); const { escapePOSIXShell } = require('../common');
// This test checks that the maxBuffer option for child_process.spawnSync() // This test checks that the maxBuffer option for child_process.spawnSync()
// works as expected. // works as expected.
@ -10,15 +10,12 @@ const { execSync } = require('child_process');
const msgOut = 'this is stdout'; const msgOut = 'this is stdout';
const msgOutBuf = Buffer.from(`${msgOut}\n`); const msgOutBuf = Buffer.from(`${msgOut}\n`);
const args = [ const [cmd, opts] = escapePOSIXShell`"${process.execPath}" -e "${`console.log('${msgOut}')`}"`;
'-e',
`"console.log('${msgOut}')";`,
];
// Verify that an error is returned if maxBuffer is surpassed. // Verify that an error is returned if maxBuffer is surpassed.
{ {
assert.throws(() => { assert.throws(() => {
execSync(`"${process.execPath}" ${args.join(' ')}`, { maxBuffer: 1 }); execSync(cmd, { ...opts, maxBuffer: 1 });
}, (e) => { }, (e) => {
assert.ok(e, 'maxBuffer should error'); assert.ok(e, 'maxBuffer should error');
assert.strictEqual(e.code, 'ENOBUFS'); assert.strictEqual(e.code, 'ENOBUFS');
@ -33,8 +30,8 @@ const args = [
// Verify that a maxBuffer size of Infinity works. // Verify that a maxBuffer size of Infinity works.
{ {
const ret = execSync( const ret = execSync(
`"${process.execPath}" ${args.join(' ')}`, cmd,
{ maxBuffer: Infinity } { ...opts, maxBuffer: Infinity },
); );
assert.deepStrictEqual(ret, msgOutBuf); assert.deepStrictEqual(ret, msgOutBuf);
@ -43,9 +40,7 @@ const args = [
// Default maxBuffer size is 1024 * 1024. // Default maxBuffer size is 1024 * 1024.
{ {
assert.throws(() => { assert.throws(() => {
execSync( execSync(...escapePOSIXShell`"${process.execPath}" -e "console.log('a'.repeat(1024 * 1024))"`);
`"${process.execPath}" -e "console.log('a'.repeat(1024 * 1024))"`
);
}, (e) => { }, (e) => {
assert.ok(e, 'maxBuffer should error'); assert.ok(e, 'maxBuffer should error');
assert.strictEqual(e.code, 'ENOBUFS'); assert.strictEqual(e.code, 'ENOBUFS');
@ -56,9 +51,7 @@ const args = [
// Default maxBuffer size is 1024 * 1024. // Default maxBuffer size is 1024 * 1024.
{ {
const ret = execSync( const ret = execSync(...escapePOSIXShell`"${process.execPath}" -e "console.log('a'.repeat(1024 * 1024 - 1))"`);
`"${process.execPath}" -e "console.log('a'.repeat(1024 * 1024 - 1))"`
);
assert.deepStrictEqual( assert.deepStrictEqual(
ret.toString().trim(), ret.toString().trim(),

View File

@ -7,16 +7,8 @@ const { promisify } = require('util');
const exec = promisify(child_process.exec); const exec = promisify(child_process.exec);
const execFile = promisify(child_process.execFile); const execFile = promisify(child_process.execFile);
// The execPath might contain chars that should be escaped in a shell context.
// On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path.
const execNode = (args) => exec(
`"${common.isWindows ? process.execPath : '$NODE'}" ${args}`,
common.isWindows ? undefined : { env: { ...process.env, NODE: process.execPath } },
);
{ {
const promise = execNode('-p 42'); const promise = exec(...common.escapePOSIXShell`"${process.execPath}" -p 42`);
assert(promise.child instanceof child_process.ChildProcess); assert(promise.child instanceof child_process.ChildProcess);
promise.then(common.mustCall((obj) => { promise.then(common.mustCall((obj) => {
@ -53,7 +45,7 @@ const execNode = (args) => exec(
const failingCodeWithStdoutErr = const failingCodeWithStdoutErr =
'console.log(42);console.error(43);process.exit(1)'; 'console.log(42);console.error(43);process.exit(1)';
{ {
execNode(`-e "${failingCodeWithStdoutErr}"`) exec(...common.escapePOSIXShell`"${process.execPath}" -e "${failingCodeWithStdoutErr}"`)
.catch(common.mustCall((err) => { .catch(common.mustCall((err) => {
assert.strictEqual(err.code, 1); assert.strictEqual(err.code, 1);
assert.strictEqual(err.stdout, '42\n'); assert.strictEqual(err.stdout, '42\n');

View File

@ -32,7 +32,6 @@ const assert = require('assert');
const child = require('child_process'); const child = require('child_process');
const path = require('path'); const path = require('path');
const fixtures = require('../common/fixtures'); const fixtures = require('../common/fixtures');
const nodejs = `"${process.execPath}"`;
if (process.argv.length > 2) { if (process.argv.length > 2) {
console.log(process.argv.slice(2).join(' ')); console.log(process.argv.slice(2).join(' '));
@ -40,13 +39,13 @@ if (process.argv.length > 2) {
} }
// Assert that nothing is written to stdout. // Assert that nothing is written to stdout.
child.exec(`${nodejs} --eval 42`, common.mustSucceed((stdout, stderr) => { child.exec(...common.escapePOSIXShell`"${process.execPath}" --eval 42`, common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
})); }));
// Assert that "42\n" is written to stderr. // Assert that "42\n" is written to stderr.
child.exec(`${nodejs} --eval "console.error(42)"`, child.exec(...common.escapePOSIXShell`"${process.execPath}" --eval "console.error(42)"`,
common.mustSucceed((stdout, stderr) => { common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
assert.strictEqual(stderr, '42\n'); assert.strictEqual(stderr, '42\n');
@ -54,14 +53,14 @@ child.exec(`${nodejs} --eval "console.error(42)"`,
// Assert that the expected output is written to stdout. // Assert that the expected output is written to stdout.
['--print', '-p -e', '-pe', '-p'].forEach((s) => { ['--print', '-p -e', '-pe', '-p'].forEach((s) => {
const cmd = `${nodejs} ${s} `; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" ${s}`;
child.exec(`${cmd}42`, common.mustSucceed((stdout, stderr) => { child.exec(`${cmd} 42`, opts, common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, '42\n'); assert.strictEqual(stdout, '42\n');
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
})); }));
child.exec(`${cmd} '[]'`, common.mustSucceed((stdout, stderr) => { child.exec(`${cmd} '[]'`, opts, common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, '[]\n'); assert.strictEqual(stdout, '[]\n');
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
})); }));
@ -69,29 +68,26 @@ child.exec(`${nodejs} --eval "console.error(42)"`,
// Assert that module loading works. // Assert that module loading works.
{ {
// Replace \ by / because Windows uses backslashes in paths, but they're still common.spawnPromisified(process.execPath, ['--eval', `require(${JSON.stringify(__filename)})`])
// interpreted as the escape character when put between quotes. .then(common.mustCall(({ stdout, stderr, code }) => {
const filename = __filename.replace(/\\/g, '/'); assert.strictEqual(stderr, '');
child.exec(`${nodejs} --eval "require('${filename}')"`,
common.mustCall((err, stdout, stderr) => {
assert.strictEqual(err.code, 42);
assert.strictEqual( assert.strictEqual(
stdout, 'Loaded as a module, exiting with status code 42.\n'); stdout, 'Loaded as a module, exiting with status code 42.\n');
assert.strictEqual(stderr, ''); assert.strictEqual(code, 42);
})); }));
} }
// Check that builtin modules are pre-defined. // Check that builtin modules are pre-defined.
child.exec(`${nodejs} --print "os.platform()"`, child.exec(...common.escapePOSIXShell`"${process.execPath}" --print "os.platform()"`,
common.mustSucceed((stdout, stderr) => { common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
assert.strictEqual(stdout.trim(), require('os').platform()); assert.strictEqual(stdout.trim(), require('os').platform());
})); }));
// Module path resolve bug regression test. // Module path resolve bug regression test.
child.exec(`${nodejs} --eval "require('./test/parallel/test-cli-eval.js')"`, const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" --eval "require('./test/parallel/test-cli-eval.js')"`;
{ cwd: path.resolve(__dirname, '../../') }, child.exec(cmd,
{ ...opts, cwd: path.resolve(__dirname, '../../') },
common.mustCall((err, stdout, stderr) => { common.mustCall((err, stdout, stderr) => {
assert.strictEqual(err.code, 42); assert.strictEqual(err.code, 42);
assert.strictEqual( assert.strictEqual(
@ -100,7 +96,7 @@ child.exec(`${nodejs} --eval "require('./test/parallel/test-cli-eval.js')"`,
})); }));
// Missing argument should not crash. // Missing argument should not crash.
child.exec(`${nodejs} -e`, common.mustCall((err, stdout, stderr) => { child.exec(...common.escapePOSIXShell`"${process.execPath}" -e`, common.mustCall((err, stdout, stderr) => {
assert.strictEqual(err.code, 9); assert.strictEqual(err.code, 9);
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
assert.strictEqual(stderr.trim(), assert.strictEqual(stderr.trim(),
@ -108,18 +104,18 @@ child.exec(`${nodejs} -e`, common.mustCall((err, stdout, stderr) => {
})); }));
// Empty program should do nothing. // Empty program should do nothing.
child.exec(`${nodejs} -e ""`, common.mustSucceed((stdout, stderr) => { child.exec(...common.escapePOSIXShell`"${process.execPath}" -e ""`, common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
})); }));
// "\\-42" should be interpreted as an escaped expression, not a switch. // "\\-42" should be interpreted as an escaped expression, not a switch.
child.exec(`${nodejs} -p "\\-42"`, common.mustSucceed((stdout, stderr) => { child.exec(...common.escapePOSIXShell`"${process.execPath}" -p "\\-42"`, common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, '-42\n'); assert.strictEqual(stdout, '-42\n');
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
})); }));
child.exec(`${nodejs} --use-strict -p process.execArgv`, child.exec(...common.escapePOSIXShell`"${process.execPath}" --use-strict -p process.execArgv`,
common.mustSucceed((stdout, stderr) => { common.mustSucceed((stdout, stderr) => {
assert.strictEqual( assert.strictEqual(
stdout, "[ '--use-strict', '-p', 'process.execArgv' ]\n" stdout, "[ '--use-strict', '-p', 'process.execArgv' ]\n"
@ -129,25 +125,25 @@ child.exec(`${nodejs} --use-strict -p process.execArgv`,
// Regression test for https://github.com/nodejs/node/issues/3574. // Regression test for https://github.com/nodejs/node/issues/3574.
{ {
let emptyFile = fixtures.path('empty.js'); const emptyFile = fixtures.path('empty.js');
if (common.isWindows) {
emptyFile = emptyFile.replace(/\\/g, '\\\\');
}
child.exec(`${nodejs} -e 'require("child_process").fork("${emptyFile}")'`, common.spawnPromisified(process.execPath, ['-e', `require("child_process").fork(${JSON.stringify(emptyFile)})`])
common.mustSucceed((stdout, stderr) => { .then(common.mustCall(({ stdout, stderr, code }) => {
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
assert.strictEqual(code, 0);
})); }));
// Make sure that monkey-patching process.execArgv doesn't cause child_process // Make sure that monkey-patching process.execArgv doesn't cause child_process
// to incorrectly munge execArgv. // to incorrectly munge execArgv.
child.exec( common.spawnPromisified(process.execPath, [
`${nodejs} -e "process.execArgv = ['-e', 'console.log(42)', 'thirdArg'];` + '-e',
`require('child_process').fork('${emptyFile}')"`, 'process.execArgv = [\'-e\', \'console.log(42)\', \'thirdArg\'];' +
common.mustSucceed((stdout, stderr) => { `require('child_process').fork(${JSON.stringify(emptyFile)})`,
]).then(common.mustCall(({ stdout, stderr, code }) => {
assert.strictEqual(stdout, '42\n'); assert.strictEqual(stdout, '42\n');
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
assert.strictEqual(code, 0);
})); }));
} }
@ -192,28 +188,30 @@ child.exec(`${nodejs} --use-strict -p process.execArgv`,
].forEach(function(args) { ].forEach(function(args) {
// Ensure that arguments are successfully passed to eval. // Ensure that arguments are successfully passed to eval.
const opt = ' --eval "console.log(process.argv.slice(1).join(\' \'))"'; child.exec(
const cmd = `${nodejs}${opt} -- ${args}`; ...common.escapePOSIXShell`"${process.execPath}" --eval "console.log(process.argv.slice(1).join(' '))" -- ${args}`,
child.exec(cmd, common.mustCall(function(err, stdout, stderr) { common.mustCall(function(err, stdout, stderr) {
assert.strictEqual(stdout, `${args}\n`); assert.strictEqual(stdout, `${args}\n`);
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
assert.strictEqual(err, null); assert.strictEqual(err, null);
})); })
);
// Ensure that arguments are successfully passed to print. // Ensure that arguments are successfully passed to print.
const popt = ' --print "process.argv.slice(1).join(\' \')"'; child.exec(
const pcmd = `${nodejs}${popt} -- ${args}`; ...common.escapePOSIXShell`"${process.execPath}" --print "process.argv.slice(1).join(' ')" -- ${args}`,
child.exec(pcmd, common.mustCall(function(err, stdout, stderr) { common.mustCall(function(err, stdout, stderr) {
assert.strictEqual(stdout, `${args}\n`); assert.strictEqual(stdout, `${args}\n`);
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
assert.strictEqual(err, null); assert.strictEqual(err, null);
})); })
);
// Ensure that arguments are successfully passed to a script. // Ensure that arguments are successfully passed to a script.
// The first argument after '--' should be interpreted as a script // The first argument after '--' should be interpreted as a script
// filename. // filename.
const filecmd = `${nodejs} -- "${__filename}" ${args}`; child.exec(...common.escapePOSIXShell`"${process.execPath}" -- "${__filename}" ${args}`,
child.exec(filecmd, common.mustCall(function(err, stdout, stderr) { common.mustCall(function(err, stdout, stderr) {
assert.strictEqual(stdout, `${args}\n`); assert.strictEqual(stdout, `${args}\n`);
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
assert.strictEqual(err, null); assert.strictEqual(err, null);
@ -226,14 +224,14 @@ child.exec(`${nodejs} --use-strict -p process.execArgv`,
// Assert that "42\n" is written to stdout on module eval. // Assert that "42\n" is written to stdout on module eval.
const execOptions = '--input-type module'; const execOptions = '--input-type module';
child.exec( child.exec(
`${nodejs} ${execOptions} --eval "console.log(42)"`, ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "console.log(42)"`,
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.strictEqual(stdout, '42\n'); assert.strictEqual(stdout, '42\n');
})); }));
// Assert that "42\n" is written to stdout with print option. // Assert that "42\n" is written to stdout with print option.
child.exec( child.exec(
`${nodejs} ${execOptions} --print --eval "42"`, ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --print --eval "42"`,
common.mustCall((err, stdout, stderr) => { common.mustCall((err, stdout, stderr) => {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
@ -242,7 +240,7 @@ child.exec(
// Assert that error is written to stderr on invalid input. // Assert that error is written to stderr on invalid input.
child.exec( child.exec(
`${nodejs} ${execOptions} --eval "!!!!"`, ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "!!!!"`,
common.mustCall((err, stdout, stderr) => { common.mustCall((err, stdout, stderr) => {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
@ -251,22 +249,25 @@ child.exec(
// Assert that require is undefined in ESM support // Assert that require is undefined in ESM support
child.exec( child.exec(
`${nodejs} ${execOptions} --eval "console.log(typeof require);"`, ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "console.log(typeof require);"`,
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.strictEqual(stdout, 'undefined\n'); assert.strictEqual(stdout, 'undefined\n');
})); }));
// Assert that import.meta is defined in ESM // Assert that import.meta is defined in ESM
child.exec( child.exec(
`${nodejs} ${execOptions} --eval "console.log(typeof import.meta);"`, ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "console.log(typeof import.meta);"`,
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.strictEqual(stdout, 'object\n'); assert.strictEqual(stdout, 'object\n');
})); }));
{
// Assert that packages can be imported cwd-relative with --eval // Assert that packages can be imported cwd-relative with --eval
const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval`;
const options = { ...opts, cwd: path.join(__dirname, '../..') };
child.exec( child.exec(
`${nodejs} ${execOptions} ` + `${cmd} "import './test/fixtures/es-modules/mjs-file.mjs'"`,
'--eval "import \'./test/fixtures/es-modules/mjs-file.mjs\'"', options,
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.strictEqual(stdout, '.mjs file\n'); assert.strictEqual(stdout, '.mjs file\n');
})); }));
@ -274,77 +275,69 @@ child.exec(
// Assert that packages can be dynamic imported initial cwd-relative with --eval // Assert that packages can be dynamic imported initial cwd-relative with --eval
child.exec( child.exec(
`${nodejs} ${execOptions} ` + cmd + ' "process.chdir(\'..\');' +
'--eval "process.chdir(\'..\');' +
'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"', 'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"',
options,
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.strictEqual(stdout, '.mjs file\n'); assert.strictEqual(stdout, '.mjs file\n');
})); }));
child.exec( child.exec(
`${nodejs} ` + cmd + ' "process.chdir(\'..\');' +
'--eval "process.chdir(\'..\');' +
'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"', 'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"',
options,
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.strictEqual(stdout, '.mjs file\n'); assert.strictEqual(stdout, '.mjs file\n');
})); }));
}
if (common.hasCrypto) { if (common.hasCrypto) {
// Assert that calls to crypto utils work without require. // Assert that calls to crypto utils work without require.
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -e "console.log(crypto.randomBytes(16).toString('hex'))"`,
'-e "console.log(crypto.randomBytes(16).toString(\'hex\'))"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /[0-9a-f]{32}/i); assert.match(stdout, /[0-9a-f]{32}/i);
})); }));
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -p "crypto.randomBytes(16).toString('hex')"`,
'-p "crypto.randomBytes(16).toString(\'hex\')"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /[0-9a-f]{32}/i); assert.match(stdout, /[0-9a-f]{32}/i);
})); }));
} }
// Assert that overriding crypto works. // Assert that overriding crypto works.
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -p "crypto=Symbol('test')"`,
'-p "crypto=Symbol(\'test\')"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /Symbol\(test\)/i); assert.match(stdout, /Symbol\(test\)/i);
})); }));
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -e "crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`,
'-e "crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /randomBytes\sundefined/); assert.match(stdout, /randomBytes\sundefined/);
})); }));
// Assert that overriding crypto with a local variable works. // Assert that overriding crypto with a local variable works.
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -e "const crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`,
'-e "const crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /randomBytes\sundefined/); assert.match(stdout, /randomBytes\sundefined/);
})); }));
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -e "let crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`,
'-e "let crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /randomBytes\sundefined/); assert.match(stdout, /randomBytes\sundefined/);
})); }));
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -e "var crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`,
'-e "var crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /randomBytes\sundefined/); assert.match(stdout, /randomBytes\sundefined/);
})); }));
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -p "const crypto = {randomBytes:1};typeof crypto.randomBytes"`,
'-p "const crypto = {randomBytes:1};typeof crypto.randomBytes"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /^number/); assert.match(stdout, /^number/);
})); }));
child.exec( child.exec(
`${nodejs} ` + ...common.escapePOSIXShell`"${process.execPath}" -p "let crypto = {randomBytes:1};typeof crypto.randomBytes"`,
'-p "let crypto = {randomBytes:1};typeof crypto.randomBytes"',
common.mustSucceed((stdout) => { common.mustSucceed((stdout) => {
assert.match(stdout, /^number/); assert.match(stdout, /^number/);
})); }));

View File

@ -11,18 +11,8 @@ const { exec, spawn } = require('child_process');
const { once } = require('events'); const { once } = require('events');
let stdOut; let stdOut;
// The execPath might contain chars that should be escaped in a shell context.
// On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path.
const execNode = (args, callback) => exec(
`"${common.isWindows ? process.execPath : '$NODE'}" ${args}`,
common.isWindows ? undefined : { env: { ...process.env, NODE: process.execPath } },
callback,
);
function startPrintHelpTest() { function startPrintHelpTest() {
execNode('--help', common.mustSucceed((stdout, stderr) => { exec(...common.escapePOSIXShell`"${process.execPath}" --help`, common.mustSucceed((stdout, stderr) => {
stdOut = stdout; stdOut = stdout;
validateNodePrintHelp(); validateNodePrintHelp();
})); }));

View File

@ -4,21 +4,10 @@ const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const { exec } = require('child_process'); const { exec } = require('child_process');
// The execPath might contain chars that should be escaped in a shell context.
// On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path.
const execNode = (args, callback) => exec(
`"${common.isWindows ? process.execPath : '$NODE'}" ${args}`,
common.isWindows ? undefined : { env: { ...process.env, NODE: process.execPath } },
callback,
);
// Should throw if -c and -e flags are both passed // Should throw if -c and -e flags are both passed
['-c', '--check'].forEach(function(checkFlag) { ['-c', '--check'].forEach(function(checkFlag) {
['-e', '--eval'].forEach(function(evalFlag) { ['-e', '--eval'].forEach(function(evalFlag) {
const args = [checkFlag, evalFlag, 'foo']; exec(...common.escapePOSIXShell`"${process.execPath}" ${checkFlag} ${evalFlag} foo`, common.mustCall((err, stdout, stderr) => {
execNode(args.join(' '), common.mustCall((err, stdout, stderr) => {
assert.strictEqual(err instanceof Error, true); assert.strictEqual(err instanceof Error, true);
assert.strictEqual(err.code, 9); assert.strictEqual(err.code, 9);
assert( assert(

View File

@ -621,12 +621,10 @@ assert.throws(
const msgfile = tmpdir.resolve('s5.msg'); const msgfile = tmpdir.resolve('s5.msg');
fs.writeFileSync(msgfile, msg); fs.writeFileSync(msgfile, msg);
const cmd = exec(...common.escapePOSIXShell`"${
`"${common.opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${ common.opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${
sigfile}" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "${ sigfile}" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "${msgfile
msgfile}"`; }"`, common.mustCall((err, stdout, stderr) => {
exec(cmd, common.mustCall((err, stdout, stderr) => {
assert(stdout.includes('Verified OK')); assert(stdout.includes('Verified OK'));
})); }));
} }

View File

@ -202,18 +202,15 @@ if (process.argv[2] === 'child') {
} else { } else {
tests.forEach(function(test, testIndex) { tests.forEach(function(test, testIndex) {
let testCmd = ''; const escapedArgs = common.escapePOSIXShell`"${process.execPath}" --abort-on-uncaught-exception "${__filename}" child ${testIndex}`;
if (!common.isWindows) { if (!common.isWindows) {
// Do not create core files, as it can take a lot of disk space on // Do not create core files, as it can take a lot of disk space on
// continuous testing and developers' machines // continuous testing and developers' machines
testCmd += 'ulimit -c 0 && '; escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0];
} }
testCmd += `"${process.argv[0]}" --abort-on-uncaught-exception ` +
`"${process.argv[1]}" child ${testIndex}`;
try { try {
child_process.execSync(testCmd); child_process.execSync(...escapedArgs);
} catch (e) { } catch (e) {
assert.fail(`Test index ${testIndex} failed: ${e}`); assert.fail(`Test index ${testIndex} failed: ${e}`);
} }

View File

@ -50,7 +50,7 @@ if (process.argv[2] === 'child') {
function runTestWithoutAbortOnUncaughtException() { function runTestWithoutAbortOnUncaughtException() {
child_process.exec( child_process.exec(
createTestCmdLine(), ...createTestCmdLine(),
function onTestDone(err, stdout, stderr) { function onTestDone(err, stdout, stderr) {
// When _not_ passing --abort-on-uncaught-exception, the process' // When _not_ passing --abort-on-uncaught-exception, the process'
// uncaughtException handler _must_ be called, and thus the error // uncaughtException handler _must_ be called, and thus the error
@ -70,7 +70,7 @@ function runTestWithoutAbortOnUncaughtException() {
} }
function runTestWithAbortOnUncaughtException() { function runTestWithAbortOnUncaughtException() {
child_process.exec(createTestCmdLine({ child_process.exec(...createTestCmdLine({
withAbortOnUncaughtException: true withAbortOnUncaughtException: true
}), function onTestDone(err, stdout, stderr) { }), function onTestDone(err, stdout, stderr) {
assert.notStrictEqual(err.code, RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE, assert.notStrictEqual(err.code, RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE,
@ -82,21 +82,14 @@ function runTestWithAbortOnUncaughtException() {
} }
function createTestCmdLine(options) { function createTestCmdLine(options) {
let testCmd = ''; const escapedArgs = common.escapePOSIXShell`"${process.execPath}" ${
options?.withAbortOnUncaughtException ? '--abort-on-uncaught-exception' : ''
} "${__filename}" child`;
if (!common.isWindows) { if (!common.isWindows) {
// Do not create core files, as it can take a lot of disk space on // Do not create core files, as it can take a lot of disk space on
// continuous testing and developers' machines // continuous testing and developers' machines
testCmd += 'ulimit -c 0 && '; escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0];
} }
return escapedArgs;
testCmd += `"${process.argv[0]}"`;
if (options?.withAbortOnUncaughtException) {
testCmd += ' --abort-on-uncaught-exception';
}
testCmd += ` "${process.argv[1]}" child`;
return testCmd;
} }

View File

@ -91,21 +91,17 @@ if (process.argv[2] === 'child') {
if (options.throwInDomainErrHandler) if (options.throwInDomainErrHandler)
throwInDomainErrHandlerOpt = 'throwInDomainErrHandler'; throwInDomainErrHandlerOpt = 'throwInDomainErrHandler';
let cmdToExec = '';
if (!common.isWindows) {
// Do not create core files, as it can take a lot of disk space on
// continuous testing and developers' machines
cmdToExec += 'ulimit -c 0 && ';
}
let useTryCatchOpt; let useTryCatchOpt;
if (options.useTryCatch) if (options.useTryCatch)
useTryCatchOpt = 'useTryCatch'; useTryCatchOpt = 'useTryCatch';
cmdToExec += `"${process.argv[0]}" ${cmdLineOption ? cmdLineOption : ''} "${ const escapedArgs = common.escapePOSIXShell`"${process.execPath}" ${cmdLineOption || ''} "${__filename}" child ${throwInDomainErrHandlerOpt || ''} ${useTryCatchOpt || ''}`;
process.argv[1]}" child ${throwInDomainErrHandlerOpt} ${useTryCatchOpt}`; if (!common.isWindows) {
// Do not create core files, as it can take a lot of disk space on
const child = exec(cmdToExec); // continuous testing and developers' machines
escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0];
}
const child = exec(...escapedArgs);
if (child) { if (child) {
child.on('exit', function onChildExited(exitCode, signal) { child.on('exit', function onChildExited(exitCode, signal) {

View File

@ -7,14 +7,13 @@ if (process.argv[2] === 'child') {
process.emitWarning('foo'); process.emitWarning('foo');
} else { } else {
function test(newEnv) { function test(newEnv) {
const env = { ...process.env, ...newEnv }; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`;
const cmd = `"${process.execPath}" "${__filename}" child`;
cp.exec(cmd, { env }, common.mustCall((err, stdout, stderr) => { cp.exec(cmd, { ...opts, env: { ...opts?.env, ...newEnv } }, common.mustCall((err, stdout, stderr) => {
assert.strictEqual(err, null); assert.strictEqual(err, null);
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
if (env.NODE_NO_WARNINGS === '1') if (newEnv.NODE_NO_WARNINGS === '1')
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');
else else
assert.match(stderr.trim(), /Warning: foo\n/); assert.match(stderr.trim(), /Warning: foo\n/);

View File

@ -28,8 +28,7 @@ const fixtures = require('../common/fixtures');
function errExec(script, option, callback) { function errExec(script, option, callback) {
callback = typeof option === 'function' ? option : callback; callback = typeof option === 'function' ? option : callback;
option = typeof option === 'string' ? option : ''; option = typeof option === 'string' ? option : '';
const cmd = `"${process.argv[0]}" ${option} "${fixtures.path(script)}"`; return exec(...common.escapePOSIXShell`"${process.execPath}" ${option} "${fixtures.path(script)}"`, (err, stdout, stderr) => {
return exec(cmd, (err, stdout, stderr) => {
// There was some error // There was some error
assert.ok(err); assert.ok(err);

View File

@ -198,7 +198,7 @@ if (!common.isWindows) {
const filename = `${tmpdir.path}/foo.pipe`; const filename = `${tmpdir.path}/foo.pipe`;
const mkfifoResult = child_process.spawnSync('mkfifo', [filename]); const mkfifoResult = child_process.spawnSync('mkfifo', [filename]);
if (!mkfifoResult.error) { if (!mkfifoResult.error) {
child_process.exec(`echo "xyz foobar" > '${filename}'`); child_process.exec(...common.escapePOSIXShell`echo "xyz foobar" > "${filename}"`);
const stream = new fs.createReadStream(filename, common.mustNotMutateObjectDeep({ end: 1 })); const stream = new fs.createReadStream(filename, common.mustNotMutateObjectDeep({ end: 1 }));
stream.data = ''; stream.data = '';

View File

@ -25,12 +25,10 @@ const data2 = 'World';
const expected = `${data1}\n${data2}\n`; const expected = `${data1}\n${data2}\n`;
const exec = require('child_process').exec; const exec = require('child_process').exec;
const f = JSON.stringify(__filename);
const node = JSON.stringify(process.execPath);
function test(child) { function test(child) {
const cmd = `(echo ${data1}; sleep 0.5; echo ${data2}) | ${node} ${f} ${child}`; exec(...common.escapePOSIXShell`(echo "${data1}"; sleep 0.5; echo "${data2}") | "${process.execPath}" "${__filename}" "${child}"`,
exec(cmd, common.mustSucceed((stdout, stderr) => { common.mustSucceed((stdout, stderr) => {
assert.strictEqual( assert.strictEqual(
stdout, stdout,
expected, expected,

View File

@ -36,9 +36,7 @@ const fixtures = require('../common/fixtures');
function test(env, cb) { function test(env, cb) {
const filename = fixtures.path('test-fs-readfile-error.js'); const filename = fixtures.path('test-fs-readfile-error.js');
const execPath = `"${process.execPath}" "${filename}"`; exec(...common.escapePOSIXShell`"${process.execPath}" "${filename}"`, (err, stdout, stderr) => {
const options = { env: { ...process.env, ...env } };
exec(execPath, options, (err, stdout, stderr) => {
assert(err); assert(err);
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');
assert.notStrictEqual(stderr, ''); assert.notStrictEqual(stderr, '');

View File

@ -25,10 +25,8 @@ tmpdir.refresh();
fs.writeFileSync(filename, dataExpected); fs.writeFileSync(filename, dataExpected);
const exec = require('child_process').exec; const exec = require('child_process').exec;
const f = JSON.stringify(__filename); const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child < "${filename}"`;
const node = JSON.stringify(process.execPath); exec(cmd, { ...opts, maxBuffer: 1000000 }, common.mustSucceed((stdout, stderr) => {
const cmd = `cat ${filename} | ${node} ${f} child`;
exec(cmd, { maxBuffer: 1000000 }, common.mustSucceed((stdout, stderr) => {
assert.strictEqual( assert.strictEqual(
stdout, stdout,
dataExpected, dataExpected,

View File

@ -43,10 +43,7 @@ const filename = fixtures.path('readfile_pipe_test.txt');
const dataExpected = fs.readFileSync(filename).toString(); const dataExpected = fs.readFileSync(filename).toString();
const exec = require('child_process').exec; const exec = require('child_process').exec;
const f = JSON.stringify(__filename); exec(...common.escapePOSIXShell`"${process.execPath}" "${__filename}" child < "${filename}"`, common.mustSucceed((stdout, stderr) => {
const node = JSON.stringify(process.execPath);
const cmd = `cat ${filename} | ${node} ${f} child`;
exec(cmd, common.mustSucceed((stdout, stderr) => {
assert.strictEqual( assert.strictEqual(
stdout, stdout,
dataExpected, dataExpected,

View File

@ -22,12 +22,10 @@ tmpdir.refresh();
fs.writeFileSync(filename, dataExpected); fs.writeFileSync(filename, dataExpected);
const exec = require('child_process').exec; const exec = require('child_process').exec;
const f = JSON.stringify(__filename); const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child < "${filename}"`;
const node = JSON.stringify(process.execPath);
const cmd = `cat ${filename} | ${node} ${f} child`;
exec( exec(
cmd, cmd,
{ maxBuffer: 1000000 }, { ...opts, maxBuffer: 1_000_000 },
common.mustSucceed((stdout, stderr) => { common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, dataExpected); assert.strictEqual(stdout, dataExpected);
assert.strictEqual(stderr, ''); assert.strictEqual(stderr, '');

View File

@ -19,8 +19,8 @@ if (process.argv[2] === 'child') {
tmpdir.refresh(); tmpdir.refresh();
fs.writeFileSync(filename, '.'.repeat(1 << 16)); // Exceeds RLIMIT_FSIZE. fs.writeFileSync(filename, '.'.repeat(1 << 16)); // Exceeds RLIMIT_FSIZE.
} else { } else {
const cmd = `ulimit -f 1 && '${process.execPath}' '${__filename}' child`; const [cmd, opts] = common.escapePOSIXShell`ulimit -f 1 && "${process.execPath}" "${__filename}" child`;
const result = child_process.spawnSync('/bin/sh', ['-c', cmd]); const result = child_process.spawnSync('/bin/sh', ['-c', cmd], opts);
const haystack = result.stderr.toString(); const haystack = result.stderr.toString();
const needle = 'Error: EFBIG: file too large, write'; const needle = 'Error: EFBIG: file too large, write';
const ok = haystack.includes(needle); const ok = haystack.includes(needle);

View File

@ -15,20 +15,12 @@ if (process.argv[2] === 'child') {
assert.ok(process.permission.has('child')); assert.ok(process.permission.has('child'));
} }
// The execPath might contain chars that should be escaped in a shell context.
// On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path.
const execNode = (args) => childProcess.execSync(
`"${common.isWindows ? process.execPath : '$NODE'}" ${args}`,
common.isWindows ? undefined : { env: { ...process.env, NODE: process.execPath } },
);
// When a permission is set by cli, the process shouldn't be able // When a permission is set by cli, the process shouldn't be able
// to spawn unless --allow-child-process is sent // to spawn unless --allow-child-process is sent
{ {
// doesNotThrow // doesNotThrow
childProcess.spawnSync(process.execPath, ['--version']); childProcess.spawnSync(process.execPath, ['--version']);
execNode('--version'); childProcess.execSync(...common.escapePOSIXShell`"${process.execPath}" --version`);
childProcess.fork(__filename, ['child']); childProcess.fork(__filename, ['child']);
childProcess.execFileSync(process.execPath, ['--version']); childProcess.execFileSync(process.execPath, ['--version']);
} }

View File

@ -15,14 +15,6 @@ if (process.argv[2] === 'child') {
assert.ok(!process.permission.has('child')); assert.ok(!process.permission.has('child'));
} }
// The execPath might contain chars that should be escaped in a shell context.
// On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path.
const execNode = (args) => [
`"${common.isWindows ? process.execPath : '$NODE'}" ${args}`,
common.isWindows ? undefined : { env: { ...process.env, NODE: process.execPath } },
];
// When a permission is set by cli, the process shouldn't be able // When a permission is set by cli, the process shouldn't be able
// to spawn // to spawn
{ {
@ -39,13 +31,13 @@ const execNode = (args) => [
permission: 'ChildProcess', permission: 'ChildProcess',
})); }));
assert.throws(() => { assert.throws(() => {
childProcess.exec(...execNode('--version')); childProcess.exec(...common.escapePOSIXShell`"${process.execPath}" --version`);
}, common.expectsError({ }, common.expectsError({
code: 'ERR_ACCESS_DENIED', code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess', permission: 'ChildProcess',
})); }));
assert.throws(() => { assert.throws(() => {
childProcess.execSync(...execNode('--version')); childProcess.execSync(...common.escapePOSIXShell`"${process.execPath}" --version`);
}, common.expectsError({ }, common.expectsError({
code: 'ERR_ACCESS_DENIED', code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess', permission: 'ChildProcess',
@ -57,13 +49,13 @@ const execNode = (args) => [
permission: 'ChildProcess', permission: 'ChildProcess',
})); }));
assert.throws(() => { assert.throws(() => {
childProcess.execFile(...execNode('--version')); childProcess.execFile(...common.escapePOSIXShell`"${process.execPath}" --version`);
}, common.expectsError({ }, common.expectsError({
code: 'ERR_ACCESS_DENIED', code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess', permission: 'ChildProcess',
})); }));
assert.throws(() => { assert.throws(() => {
childProcess.execFileSync(...execNode('--version')); childProcess.execFileSync(...common.escapePOSIXShell`"${process.execPath}" --version`);
}, common.expectsError({ }, common.expectsError({
code: 'ERR_ACCESS_DENIED', code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess', permission: 'ChildProcess',

View File

@ -13,8 +13,8 @@ if (!common.isMainThread)
const selfRefModule = fixtures.path('self_ref_module'); const selfRefModule = fixtures.path('self_ref_module');
const fixtureA = fixtures.path('printA.js'); const fixtureA = fixtures.path('printA.js');
exec(`"${nodeBinary}" -r self_ref "${fixtureA}"`, { cwd: selfRefModule }, const [cmd, opts] = common.escapePOSIXShell`"${nodeBinary}" -r self_ref "${fixtureA}"`;
(err, stdout, stderr) => { exec(cmd, { ...opts, cwd: selfRefModule },
assert.ifError(err); common.mustSucceed((stdout, stderr) => {
assert.strictEqual(stdout, 'A\n'); assert.strictEqual(stdout, 'A\n');
}); }));

View File

@ -7,4 +7,4 @@ const { exec } = require('child_process');
const kNodeBinary = process.argv[0]; const kNodeBinary = process.argv[0];
exec(`"${kNodeBinary}" -r "${worker}" -pe "1+1"`, common.mustSucceed()); exec(...common.escapePOSIXShell`"${kNodeBinary}" -r "${worker}" -pe "1+1"`, common.mustSucceed());

View File

@ -10,15 +10,11 @@ const stdoutScript = fixtures.path('echo-close-check.js');
const tmpFile = tmpdir.resolve('stdin.txt'); const tmpFile = tmpdir.resolve('stdin.txt');
const string = fixtures.utf8TestText; const string = fixtures.utf8TestText;
const cmd = `"${process.argv[0]}" "${stdoutScript}" < "${tmpFile}"`;
tmpdir.refresh(); tmpdir.refresh();
console.log(`${cmd}\n\n`);
fs.writeFileSync(tmpFile, string); fs.writeFileSync(tmpFile, string);
childProcess.exec(cmd, common.mustCall(function(err, stdout, stderr) { childProcess.exec(...common.escapePOSIXShell`"${process.argv0}" "${stdoutScript}" < "${tmpFile}"`, common.mustCall(function(err, stdout, stderr) {
fs.unlinkSync(tmpFile); fs.unlinkSync(tmpFile);
assert.ifError(err); assert.ifError(err);

View File

@ -29,8 +29,8 @@ if (process.argv[2] === 'child') {
} }
// Run the script in a shell but close stdout and stderr. // Run the script in a shell but close stdout and stderr.
const cmd = `"${process.execPath}" "${__filename}" child 1>&- 2>&-`; const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child 1>&- 2>&-`;
const proc = spawn('/bin/sh', ['-c', cmd], { stdio: 'inherit' }); const proc = spawn('/bin/sh', ['-c', cmd], { ...opts, stdio: 'inherit' });
proc.on('exit', common.mustCall(function(exitCode) { proc.on('exit', common.mustCall(function(exitCode) {
assert.strictEqual(exitCode, 0); assert.strictEqual(exitCode, 0);

View File

@ -7,12 +7,9 @@ const { getSystemErrorName } = require('util');
const testScript = fixtures.path('catch-stdout-error.js'); const testScript = fixtures.path('catch-stdout-error.js');
const cmd = `${JSON.stringify(process.execPath)} ` + const child = child_process.exec(
`${JSON.stringify(testScript)} | ` + ...common.escapePOSIXShell`"${process.execPath}" "${testScript}" | "${process.execPath}" -pe "process.stdin.on('data' , () => process.exit(1))"`
`${JSON.stringify(process.execPath)} ` + );
'-pe "process.stdin.on(\'data\' , () => process.exit(1))"';
const child = child_process.exec(cmd);
let output = ''; let output = '';
child.stderr.on('data', function(c) { child.stderr.on('data', function(c) {
@ -21,12 +18,7 @@ child.stderr.on('data', function(c) {
child.on('close', common.mustCall(function(code) { child.on('close', common.mustCall(function(code) {
try {
output = JSON.parse(output); output = JSON.parse(output);
} catch {
console.error(output);
process.exit(1);
}
assert.strictEqual(output.code, 'EPIPE'); assert.strictEqual(output.code, 'EPIPE');
assert.strictEqual(getSystemErrorName(output.errno), 'EPIPE'); assert.strictEqual(getSystemErrorName(output.errno), 'EPIPE');

View File

@ -13,9 +13,6 @@ const tmpFile = tmpdir.resolve('stdout.txt');
tmpdir.refresh(); tmpdir.refresh();
function test(size, useBuffer, cb) { function test(size, useBuffer, cb) {
const cmd = `"${process.argv[0]}" "${
useBuffer ? scriptBuffer : scriptString}" ${size} > "${tmpFile}"`;
try { try {
fs.unlinkSync(tmpFile); fs.unlinkSync(tmpFile);
} catch { } catch {
@ -24,7 +21,9 @@ function test(size, useBuffer, cb) {
console.log(`${size} chars to ${tmpFile}...`); console.log(`${size} chars to ${tmpFile}...`);
childProcess.exec(cmd, common.mustSucceed(() => { childProcess.exec(...common.escapePOSIXShell`"${
process.execPath}" "${useBuffer ? scriptBuffer : scriptString}" ${size} > "${tmpFile
}"`, common.mustSucceed(() => {
console.log('done!'); console.log('done!');
const stat = fs.statSync(tmpFile); const stat = fs.statSync(tmpFile);

View File

@ -13,14 +13,7 @@ if (process.argv[2] === 'child') {
); );
} else { } else {
const cp = require('child_process'); const cp = require('child_process');
cp.exec([ cp.exec(...common.escapePOSIXShell`echo hello | "${process.execPath}" "${__filename}" child`, common.mustSucceed((stdout) => {
'echo',
'hello',
'|',
`"${process.execPath}"`,
`"${__filename}"`,
'child',
].join(' '), common.mustSucceed((stdout) => {
assert.strictEqual(stdout.split(os.EOL).shift().trim(), 'hello'); assert.strictEqual(stdout.split(os.EOL).shift().trim(), 'hello');
})); }));
} }

View File

@ -49,10 +49,10 @@ const server = tls.createServer(options, common.mustCall(function(conn) {
})); }));
server.listen(0, '127.0.0.1', common.mustCall(function() { server.listen(0, '127.0.0.1', common.mustCall(function() {
const cmd = `"${common.opensslCli}" s_client -cipher ${ const cmd = common.escapePOSIXShell`"${common.opensslCli}" s_client -cipher ${
options.ciphers} -connect 127.0.0.1:${this.address().port}`; options.ciphers} -connect 127.0.0.1:${this.address().port}`;
exec(cmd, common.mustSucceed((stdout, stderr) => { exec(...cmd, common.mustSucceed((stdout, stderr) => {
assert(stdout.includes(reply)); assert(stdout.includes(reply));
server.close(); server.close();
})); }));

View File

@ -47,9 +47,7 @@ if (process.argv[2] === 'child') {
} else { } else {
// Limit the number of open files, to force workers to fail. // Limit the number of open files, to force workers to fail.
let testCmd = `ulimit -n ${OPENFILES} && `; const cp = child_process.exec(...common.escapePOSIXShell`ulimit -n ${OPENFILES} && "${process.execPath}" "${__filename}" child`);
testCmd += `${process.execPath} ${__filename} child`;
const cp = child_process.exec(testCmd);
// Turn on the child streams for debugging purposes. // Turn on the child streams for debugging purposes.
let stdout = ''; let stdout = '';

View File

@ -30,12 +30,14 @@ const fs = require('fs');
const ulimit = Number(child_process.execSync('ulimit -Hn')); const ulimit = Number(child_process.execSync('ulimit -Hn'));
if (ulimit > 64 || Number.isNaN(ulimit)) { if (ulimit > 64 || Number.isNaN(ulimit)) {
const [cmd, opts] = common.escapePOSIXShell`ulimit -n 64 && "${process.execPath}" "${__filename}"`;
// Sorry about this nonsense. It can be replaced if // Sorry about this nonsense. It can be replaced if
// https://github.com/nodejs/node-v0.x-archive/pull/2143#issuecomment-2847886 // https://github.com/nodejs/node-v0.x-archive/pull/2143#issuecomment-2847886
// ever happens. // ever happens.
const result = child_process.spawnSync( const result = child_process.spawnSync(
'/bin/sh', '/bin/sh',
['-c', `ulimit -n 64 && '${process.execPath}' '${__filename}'`] ['-c', cmd],
opts,
); );
assert.strictEqual(result.stdout.toString(), ''); assert.strictEqual(result.stdout.toString(), '');
assert.strictEqual(result.stderr.toString(), ''); assert.strictEqual(result.stderr.toString(), '');

View File

@ -1,16 +1,14 @@
'use strict'; 'use strict';
require('../common'); const common = require('../common');
const { exec } = require('child_process'); const { exec } = require('child_process');
const { test } = require('node:test'); const { test } = require('node:test');
const fixtures = require('../common/fixtures'); const fixtures = require('../common/fixtures');
const node = process.execPath;
// Test both sets of arguments that check syntax // Test both sets of arguments that check syntax
const syntaxArgs = [ const syntaxArgs = [
['-c'], '-c',
['--check'], '--check',
]; ];
// Match on the name of the `Error` but not the message as it is different // Match on the name of the `Error` but not the message as it is different
@ -27,13 +25,10 @@ const syntaxErrorRE = /^SyntaxError: \b/m;
const path = fixtures.path(file); const path = fixtures.path(file);
// Loop each possible option, `-c` or `--check` // Loop each possible option, `-c` or `--check`
syntaxArgs.forEach((args) => { syntaxArgs.forEach((flag) => {
test(`Checking syntax for ${file} with ${args.join(' ')}`, async (t) => { test(`Checking syntax for ${file} with ${flag}`, async (t) => {
const _args = args.concat(path);
const cmd = [node, ..._args].join(' ');
try { try {
const { stdout, stderr } = await execPromise(cmd); const { stdout, stderr } = await execNode(flag, path);
// No stdout should be produced // No stdout should be produced
t.assert.strictEqual(stdout, ''); t.assert.strictEqual(stdout, '');
@ -51,9 +46,9 @@ const syntaxErrorRE = /^SyntaxError: \b/m;
}); });
// Helper function to promisify exec // Helper function to promisify exec
function execPromise(cmd) { function execNode(flag, path) {
const { promise, resolve, reject } = Promise.withResolvers(); const { promise, resolve, reject } = Promise.withResolvers();
exec(cmd, (err, stdout, stderr) => { exec(...common.escapePOSIXShell`"${process.execPath}" ${flag} "${path}"`, (err, stdout, stderr) => {
if (err) return reject({ ...err, stdout, stderr }); if (err) return reject({ ...err, stdout, stderr });
resolve({ stdout, stderr }); resolve({ stdout, stderr });
}); });

View File

@ -5,15 +5,6 @@ const assert = require('assert');
const { exec } = require('child_process'); const { exec } = require('child_process');
const fixtures = require('../common/fixtures'); const fixtures = require('../common/fixtures');
// The execPath might contain chars that should be escaped in a shell context.
// On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path.
const execNode = (flag, file, callback) => exec(
`"${common.isWindows ? process.execPath : '$NODE'}" ${flag} "${common.isWindows ? file : '$FILE'}"`,
common.isWindows ? undefined : { env: { ...process.env, NODE: process.execPath, FILE: file } },
callback,
);
// Test both sets of arguments that check syntax // Test both sets of arguments that check syntax
const syntaxArgs = [ const syntaxArgs = [
'-c', '-c',
@ -31,7 +22,7 @@ const notFoundRE = /^Error: Cannot find module/m;
// Loop each possible option, `-c` or `--check` // Loop each possible option, `-c` or `--check`
syntaxArgs.forEach(function(flag) { syntaxArgs.forEach(function(flag) {
execNode(flag, file, common.mustCall((err, stdout, stderr) => { exec(...common.escapePOSIXShell`"${process.execPath}" ${flag} "${file}"`, common.mustCall((err, stdout, stderr) => {
// No stdout should be produced // No stdout should be produced
assert.strictEqual(stdout, ''); assert.strictEqual(stdout, '');

View File

@ -5,15 +5,6 @@ const assert = require('assert');
const { exec } = require('child_process'); const { exec } = require('child_process');
const fixtures = require('../common/fixtures'); const fixtures = require('../common/fixtures');
// The execPath might contain chars that should be escaped in a shell context.
// On non-Windows, we can pass the path via the env; `"` is not a valid char on
// Windows, so we can simply pass the path.
const execNode = (flag, file, callback) => exec(
`"${common.isWindows ? process.execPath : '$NODE'}" ${flag} "${common.isWindows ? file : '$FILE'}"`,
common.isWindows ? undefined : { env: { ...process.env, NODE: process.execPath, FILE: file } },
callback,
);
// Test both sets of arguments that check syntax // Test both sets of arguments that check syntax
const syntaxArgs = [ const syntaxArgs = [
'-c', '-c',
@ -33,7 +24,7 @@ const syntaxArgs = [
// Loop each possible option, `-c` or `--check` // Loop each possible option, `-c` or `--check`
syntaxArgs.forEach(function(flag) { syntaxArgs.forEach(function(flag) {
execNode(flag, file, common.mustCall((err, stdout, stderr) => { exec(...common.escapePOSIXShell`"${process.execPath}" ${flag} "${file}"`, common.mustCall((err, stdout, stderr) => {
if (err) { if (err) {
console.log('-- stdout --'); console.log('-- stdout --');
console.log(stdout); console.log(stdout);

View File

@ -35,8 +35,7 @@ if (process.env.TEST_INIT) {
process.env.TEST_INIT = 1; process.env.TEST_INIT = 1;
function test(file, expected) { function test(file, expected) {
const path = `"${process.execPath}" ${file}`; child.exec(...common.escapePOSIXShell`"${process.execPath}" "${file}"`, common.mustSucceed((out) => {
child.exec(path, { env: process.env }, common.mustSucceed((out) => {
assert.strictEqual(out, expected, `'node ${file}' failed!`); assert.strictEqual(out, expected, `'node ${file}' failed!`);
})); }));
} }