DC-026/027/028: close 3 more auth security holes + rate limit /auth/* + audit credential exposures
[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:
@@ -27,8 +27,12 @@ module.exports = function(deps) {
|
||||
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
||||
const serviceId = req.params.serviceId;
|
||||
|
||||
// Check TOTP session first
|
||||
if (totpConfig.enabled && totpConfig.sessionDuration !== 'never') {
|
||||
// SECURITY [DC-026]: Session is required whenever TOTP is enabled, regardless
|
||||
// of sessionDuration. Previously the check was gated on `!== 'never'`, which
|
||||
// meant an admin setting TOTP to never-expire accidentally created an
|
||||
// authentication-free path to credential injection. Even with a non-expiring
|
||||
// session, the request itself must still present a valid session cookie.
|
||||
if (totpConfig.enabled) {
|
||||
const valid = session.isValid(req);
|
||||
if (!valid) return errorResponse(res, 401, 'Session expired or invalid', { authenticated: false });
|
||||
}
|
||||
@@ -100,7 +104,9 @@ module.exports = function(deps) {
|
||||
router.get('/auth/app-token/:serviceId', ctx.licenseManager.requirePremium('sso'), asyncHandler(async (req, res) => {
|
||||
const { serviceId } = req.params;
|
||||
|
||||
if (totpConfig.enabled && totpConfig.sessionDuration !== 'never') {
|
||||
// SECURITY [DC-026]: Same gate fix as /auth/gate — drop the sessionDuration
|
||||
// exception. TOTP-enabled means session is required, period.
|
||||
if (totpConfig.enabled) {
|
||||
if (!session.isValid(req)) throw new AuthenticationError('Not authenticated');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user