mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-01 02:11:54 +00:00
Merge pull request #275 from PR0M3TH3AN/fix-install-dir-command-injection-4005485668731201594
🔒 Fix command injection via install directory name
This commit is contained in:
2 files changed
+128
-4
No files matched your search
+19
-4
@@ -147,11 +147,26 @@ async function interactiveInit(cwd) {
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveConfiguration(cwd, mockAnswers) {
|
||||
if (mockAnswers) {
|
||||
return mockAnswers;
|
||||
function validateInstallDir(dir) {
|
||||
if (dir === '.') return;
|
||||
|
||||
// Strict validation to prevent command injection
|
||||
// Only allow alphanumeric, hyphens, underscores, slashes, and periods.
|
||||
if (!/^[a-zA-Z0-9_\-\/\.]+$/.test(dir)) {
|
||||
throw new Error(`Invalid directory name: "${dir}". Only alphanumeric characters, hyphens, underscores, slashes, and periods are allowed.`);
|
||||
}
|
||||
return await interactiveInit(cwd);
|
||||
}
|
||||
|
||||
async function resolveConfiguration(cwd, mockAnswers) {
|
||||
let config;
|
||||
if (mockAnswers) {
|
||||
config = mockAnswers;
|
||||
} else {
|
||||
config = await interactiveInit(cwd);
|
||||
}
|
||||
|
||||
validateInstallDir(config.installDir);
|
||||
return config;
|
||||
}
|
||||
|
||||
function ensureInstallDirectory(paths, force, installDir) {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import { cmdInit } from '../src/ops.mjs';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const TEST_DIR = 'test_validation_env';
|
||||
|
||||
test('cmdInit should validate install directory name', async (t) => {
|
||||
if (fs.existsSync(TEST_DIR)) {
|
||||
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
||||
}
|
||||
fs.mkdirSync(TEST_DIR);
|
||||
|
||||
const mockPkg = { name: "host", scripts: {} };
|
||||
fs.writeFileSync(path.join(TEST_DIR, 'package.json'), JSON.stringify(mockPkg));
|
||||
|
||||
await t.test('should reject directory with semicolon', async () => {
|
||||
await assert.rejects(async () => {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: 'torch; echo pwned',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
}, /Invalid directory name/);
|
||||
});
|
||||
|
||||
await t.test('should reject directory with spaces', async () => {
|
||||
await assert.rejects(async () => {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: 'torch dir',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
}, /Invalid directory name/);
|
||||
});
|
||||
|
||||
await t.test('should reject directory with quotes', async () => {
|
||||
await assert.rejects(async () => {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: 'torch"dir',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
}, /Invalid directory name/);
|
||||
});
|
||||
|
||||
await t.test('should reject directory with backticks', async () => {
|
||||
await assert.rejects(async () => {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: 'torch`dir',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
}, /Invalid directory name/);
|
||||
});
|
||||
|
||||
await t.test('should reject directory with $', async () => {
|
||||
await assert.rejects(async () => {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: 'torch$dir',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
}, /Invalid directory name/);
|
||||
});
|
||||
|
||||
await t.test('should accept valid directory names', async () => {
|
||||
try {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: 'valid-dir_123',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
} catch (e) {
|
||||
// It might fail because of other initialization steps, but shouldn't fail validation
|
||||
assert.notMatch(e.message, /Invalid directory name/);
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('should accept "."', async () => {
|
||||
try {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: '.',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
} catch (e) {
|
||||
assert.notMatch(e.message, /Invalid directory name/);
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('should accept nested paths with slashes', async () => {
|
||||
try {
|
||||
await cmdInit(false, TEST_DIR, {
|
||||
installDir: 'path/to/torch',
|
||||
namespace: 'ns',
|
||||
relays: []
|
||||
});
|
||||
} catch (e) {
|
||||
assert.notMatch(e.message, /Invalid directory name/);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
if (fs.existsSync(TEST_DIR)) {
|
||||
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user