Commit Graph

10 Commits

Author SHA1 Message Date
Hartmut 40ca0c3046 security: bound Zod inputs, add SSE per-user cap and tRPC body limit (#51)
CI / Architecture Guardrails (pull_request) Successful in 2m6s
CI / Lint (pull_request) Successful in 7m29s
CI / Typecheck (pull_request) Successful in 8m3s
CI / Unit Tests (pull_request) Successful in 8m11s
CI / Build (pull_request) Successful in 5m24s
CI / E2E Tests (pull_request) Successful in 5m25s
CI / Fresh-Linux Docker Deploy (pull_request) Successful in 6m30s
CI / Release Images (pull_request) Has been skipped
CI / Assistant Split Regression (pull_request) Successful in 3m47s
Mechanical .max() bounds across 9 router schemas per the convention in
#51: IDs at 64, names at 200, search/filter strings at 500, arrays at
100-5000 depending on domain. Webhook secret bounded at min(16)/max(256).

Reports route now validates startDate/endDate via zod with year bounds
and rejects end<start. SSE timeline route enforces a per-user connection
cap of 8 (returns 429 with Retry-After). tRPC route rejects bodies over
2 MiB via Content-Length check before auth/DB work.

Covers 12 call-sites listed in #51. ESLint rule and zod conventions doc
remain as follow-up.
2026-04-18 13:31:18 +02:00
Hartmut d45cc00f2f security: cookie + session hardening (#41)
Three related fixes:
- Cookie secure flag now tracks AUTH_URL scheme (https → Secure),
  not NODE_ENV — staging over HTTPS with NODE_ENV!=production used
  to ship Set-Cookie without Secure. Cookie name gains __Host-
  prefix when Secure is on.
- jwt() callback no longer swallows session-registry write failures;
  concurrent-session cap is now fail-closed.
- Session callback no longer copies token.sid onto session.user.jti.
  The tRPC route handler reads the JTI directly from the encrypted
  JWT via getToken() so it stays server-side.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:00:54 +02:00
Hartmut 3c5d1d37f7 security: rate-limit IP-keyed, fail-closed on empty key (#37)
Rate-limiter now accepts string | string[] so callers can key on
multiple buckets simultaneously. If any bucket is exhausted the
request is denied, which lets login/TOTP/reset-password throttle on
BOTH user identifier and source IP without either becoming a bypass.

Fail-closed: empty/whitespace-only keys now deny by default instead
of silently allowing unbounded attempts (was CWE-307 gap).

Degraded-fallback divisor reduced from /10 to /2 — the old aggressive
clamp forced-logged-out legitimate users during brief Redis outages;
/2 still meaningfully slows distributed brute-force.

Callers updated:
- auth.ts (login): both email: and ip: buckets
- auth router requestPasswordReset: email + IP
- auth router resetPassword: IP before lookup, email-reset after
- invite router getInvite/acceptInvite: IP
- user-self-service verifyTotp: userId + IP

TRPCContext now carries clientIp; web tRPC route extracts it from
X-Forwarded-For / X-Real-IP.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 08:19:33 +02:00
Hartmut 4901bc878b fix(e2e): complete E2E_TEST_MODE isolation for session registry + rate limits
Three related fixes to prevent E2E test runs from disrupting real user sessions:

1. auth.ts: skip active_sessions registration in E2E mode
   E2E logins now return early after setting token.sid without writing
   to active_sessions. Prevents test sessions from kicking real user
   sessions via the concurrent-session limit.

2. trpc/route.ts: skip active_sessions validation in E2E mode
   Pairs with (1): if registration is skipped, validation must be too,
   otherwise every storageState-based test gets a 401 "Session revoked".

3. docker-compose.yml: hardcode Docker-internal DATABASE_URL + E2E_TEST_MODE
   Previously ${DATABASE_URL:-postgres:5432} picked up the host's
   localhost:5433 override and passed it into the container, where
   localhost refers to the container itself — breaking db:migrate:deploy
   on container recreate. Now hardcoded to postgres:5432.
   Also adds E2E_TEST_MODE=true to the dev container environment.

Result: 21/21 dev-system E2E tests pass, test runs leave zero footprint
in active_sessions and rate limiter counters for real user accounts.
The timeline disruption caused by test sessions kicking the admin's
real browser session is also resolved.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 20:57:14 +02:00
Hartmut 5bc7cace26 fix(auth): make active-session check fail-open; add missing DB migration
The active_sessions table was never migrated to production — the model
was added to the Prisma schema via db push only. prisma migrate deploy
was a no-op because no migration directories existed.

Without the table, prisma.activeSession.findUnique() throws P2021,
crashing the tRPC handler with 500 on every authenticated request.
This silently emptied all admin pages (users, system-roles, etc.).

Changes:
- Wrap the jti ActiveSession lookup in try-catch so the tRPC handler
  degrades gracefully (fail-open) if the table is temporarily missing
- Add packages/db/prisma/migrations/20260401000000_active_sessions/
  so prisma migrate deploy creates the table on next production deploy
  (idempotent via IF NOT EXISTS — safe if table already exists)

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 18:38:05 +02:00
Hartmut 0e119cfe73 security: close audit findings #19–#23 and harden Docker setup (#24)
#19 MFA QR code: render locally via qrcode package, remove external qrserver.com request
#20 Webhook SSRF: add ssrf-guard.ts with DNS-verified IP blocklist; enforce on create/update/test/dispatch
#21 /api/perf: fail-closed when CRON_SECRET missing; remove query-string token auth
#22 CSP: remove unsafe-eval and unsafe-inline from script-src in production builds
#23 Active session registry: forward jti into session object; validate against ActiveSession on every tRPC request

#24 Docker: add missing packages/application to Dockerfile.dev; fix pnpm-lock.yaml glob;
    run db:migrate:deploy on container start so a fresh checkout boots without manual steps

Also: fix pre-existing TS error in e2e/allocations.spec.ts (args.length literal type overlap)

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-01 18:19:21 +02:00
Hartmut 8c5be51251 feat(platform): checkpoint current implementation state 2026-04-01 07:42:03 +02:00
Hartmut cd78f72f33 chore: full technical rename planarchy → capakraken
Complete rename of all technical identifiers across the codebase:

Package names (11 packages):
- @planarchy/* → @capakraken/* in all package.json, tsconfig, imports

Import statements: 277 files, 548 occurrences replaced

Database & Docker:
- PostgreSQL user/db: planarchy → capakraken
- Docker volumes: planarchy_pgdata → capakraken_pgdata
- Connection strings updated in docker-compose, .env, CI

CI/CD:
- GitHub Actions workflow: all filter commands updated
- Test database credentials updated

Infrastructure:
- Redis channel: planarchy:sse → capakraken:sse
- Logger service name: planarchy-api → capakraken-api
- Anonymization seed updated
- Start/stop/restart scripts updated

Test data:
- Seed emails: @planarchy.dev → @capakraken.dev
- E2E test credentials: all 11 spec files updated
- Email defaults: @planarchy.app → @capakraken.app
- localStorage keys: planarchy_* → capakraken_*

Documentation: 30+ .md files updated

Verification:
- pnpm install: workspace resolution works
- TypeScript: only pre-existing TS2589 (no new errors)
- Engine: 310/310 tests pass
- Staffing: 37/37 tests pass

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-27 13:18:09 +01:00
Hartmut ddec3a927a feat: timeline multi-select, demand popover, resource hover card, merged tooltips, dark mode fixes
Major timeline enhancements:
- Right-click drag multi-selection with floating action bar (batch delete/assign)
- DemandPopover for demand strip details (replaces broken "Loading" modal)
- ResourceHoverCard on name hover showing skills, rates, role, chapter
- Merged heatmap+vacation tooltips into unified TimelineTooltip component
- Fixed overbooking blink animation (date normalization, z-index ordering)
- Fixed dark mode sticky column bleed-through in project view
- System roles admin page, notification task management, performance review docs

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-18 23:43:51 +01:00
Hartmut dd55d0e78b chore(repo): initialize planarchy workspace 2026-03-14 14:31:09 +01:00