mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-08 22:02:12 +00:00
feat(security): add authentication and host binding to dashboard
This commit addresses the missing authentication vulnerability in the dashboard server. Changes: - Added Basic Authentication support via TORCH_DASHBOARD_AUTH env var or dashboard.auth config field. - Defaulted dashboard host binding to 127.0.0.1 for better security. - Added --host CLI argument to allow overriding the bind address. - Used crypto.timingSafeEqual for secure credential comparison. - Fixed a potential crash when parsing malformed Authorization headers. - Added comprehensive unit tests for dashboard authentication. - Updated documentation in README.md and torch-config.example.json. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
5196f264cb
commit
456bc2df18
7 files changed
+174
-8
No files matched your search
+52
-4
@@ -2,15 +2,57 @@ import fs from 'node:fs';
|
||||
import fsp from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import http from 'node:http';
|
||||
import crypto from 'node:crypto';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { DEFAULT_DASHBOARD_PORT } from './constants.mjs';
|
||||
import { getDashboardAuth } from './torch-config.mjs';
|
||||
|
||||
export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT) {
|
||||
function timingSafeCompare(a, b) {
|
||||
const bufA = Buffer.from(a);
|
||||
const bufB = Buffer.from(b);
|
||||
if (bufA.length !== bufB.length) {
|
||||
// Still do a comparison to avoid some timing leaks, though length leak is hard to avoid entirely
|
||||
crypto.timingSafeEqual(bufA, bufA);
|
||||
return false;
|
||||
}
|
||||
return crypto.timingSafeEqual(bufA, bufB);
|
||||
}
|
||||
|
||||
export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT, host = '127.0.0.1') {
|
||||
// Resolve package root relative to this file (src/dashboard.mjs)
|
||||
// this file is in <root>/src/dashboard.mjs, so '..' goes to <root>
|
||||
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const auth = getDashboardAuth();
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
// Basic Auth check
|
||||
if (auth) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) {
|
||||
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
|
||||
res.end('Authentication required');
|
||||
return;
|
||||
}
|
||||
const parts = authHeader.split(' ');
|
||||
const type = parts[0];
|
||||
const credentials = parts[1];
|
||||
let isValid = false;
|
||||
if (type === 'Basic' && credentials) {
|
||||
try {
|
||||
const decoded = Buffer.from(credentials, 'base64').toString();
|
||||
isValid = timingSafeCompare(decoded, auth);
|
||||
} catch {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
|
||||
res.end('Invalid credentials');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// URL parsing
|
||||
const url = new URL(req.url, `http://${req.headers.host}`);
|
||||
let pathname = url.pathname;
|
||||
@@ -82,12 +124,18 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT) {
|
||||
fs.createReadStream(filePath).pipe(res);
|
||||
});
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(`Dashboard running at http://localhost:${port}/dashboard/`);
|
||||
server.listen(port, host, () => {
|
||||
const listenUrl = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}/dashboard/`;
|
||||
console.log(`Dashboard running at ${listenUrl}`);
|
||||
if (auth) {
|
||||
console.log('Authentication: enabled (Basic Auth)');
|
||||
} else {
|
||||
console.warn('Authentication: DISABLED (Dashboard is public)');
|
||||
}
|
||||
console.log(`Serving files from ${packageRoot}`);
|
||||
console.log(`Using configuration from ${process.cwd()}`);
|
||||
});
|
||||
|
||||
// Keep process alive
|
||||
return new Promise(() => {});
|
||||
return server;
|
||||
}
|
||||
Reference in new issue
Block a user