Two regressions surfaced after merging security/audit-2026-04-17:
1. **Build job** failed with `assertSecureRuntimeEnv` rejecting the CI
`NEXTAUTH_SECRET=ci-test-secret-minimum-32-chars-xx`. The CI placeholder
strings were added to `DISALLOWED_PRODUCTION_SECRETS` defensively, but
that list is only consulted when `NODE_ENV=production` — exactly the
mode `next build` runs in. The length + Shannon-entropy gates already
reject genuinely weak prod secrets (the CI value scores ~3.68 vs the
3.5 threshold), so removing the CI strings from the blocklist restores
the build without weakening prod protection.
2. **Unit-tests job** failed with `(0 , brace_expansion_1.default) is not
a function` from `minimatch@9` → `brace-expansion@5.0.5` (ESM-only)
loaded via CJS `require`. The blanket override `"brace-expansion":
"^5.0.5"` (added for CVE-2025-5889) was too broad. Switching to the
targeted `"brace-expansion@<2.0.2": ">=2.0.2"` patches the CVE while
leaving CJS consumers (test-exclude/glob/minimatch) on v2.
Drops the now-stale CI-placeholder unit test in `runtime-env.test.ts`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
#36 CRITICAL: add .max(128) to all password Zod schemas to prevent
Argon2-based DoS from unbounded password strings.
#46 HIGH: configure pino redact paths so passwords/tokens/cookies/TOTP
secrets are never serialized in logs.
#58 MEDIUM: upgrade dompurify to ^3.4.0 and add pnpm overrides for
brace-expansion (>=5.0.5) and esbuild (>=0.25.0) to patch known CVEs.
Vite moderate (path traversal, dev-only) remains — requires vitest 3.x
major upgrade, deferred.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `pnpm check:unused` script powered by knip. Initial run finds
17 unused files, 3 unused deps, 96 unused exports, and 117 unused
exported types — all candidates for cleanup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Install husky v9 + lint-staged: pre-commit runs eslint --fix and prettier on staged files
- Tighten ESLint base config: no-console→error, ban-ts-comment (ts-ignore banned, ts-expect-error with description allowed), reportUnusedDisableDirectives→error
- Migrate web app from deprecated `next lint` to `eslint src/` with flat config and react-hooks plugin
- Convert all 5 @ts-ignore to @ts-expect-error with descriptions, remove stale disable comments
- Add NEXT_PUBLIC_SENTRY_DSN to docker-compose.prod.yml and .env.example
- Add coverage artifact upload step to CI test job
- Pre-existing violations (102 warnings) downgraded to warn in web config for Phase 2 cleanup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Password validation: min(8) → min(12) across auth.ts, user-procedure-support.ts,
and invite.ts (aligns with NIST SP 800-63B modern recommendations)
- Error boundary: stop rendering raw error.message which could leak internal
details; always show the generic fallback text
- Add `pnpm audit` script (--audit-level=high) for dependency vulnerability scanning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Invite flow: admin can invite users by email with role selection; accept-invite page
sets password and creates the account; 72-hour token expiry; E2E tests
- User deactivate/reactivate/delete: new tRPC procedures + UI buttons; deactivation
revokes all active sessions immediately; delete cascades vacation/broadcast records;
isActive field added via migration 20260402000000_user_isactive
- Auth: block login for inactive users with audit entry
- Favicon: SVG favicon + ICO/PNG fallbacks (16, 32, 180, 192, 512px); manifest updated
- Dashboard: GridLayout dynamic-import loading skeleton prevents blank dark area
on first login before react-grid-layout chunk is cached
- Admin users: remove max-w-5xl constraint so table uses full page width
- Dev: docker container restart workflow documented in LEARNINGS.md; Prisma generate
must run inside the container after schema changes (named node_modules volume)
Co-Authored-By: claude-flow <ruv@ruv.net>