node/test/parallel/test-child-process-spawn-windows-batch-file.js
Tobias Nießen 3790d524c1
src: remove erroneous CVE-2024-27980 revert option
No security reverts should exist on the main branch.

PR-URL: https://github.com/nodejs/node/pull/52543
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
2024-04-18 13:09:09 +00:00

98 lines
2.7 KiB
JavaScript

'use strict';
// Node.js on Windows should not be able to spawn batch files directly,
// only when the 'shell' option is set. An undocumented feature of the
// Win32 CreateProcess API allows spawning .bat and .cmd files directly
// but it does not sanitize arguments. We cannot do that automatically
// because it's sometimes impossible to escape arguments unambiguously.
//
// Expectation: spawn() and spawnSync() raise EINVAL if and only if:
//
// 1. 'shell' option is unset
// 2. Platform is Windows
// 3. Filename ends in .bat or .cmd, case-insensitive
//
// exec() and execSync() are unchanged.
const common = require('../common');
const cp = require('child_process');
const assert = require('assert');
const { isWindows } = common;
const expectedCode = isWindows ? 'EINVAL' : 'ENOENT';
const expectedStatus = isWindows ? 1 : 127;
const suffixes =
'BAT bAT BaT baT BAt bAt Bat bat CMD cMD CmD cmD CMd cMd Cmd cmd'
.split(' ');
function testExec(filename) {
return new Promise((resolve) => {
cp.exec(filename).once('exit', common.mustCall(function(status) {
assert.strictEqual(status, expectedStatus);
resolve();
}));
});
}
function testExecSync(filename) {
let e;
try {
cp.execSync(filename);
} catch (_e) {
e = _e;
}
if (!e) throw new Error(`Exception expected for ${filename}`);
assert.strictEqual(e.status, expectedStatus);
}
function testSpawn(filename, code) {
// Batch file case is a synchronous error, file-not-found is asynchronous.
if (code === 'EINVAL') {
let e;
try {
cp.spawn(filename);
} catch (_e) {
e = _e;
}
if (!e) throw new Error(`Exception expected for ${filename}`);
assert.strictEqual(e.code, code);
} else {
return new Promise((resolve) => {
cp.spawn(filename).once('error', common.mustCall(function(e) {
assert.strictEqual(e.code, code);
resolve();
}));
});
}
}
function testSpawnSync(filename, code) {
{
const r = cp.spawnSync(filename);
assert.strictEqual(r.error.code, code);
}
{
const r = cp.spawnSync(filename, { shell: true });
assert.strictEqual(r.status, expectedStatus);
}
}
testExecSync('./nosuchdir/nosuchfile');
testSpawnSync('./nosuchdir/nosuchfile', 'ENOENT');
for (const suffix of suffixes) {
testExecSync(`./nosuchdir/nosuchfile.${suffix}`);
testSpawnSync(`./nosuchdir/nosuchfile.${suffix}`, expectedCode);
}
go().catch((ex) => { throw ex; });
async function go() {
await testExec('./nosuchdir/nosuchfile');
await testSpawn('./nosuchdir/nosuchfile', 'ENOENT');
for (const suffix of suffixes) {
await testExec(`./nosuchdir/nosuchfile.${suffix}`);
await testSpawn(`./nosuchdir/nosuchfile.${suffix}`, expectedCode);
}
}