Commit Graph

8 Commits

Author SHA1 Message Date
Hartmut aef4c61dcc test(e2e): add nav-smoke spec covering all 35 sidebar routes (#50)
- e2e/dev-system/nav-smoke.spec.ts: new Playwright spec in the dev-system
  suite; iterates every href from navSections + adminNavEntries, asserts
  HTTP status ≠ 404 and no "page not found" text for an authenticated admin
- e2e/navigation.spec.ts: add "all nav routes resolve" smoke block covering
  16 routes not previously tested in the isolated test suite

All 35 routes pass against live dev server. Catches dead nav links before
users encounter them.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-03 12:11:37 +02:00
Hartmut 7c0110df91 fix(e2e): make email E2E tests green end-to-end
- global-setup.ts: create reset-test@planarchy.dev directly via DB
  (argon2id hash computed in Node.js, inserted via docker exec psql stdin
   with correct camelCase quoted column names + createdAt/updatedAt;
   ON_ERROR_STOP=1 so failures propagate rather than being swallowed)
- helpers.ts: resetPasswordViaApi now updates passwordHash directly in DB
  (bypasses tRPC batch mutation format issues entirely);
  getLatestEmailTo decodes MIME parts per Content-Transfer-Encoding
  (quoted-printable soft line breaks were truncating 64-char tokens to ~14 chars)
- invite-flow.spec.ts: use fresh unauthenticated browser context for
  the invite accept page (admin context was inheriting cookies)
- docker-compose.yml: hardcode SMTP_HOST=mailhog for Docker app service
  (host .env value localhost doesn't reach Mailhog inside Docker network)

All 3 email E2E tests pass: invite flow, password reset flow, invalid token.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-02 13:53:47 +02:00
Hartmut fceceeee4b feat: SMTP full ENV override, password reset flow, and E2E email testing
- SMTP: SMTP_HOST/PORT/USER/FROM/TLS now all have ENV override support
  (previously only SMTP_PASSWORD was env-aware). ENV takes priority over DB.
- docker-compose.yml: forward all SMTP_* env vars to app container + add
  Mailhog service (ports 1025 SMTP / 8025 HTTP, always available in dev)
- Password reset: PasswordResetToken Prisma model + authRouter with
  requestPasswordReset (timing-safe, no email enumeration) + resetPassword
- UI: /auth/forgot-password, /auth/reset-password/[token] pages +
  "Forgot password?" link on sign-in page
- E2E: Mailhog helpers (getLatestEmailTo, clearMailhog, extractUrlFromEmail)
  + invite-flow.spec.ts + password-reset.spec.ts

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-02 08:55:39 +02:00
Hartmut 745be7ee8b fix(dashboard): scope localStorage key by userId to prevent cross-user layout bleed (#27)
New users on a shared device were picking up a previous user's stale
(potentially empty) dashboard layout from localStorage because the key
"capakraken_dashboard_v1" was not user-scoped.

- useDashboardLayout: key is now capakraken_dashboard_v1_{userId};
  userId is resolved via trpc.user.me before touching localStorage
- Initial state falls back to createDefaultDashboardLayout() until
  userId resolves, then hydrates from the user-scoped key
- DB layout still wins over localStorage when it has data (unchanged)
- E2E test suite covers: new-user flow, modal widget list, add widget
  persists after reload, cross-user localStorage isolation
- plan.md: added ticket #27 implementation plan

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 22:44:41 +02:00
Hartmut d3bfa8ca98 test(mfa): full MFA test coverage — unit + E2E
Unit tests (packages/api — 13 tests):
- generateTotpSecret: DB write, returns secret + uri
- verifyAndEnableTotp: valid token enables; invalid/already-enabled/no-secret guards
- verifyTotp (login): valid → ok; invalid → UNAUTHORIZED; not-enabled → BAD_REQUEST
- getCurrentMfaStatus: reads totpEnabled flag

E2E tests (apps/web/e2e/dev-system/mfa.spec.ts — 7 scenarios):
- Setup flow: generate secret, enable with valid code, reject invalid code, UI QR check
- Login flow: MFA prompt appears, valid code logs in, wrong code shows error + stays on prompt
- Login without MFA: no TOTP prompt for users without MFA enabled

Also: start.sh health-check timeout 30s → 90s (container startup can exceed 30s)

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 22:30:36 +02:00
Hartmut bfdf0a82da security/platform: close audit findings #19–#26
Tests, CSP nonce middleware, SSRF guard, perf-route hardening,
Docker env isolation, migration runbook, RBAC E2E coverage.

Tickets resolved:
- #19: MfaSetup.test.ts — static source tests confirming local QR rendering
- #20: ssrf-guard.test.ts (16 tests) + webhook-procedure-support mock fix
- #21: /api/perf route.test.ts (5 tests) — header-only auth, fail-closed
- #22: middleware.ts (nonce-based CSP) + middleware.test.ts (6 tests);
       layout.tsx async + nonce prop; CSP removed from next.config.ts
- #23: Active-session registry enforcement verified (already in codebase)
- #24: docker-compose.yml REDIS_URL hardcoded (no host-env substitution)
- #25: docker-compose.yml REDIS_URL + docs/developer-runbook.md created
- #26: e2e/dev-system/rbac-data-access.spec.ts (12 tests, 3 roles × 4 procedures)

Quality gates: tsc clean, api 1447/1447, web 189/189 passing.
Turbo concurrency capped at 2 (package.json) to prevent OOM under
parallel test runs.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 22:14:20 +02:00
Hartmut 8429bd86d4 test(e2e): fix dev-system test suite — storageState + strict-mode + signout
Fixes 8 failures from the first test run:

1. Rate limiter exhaustion (5/8 failures)
   Admin was logged in 9× across the suite, hitting the 5/15min auth
   limit. Fix: global-setup.ts logs in once per role and saves storage
   state; all non-login tests use storageState so they skip the form.
   Total admin logins per suite run: 3 (global setup + 2 explicit tests).

2. Strict-mode violations (2/8 failures)
   toBeVisible() matched 3 email cells / 2 permission-error nodes.
   Fix: .first() on both locators.

3. Auth.js v5 signout confirmation page (1/8 failures)
   GET /auth/signout renders a confirm form rather than immediately
   redirecting. Fix: signOut() helper clicks the submit button.

Note: running the suite right after a previous run may fail if the
in-memory rate limit hasn't reset (15-min window). Restart the dev
server, or add E2E_TEST_MODE=true to apps/web/.env.local to bypass.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 19:09:49 +02:00
Hartmut 3d8a256d52 fix(auth): use token.sid to avoid Auth.js jti claim conflict
Auth.js v5 manages token.jti internally and overwrites it after the jwt
callback. Storing our session UUID in token.sid ensures the value we
persist in active_sessions matches what the signed cookie carries.

- jwt callback: token.sid = jti (was token.jti)
- session callback: read from token.sid
- signOut event: falls back to token.jti for backward compat with any
  sessions created before this change

Also adds Playwright dev-system test suite (playwright.dev.config.ts +
e2e/dev-system/) that validates login, session registry health, and
RBAC enforcement against the running localhost:3100 dev server.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 19:00:44 +02:00