🔒 fix: require ACCESS_ADMIN on audit-log routes

Every other admin router (config, grants, users, roles, groups, auth)
enforces requireJwtAuth followed by requireCapability(ACCESS_ADMIN) before
any feature-specific capability check. The audit-log router only required
READ_AUDIT_LOG, which is independent of ACCESS_ADMIN in CapabilityImplications,
so a role delegated only READ_AUDIT_LOG without ACCESS_ADMIN could read or
CSV-export the audit trail and bypass the admin boundary.

Aligned the middleware chain with the rest of the admin surface so
ACCESS_ADMIN gates entry and READ_AUDIT_LOG gates the feature within it.
This commit is contained in:
Dustin Healy 2026-06-01 22:01:46 -07:00 • committed by Danny Avila
parent 5506281a1d
commit 956245b064

View file

@ -7,6 +7,7 @@ const db = require('~/models');
const router = express.Router();
const requireAdminAccess = requireCapability(SystemCapabilities.ACCESS_ADMIN);
const requireAuditLogRead = requireCapability(SystemCapabilities.READ_AUDIT_LOG);
const handlers = createAdminAuditLogHandlers({
@ -15,7 +16,14 @@ const handlers = createAdminAuditLogHandlers({
streamAuditLogEntries: db.streamAuditLogEntries,
});
router.use(requireJwtAuth, requireAuditLogRead);
/**
* `ACCESS_ADMIN` gates entry to the admin surface; `READ_AUDIT_LOG` then gates
* this specific feature within that surface. The two capabilities are
* independent in `CapabilityImplications`, so a role delegated only
* `READ_AUDIT_LOG` without `ACCESS_ADMIN` would otherwise bypass the admin
* boundary on this router — every other admin router enforces the same pair.
*/
router.use(requireJwtAuth, requireAdminAccess, requireAuditLogRead);
router.get('/', handlers.listAuditLog);
/** `/export.csv` MUST precede `/:id` so it isn't matched as `{ id: 'export.csv' }`. */