mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
fix: restrict dashboard file access to allowed directories
This patch addresses a security vulnerability where the dashboard server could serve any file from the package root. Changes: - Implemented an allowlist in `src/dashboard.mjs` to restrict file access to: - `dashboard/` - `assets/` - `src/docs/` - `torch-config.json` - Added `test/dashboard-security.test.mjs` to verify the fix and prevent regressions. The fix ensures that sensitive files like `package.json` or source code cannot be accessed via the dashboard server. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
4d11b2b710
commit
171836d2ac
2 files changed
+89
No files matched your search
@@ -90,6 +90,25 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT, host = '127.0.
|
||||
const safePath = path.normalize(pathname).replace(new RegExp('^(\\.\\.[\\/\\\\])+'), '');
|
||||
let filePath = path.join(packageRoot, safePath);
|
||||
|
||||
// Security check: restrict access to allowed paths
|
||||
const allowedPaths = [
|
||||
path.join(packageRoot, 'dashboard'),
|
||||
path.join(packageRoot, 'assets'),
|
||||
path.join(packageRoot, 'src', 'docs'),
|
||||
path.join(packageRoot, 'torch-config.json')
|
||||
];
|
||||
|
||||
const isAllowed = allowedPaths.some(allowedPath => {
|
||||
const rel = path.relative(allowedPath, filePath);
|
||||
return !rel.startsWith('..') && !path.isAbsolute(rel);
|
||||
});
|
||||
|
||||
if (!isAllowed) {
|
||||
res.writeHead(403);
|
||||
res.end('Forbidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// If directory, try index.html
|
||||
let fileStat = await statSafe(filePath);
|
||||
if (fileStat && fileStat.isDirectory()) {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import http from 'node:http';
|
||||
import { cmdDashboard } from '../src/dashboard.mjs';
|
||||
|
||||
test('Dashboard File Access Security', async (t) => {
|
||||
const testPort = 4175;
|
||||
let server;
|
||||
|
||||
t.beforeEach(async () => {
|
||||
server = await cmdDashboard(testPort);
|
||||
});
|
||||
|
||||
t.afterEach(() => {
|
||||
if (server) server.close();
|
||||
});
|
||||
|
||||
function get(path) {
|
||||
return new Promise((resolve) => {
|
||||
http.get({
|
||||
host: '127.0.0.1',
|
||||
port: testPort,
|
||||
path: path
|
||||
}, (res) => {
|
||||
// Consume data to avoid memory leaks
|
||||
res.resume();
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
await t.test('allows access to dashboard assets', async () => {
|
||||
const res = await get('/dashboard/index.html');
|
||||
assert.strictEqual(res.statusCode, 200);
|
||||
});
|
||||
|
||||
await t.test('allows access to global assets', async () => {
|
||||
// assuming favicon.svg exists
|
||||
const res = await get('/assets/favicon.svg');
|
||||
assert.strictEqual(res.statusCode, 200);
|
||||
});
|
||||
|
||||
await t.test('blocks access to package.json', async () => {
|
||||
const res = await get('/package.json');
|
||||
assert.strictEqual(res.statusCode, 403);
|
||||
});
|
||||
|
||||
await t.test('blocks access to src/dashboard.mjs', async () => {
|
||||
const res = await get('/src/dashboard.mjs');
|
||||
assert.strictEqual(res.statusCode, 403);
|
||||
});
|
||||
|
||||
await t.test('blocks access to random root files', async () => {
|
||||
const res = await get('/README.md');
|
||||
assert.strictEqual(res.statusCode, 403);
|
||||
});
|
||||
|
||||
await t.test('allows access to torch-config.json via special handler', async () => {
|
||||
const res = await get('/torch-config.json');
|
||||
// It exists in root, so it should return 200
|
||||
assert.strictEqual(res.statusCode, 200);
|
||||
});
|
||||
|
||||
await t.test('blocks directory traversal attempts', async () => {
|
||||
const res = await get('/dashboard/../../package.json');
|
||||
// path.normalize collapses this to /package.json, which is then blocked
|
||||
assert.strictEqual(res.statusCode, 403);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user