DC-026/027/028: close 3 more auth security holes + rate limit /auth/* + audit credential exposures
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled

[DC-026] routes/auth/sso-gate.js — fix sessionDuration='never' bypass
  Both /auth/gate/:serviceId and /auth/app-token/:serviceId had a session
  check gated on `sessionDuration !== 'never'`. An admin setting TOTP to
  never-expire accidentally created an authentication-free path to credential
  injection (Basic Auth, X-Api-Key, Plex/Prowlarr tokens). Patched: session
  required whenever TOTP is enabled, period. Added 8 regression tests.

[DC-027] src/utilities/middleware.js — rate limit /auth/*
  New authLimiter (20 req / 15 min) on /auth/keys, /auth/jwt, /auth/gate,
  /auth/app-token. These endpoints expose credentials and were unmetered.
  Without this, an attacker with a guessed session cookie could burn through
  every credential-touching endpoint. Added 5 tests.

[DC-028] src/security/audit-logger.js — log credential exposures
  /auth/gate and /auth/app-token were in SKIP_PATHS, silently dropping
  every credential-exposure event from the audit log. Combined with the
  GET-skip rule, NONE of these events were being recorded. Now logged
  with named actions: auth.credential-injection, auth.app-token-issue,
  auth.api-key-generate, auth.api-key-revoke, auth.jwt-mint. Added 9 tests.

[start.sh] Disable in-container self-updater
  DASHCADDY_UPDATE_ENABLED=false. Without this, the container kept writing
  trigger.json every 30 min and clobbered my in-progress host edits. The
  path unit on the host is still active for manual triggers, but the
  container won't auto-update itself — only when an admin clicks the
  update button or a new release is manually published.

[package.json] Bump to 1.14.7

Test results: 1066/1066 passing across 39 suites (added 22 new tests).
This commit is contained in:
Krystie
2026-07-01 04:20:57 -07:00
parent bfa4ba570e
commit fef7e07b49
8 changed files with 412 additions and 6 deletions
+19
View File
@@ -479,6 +479,25 @@ module.exports = function configureMiddleware(app, {
// RateLimit-Limit / RateLimit-Remaining for clients to see.
app.use('/api/v1/totp/setup', totpLimiter);
// SECURITY [DC-027]: Dedicated rate limiter for credential-touching auth
// endpoints. /auth/keys (manage API keys), /auth/jwt (mint admin JWT),
// /auth/gate/* (Caddy forward_auth — returns Basic Auth + X-Api-Key),
// /auth/app-token/* (returns upstream service tokens like Plex/Prowlarr).
// Without this, an attacker with a guessed-or-leaked session cookie could
// burn through every endpoint. 20 requests per 15 min is plenty for legit
// use (1/min average) but cuts off brute-force + scraping cold.
const authLimiter = rateLimit({
...RATE_LIMITS.STRICT,
standardHeaders: true,
legacyHeaders: false,
skip: () => isTest,
message: { success: false, error: 'Too many auth requests, please try again later' }
});
app.use('/api/v1/auth/keys', authLimiter);
app.use('/api/v1/auth/jwt', authLimiter);
app.use('/api/v1/auth/gate', authLimiter);
app.use('/api/v1/auth/app-token', authLimiter);
// ── Audit logging middleware (logs non-GET API requests) ──
app.use(auditLogger.middleware());