Skip to content

Commit b6d5d1b

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 b6d5d1b

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
}
38+
});
39+
40+
const env = { ...process.env };
41+
const sep = path.delimiter;
42+
43+
// Prepend the problematic entries to PATH
44+
env.PATH = `${noPermDir}${sep}${fileInPath}${sep}${env.PATH}`;
45+
46+
const command = 'command-that-does-not-exist-at-all-' + Date.now();
47+
48+
const child = cp.spawn(command, { env });
49+
50+
child.on('error', common.mustCall((err) => {
51+
assert.strictEqual(err.code, 'ENOENT');
52+
assert.strictEqual(err.syscall, `spawn ${command}`);
53+
}));
54+
55+
// Also test sync
56+
try {
57+
cp.spawnSync(command, { env });
58+
} catch (err) {
59+
assert.strictEqual(err.code, 'ENOENT');
60+
}

0 commit comments

Comments
 (0)