Fix 5 critical security vulnerabilities

1. WebSocket exec auth bypass (exec.js): Require valid JWT or API key
   before accepting WebSocket upgrade. Reject unauthenticated requests
   with 401 before the upgrade completes.

2. Shell injection in router auto-login (session-handlers.js): Validate
   baseUrl against safe hostname pattern before embedding in wget shell
   command. Reject with null session if invalid.

3. Path traversal in credentials routes (services.js): Add explicit
   serviceId validation (alphanumeric + dash/underscore/dot, max 100
   chars) to all three credential endpoints. Removed redundant
   try/catch wrapper.

4. execSync injection in CA CSR generation (ca.js): Add sanitize step
   replacing any non-alphanumeric domain chars with underscore before
   interpolation into shell subj argument. Redundant with existing
   validation but provides defense-in-depth.

5. Auth bypass when TOTP disabled (middleware.js): Split the logic
   cleanly — disabled TOTP means no auth (initial setup state), enabled
   TOTP means all auth methods checked (session/JWT/API key). Removed
   the sessionDuration:never conflating shortcut.
This commit is contained in:
Hermes
2026-05-27 18:05:35 -07:00
parent 445da9f5fc
commit 17edb3bc90
6 changed files with 92 additions and 28 deletions
+6 -4
View File
@@ -180,7 +180,9 @@ module.exports = function(ctx) {
if (needsRegeneration) {
execSync(`openssl genrsa -out "${keyFile}" 2048`, { stdio: 'pipe' });
const subject = `/CN=${domain}`;
// Sanitize domain for safe use in shell arguments — defensive, since validation already restricts input
const safeDomain = domain.replace(/[^a-zA-Z0-9.-]/g, '_');
const subject = `/CN=${safeDomain}`;
execSync(`openssl req -new -key "${keyFile}" -out "${csrFile}" -subj "${subject}"`, { stdio: 'pipe' });
const configContent = `[req]
@@ -189,7 +191,7 @@ req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = ${domain}
CN = ${safeDomain}
[v3_req]
keyUsage = keyEncipherment, dataEncipherment, digitalSignature
@@ -197,8 +199,8 @@ extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${domain}
${domain.includes('.') ? `DNS.2 = *.${domain}` : ''}`;
DNS.1 = ${safeDomain}
${safeDomain.includes('.') ? `DNS.2 = *.${safeDomain}` : ''}`;
const configFile = path.join(domainDir, 'openssl.cnf');
await fsp.writeFile(configFile, configContent);