Hartmut
435c871e1f
security: implement tickets #28-#35 + architecture decision #30
...
#28 - TOTP rate limiting (verifyTotp): added totpRateLimiter (10 req/30s),
throws TOO_MANY_REQUESTS before DB hit; 16 unit tests including rate-limit
exceeded + userId key isolation.
#29 - /api/reports/allocations role check: only ADMIN/MANAGER/CONTROLLER may
access; returns 403 otherwise; 9 unit tests (401 unauthenticated, 403 for
USER/VIEWER, 200 for allowed roles + xlsx format).
#31 - pgAdmin credentials moved out of docker-compose.yml into env vars;
PGADMIN_PASSWORD is now required (:?) to prevent accidental plaintext
exposure in committed files.
#34 - Server-side HTML sanitization for comment bodies via stripHtml():
strips all tags + decodes safe entities before persistence; 16 unit tests
covering passthrough, injection patterns, entity decoding.
#35 - MFA setup prompt banner (MfaPromptBanner): shown to ADMIN/MANAGER users
without TOTP enabled; user-scoped localStorage snooze (7 days); links to
/account/security; accessibility role=alert; 7 structural unit tests.
#33 - Auth anomaly alerting cron (/api/cron/auth-anomaly-check): detects
HIGH_GLOBAL_FAILURE_RATE and CONCENTRATED_FAILURES in 30-minute window;
CRITICAL notification to ADMINs; fail-closed via verifyCronSecret;
10 unit tests.
#32 - MFA enforcement policy: added requireMfaForRoles field to SystemSettings
schema + Prisma migration; auth.ts blocks login with MFA_REQUIRED_SETUP
signal if role is enforced but TOTP not set up; signin page redirects to
/account/security?mfa_required=1; settings schema + view model updated;
11 unit tests.
#30 - API keys architecture decision documented in LEARNINGS.md; no code
written — product decision required before implementation.
Co-Authored-By: claude-flow <ruv@ruv.net >
2026-04-01 23:25:06 +02:00
..
2026-04-01 07:42:03 +02:00
2026-03-31 22:21:30 +02:00
2026-03-31 22:30:03 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 10:46:25 +02:00
2026-03-31 11:17:27 +02:00
2026-03-31 10:46:25 +02:00
2026-03-31 11:02:35 +02:00
2026-03-31 10:46:25 +02:00
2026-03-31 10:46:25 +02:00
2026-03-31 11:17:27 +02:00
2026-03-31 10:30:28 +02:00
2026-03-31 13:15:44 +02:00
2026-03-31 12:16:20 +02:00
2026-03-31 10:30:28 +02:00
2026-03-28 22:49:28 +01:00
2026-03-31 22:44:54 +02:00
2026-03-31 22:44:54 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 10:30:28 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 22:44:54 +02:00
2026-03-31 13:41:08 +02:00
2026-03-31 21:11:19 +02:00
2026-03-31 12:09:24 +02:00
2026-03-31 21:11:19 +02:00
2026-03-31 20:15:19 +02:00
2026-03-31 13:47:35 +02:00
2026-03-27 13:18:09 +01:00
2026-03-31 20:15:19 +02:00
2026-03-31 21:15:02 +02:00
2026-03-31 14:00:22 +02:00
2026-03-31 21:15:02 +02:00
2026-03-31 22:43:33 +02:00
2026-03-31 20:42:33 +02:00
2026-03-31 20:15:25 +02:00
2026-03-31 13:45:53 +02:00
2026-03-31 20:15:25 +02:00
2026-04-01 23:25:06 +02:00
2026-03-31 14:28:07 +02:00
2026-03-31 19:16:57 +02:00
2026-03-31 22:19:09 +02:00
2026-03-31 22:19:09 +02:00
2026-03-31 21:24:28 +02:00
2026-03-31 22:12:02 +02:00
2026-03-31 11:28:18 +02:00
2026-03-31 22:12:02 +02:00
2026-03-31 22:14:53 +02:00
2026-03-31 22:01:29 +02:00
2026-03-31 10:41:24 +02:00
2026-03-31 10:41:24 +02:00
2026-03-31 22:16:31 +02:00
2026-03-31 22:16:31 +02:00
2026-03-31 10:12:05 +02:00
2026-03-31 21:24:28 +02:00
2026-03-31 20:25:11 +02:00
2026-03-31 13:54:22 +02:00
2026-03-31 20:25:11 +02:00
2026-03-27 13:18:09 +01:00
2026-03-31 22:26:52 +02:00
2026-03-31 23:46:07 +02:00
2026-03-31 20:54:54 +02:00
2026-03-31 14:31:59 +02:00
2026-03-31 14:31:59 +02:00
2026-03-31 20:32:59 +02:00
2026-03-31 12:13:28 +02:00
2026-03-31 20:32:59 +02:00
2026-03-31 19:59:36 +02:00
2026-03-31 14:05:20 +02:00
2026-03-31 19:59:36 +02:00
2026-03-31 22:42:00 +02:00
2026-03-31 21:05:56 +02:00
2026-03-31 09:21:43 +02:00
2026-03-31 11:32:00 +02:00
2026-03-31 11:30:38 +02:00
2026-03-31 22:45:05 +02:00
2026-03-31 09:16:46 +02:00
2026-03-31 22:45:05 +02:00
2026-03-31 22:45:05 +02:00
2026-03-31 20:02:15 +02:00
2026-03-31 14:07:21 +02:00
2026-03-31 20:02:15 +02:00
2026-03-31 14:24:46 +02:00
2026-03-31 20:25:17 +02:00
2026-04-01 07:35:34 +02:00
2026-03-31 09:05:18 +02:00
2026-03-31 14:24:46 +02:00
2026-03-31 13:25:27 +02:00
2026-03-31 20:25:17 +02:00
2026-03-31 22:55:26 +02:00
2026-03-31 20:36:46 +02:00
2026-03-28 22:49:28 +01:00
2026-03-31 11:33:36 +02:00
2026-03-31 22:53:53 +02:00
2026-03-31 20:31:55 +02:00
2026-03-31 20:04:17 +02:00
2026-03-31 13:52:24 +02:00
2026-03-31 20:04:17 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 21:56:15 +02:00
2026-03-31 21:56:15 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 20:50:14 +02:00
2026-03-31 20:28:13 +02:00
2026-03-31 13:55:47 +02:00
2026-03-31 20:28:13 +02:00
2026-03-31 13:25:20 +02:00
2026-03-31 08:58:22 +02:00
2026-03-31 08:57:21 +02:00
2026-03-31 08:54:52 +02:00
2026-03-31 09:03:00 +02:00
2026-03-31 13:25:20 +02:00
2026-03-27 13:18:09 +01:00
2026-03-31 21:28:56 +02:00
2026-03-31 08:54:52 +02:00
2026-03-31 13:25:20 +02:00
2026-03-31 21:28:56 +02:00
2026-03-31 14:33:38 +02:00
2026-03-31 19:56:14 +02:00
2026-03-31 14:33:38 +02:00
2026-03-31 14:30:29 +02:00
2026-03-31 19:56:14 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 10:09:02 +02:00
2026-03-31 21:59:10 +02:00
2026-04-01 07:42:03 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 08:34:38 +02:00
2026-03-31 08:05:45 +02:00
2026-03-31 08:05:45 +02:00
2026-03-31 08:05:45 +02:00
2026-04-01 07:38:03 +02:00
2026-03-31 08:05:45 +02:00
2026-03-31 08:05:45 +02:00
2026-03-31 08:36:29 +02:00
2026-04-01 07:35:34 +02:00
2026-04-01 07:38:03 +02:00
2026-04-01 07:38:03 +02:00
2026-03-31 07:57:39 +02:00
2026-03-31 08:34:38 +02:00
2026-04-01 07:38:03 +02:00
2026-03-31 20:59:26 +02:00
2026-03-31 08:34:38 +02:00
2026-03-31 21:22:44 +02:00
2026-03-31 13:40:55 +02:00
2026-03-31 21:22:44 +02:00
2026-03-31 11:03:50 +02:00
2026-03-31 11:03:50 +02:00
2026-03-31 19:18:56 +02:00
2026-03-31 11:03:50 +02:00
2026-03-31 11:03:50 +02:00
2026-03-31 19:18:56 +02:00
2026-03-31 19:46:50 +02:00
2026-04-01 23:25:06 +02:00
2026-03-31 19:46:50 +02:00
2026-03-31 22:45:00 +02:00
2026-03-31 08:48:01 +02:00
2026-03-31 22:45:00 +02:00
2026-03-31 08:45:20 +02:00
2026-03-31 22:45:00 +02:00
2026-03-31 11:26:45 +02:00
2026-03-31 22:54:33 +02:00
2026-03-31 14:00:26 +02:00
2026-03-31 21:09:21 +02:00
2026-03-31 18:08:19 +02:00
2026-03-31 17:57:28 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 17:59:16 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 18:00:23 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 18:08:19 +02:00
2026-03-31 17:55:52 +02:00
2026-04-01 07:42:03 +02:00
2026-03-31 17:57:28 +02:00
2026-03-31 17:59:16 +02:00
2026-03-31 17:53:59 +02:00
2026-03-31 17:53:59 +02:00
2026-03-31 18:18:52 +02:00
2026-03-31 15:46:17 +02:00
2026-03-31 18:56:01 +02:00
2026-03-31 17:30:04 +02:00
2026-03-31 15:54:56 +02:00
2026-03-31 17:40:44 +02:00
2026-03-31 18:53:20 +02:00
2026-03-31 18:56:01 +02:00
2026-03-31 15:18:43 +02:00
2026-03-31 18:15:38 +02:00
2026-03-31 15:52:31 +02:00
2026-03-31 18:48:48 +02:00
2026-03-31 17:30:04 +02:00
2026-03-31 17:26:42 +02:00
2026-03-31 17:32:43 +02:00
2026-03-31 18:49:51 +02:00
2026-03-31 16:01:35 +02:00
2026-03-31 16:01:35 +02:00
2026-03-31 18:56:01 +02:00
2026-03-31 18:56:01 +02:00
2026-03-31 18:56:01 +02:00
2026-03-31 07:45:15 +02:00
2026-03-31 15:50:17 +02:00
2026-03-27 13:18:09 +01:00
2026-03-31 17:50:36 +02:00
2026-03-31 18:15:38 +02:00
2026-03-31 17:50:36 +02:00
2026-03-31 18:15:38 +02:00
2026-03-31 21:40:50 +02:00
2026-04-01 23:25:06 +02:00
2026-03-31 21:40:50 +02:00
2026-03-31 22:54:33 +02:00
2026-03-31 13:49:10 +02:00
2026-03-31 21:09:13 +02:00
2026-03-31 11:08:58 +02:00
2026-03-31 19:00:47 +02:00
2026-03-31 14:27:54 +02:00
2026-03-31 14:27:54 +02:00
2026-03-31 11:08:58 +02:00
2026-03-31 22:42:00 +02:00
2026-04-01 07:35:34 +02:00
2026-03-31 11:02:40 +02:00
2026-03-31 19:00:47 +02:00
2026-04-01 18:19:21 +02:00
2026-03-31 13:41:02 +02:00
2026-03-31 21:18:29 +02:00