Skip to content

Commit 6b74734

Browse files
committed
test: add test for spawn path traversal with access errors
Adds a test case that places inaccessible directories and files in PATH to verify that spawn correctly returns ENOENT for non-existent commands, ensuring robustness of the EACCES handling fix.
1 parent b0ef4b5 commit 6b74734

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

lib/internal/child_process.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,7 @@ function verifyENOENT(file, envPairs) {
8787
}
8888
}
8989
}
90-
if (!envPath) {
91-
envPath = process.env.PATH;
92-
}
90+
envPath ||= process.env.PATH;
9391
if (!envPath) return false;
9492
const paths = envPath.split(path.delimiter);
9593
for (let i = 0; i < paths.length; i++) {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const cp = require('child_process');
5+
const fs = require('fs');
6+
const path = require('path');
7+
8+
const tmpdir = require('../common/tmpdir');
9+
tmpdir.refresh();
10+
11+
// Scenario: PATH contains directories that are inaccessible or are actually files.
12+
// The system spawn (execvp) might return EACCES in these cases on some platforms.
13+
// We want to ensure Node.js consistently reports ENOENT if the command is truly missing.
14+
15+
const noPermDir = path.join(tmpdir.path, 'no-perm-dir');
16+
fs.mkdirSync(noPermDir);
17+
18+
const fileInPath = path.join(tmpdir.path, 'file-in-path');
19+
fs.writeFileSync(fileInPath, '');
20+
21+
if (!common.isWindows) {
22+
try {
23+
fs.chmodSync(noPermDir, '000');
24+
} catch (e) {
25+
// If we can't chmod (e.g. root or weird fs), skip the permission part of the test
26+
// but keep the structure.
27+
console.log('# Skipped chmod 000 on no-perm-dir due to error:', e.message);
28+
}
29+
}
30+
31+
// Ensure cleanup restores permissions so tmpdir can be removed
32+
process.prependListener('exit', () => {
33+
if (!common.isWindows && fs.existsSync(noPermDir)) {
34+
try {
35+
fs.chmodSync(noPermDir, '777');
36+
} catch {
37+
// Ignore cleanup errors during exit
38+
}
39+
}
40+
});
41+
42+
const env = { ...process.env };
43+
const sep = path.delimiter;
44+
45+
// Prepend the problematic entries to PATH
46+
env.PATH = `${noPermDir}${sep}${fileInPath}${sep}${env.PATH}`;
47+
48+
const command = 'command-that-does-not-exist-at-all-' + Date.now();
49+
50+
const child = cp.spawn(command, { env });
51+
52+
child.on('error', common.mustCall((err) => {
53+
assert.strictEqual(err.code, 'ENOENT');
54+
assert.strictEqual(err.syscall, `spawn ${command}`);
55+
}));
56+
57+
// Also test sync
58+
try {
59+
cp.spawnSync(command, { env });
60+
} catch (err) {
61+
assert.strictEqual(err.code, 'ENOENT');
62+
}

0 commit comments

Comments
 (0)