diff --git a/AUDIT_REPORT.md b/AUDIT_REPORT.md index 2857ce8..6a4b1ec 100644 --- a/AUDIT_REPORT.md +++ b/AUDIT_REPORT.md @@ -238,5 +238,30 @@ Severity counts: **Critical 0 · High 3 · Medium 6 · Low 7** | 11 | L-2/L-3/L-4/L-10 (AllowedHosts, SMTP log level, DB TLS note, CI format check) | Low | S each | Isolated | | 12 | L-5 bundle split / grid-layout removal (with redesign) | Low | S–M | Frontend | -**Stopping here per Phase 1 instructions — no changes made. Awaiting approval of this plan -(or an edited subset) before implementing anything.** +--- + +# Phase 2 — Remediation status (2026-07-02, all items approved & implemented) + +Shipped as four PRs (#20–#23), each with tests, green CI, and a verified staging deploy. + +| Finding | Status | How it was resolved | PR | +|---------|--------|---------------------|----| +| **H-1** validators never ran | ✅ **Resolved** | `AddFluentValidationAutoValidation()`; invalid DTOs now 400 at the boundary. Proven by 2 integration tests (pageSize=0, From>To → 400) via a new test-auth scheme | #20 | +| **H-2** no rate limiting | ✅ **Resolved** | Global 300/min per-user (per-IP anonymous) + `auth` 10/min + `expensive` 20/min (export/unsubscribe/AI), config-driven, 429/no-queue. Proven by a 429-on-3rd-request test | #20 | +| **H-3** plaintext bodies + unbounded retention | ✅ **Resolved (as scoped)** | `SECURITY.md` documents the deliberate posture (volume-encryption + backup guidance, delete-my-data procedure); **opt-in retention** (`DataRetention:*`, default off) + daily purge worker with 3 tests. Field-level encryption deliberately deferred (FTS can't index encrypted columns) — revisit before any multi-user deployment | #22 | +| **M-1** sliding-only sessions | ✅ **Resolved** | Absolute 30 d cap (`Auth:AbsoluteSessionDays`) via issued-at stamp checked in `OnValidatePrincipal`; 3 unit tests (incl. missing-stamp = expired) | #20 | +| **M-2** no CSP on the SPA | ✅ **Resolved** | Full CSP (`script-src 'self'`, `frame-ancestors 'none'`, …) + nosniff/XFO/Referrer-Policy + gzip on the SPA nginx; inline theme script moved to `/theme-init.js` to keep `script-src 'self'` honest. **Verified serving on staging** | #21 | +| **M-3** unprotected DP key ring | ✅ **Resolved (opt-in)** | `DataProtection:CertificatePath/Password` → `ProtectKeysWithCertificate`; documented in SECURITY.md as recommended for shared hosts | #22 | +| **M-4** default DB password | ✅ **Resolved** | Guessable default removed from `appsettings.json`; startup fails fast with a clear message when the connection string has no password | #20 | +| **M-5** dev-dep esbuild CVEs | ✅ **Resolved** | vite 5→8 + plugin-react 6; `npm audit` now clean **including dev deps**; build verified | #21 | +| **M-6** EF query-filter warning | ✅ **Resolved** | Matching tenant filter on `EmailLabel` (via Email navigation); boot warning confirmed gone from staging logs; cross-user invisibility test added | #20 | +| **M-7** no live-Postgres tests | ✅ **Resolved** | CI `db-tests` job with a `pgvector/pg16` service container runs 3 permanent `Category=LiveDb` regression tests (FTS weighting, ts_headline sentinels, trigram typo fallback, pgvector cosine). **Confirmed green on the actual runner** | #23 | +| **L-2** AllowedHosts `*` | ✅ Documented | Production checklist item in SECURITY.md (set at deployment) | #22 | +| **L-3** SMTP recipient at Info | ✅ **Resolved** | Downgraded to Debug | #20 | +| **L-4** DB TLS note | ✅ Documented | SECURITY.md: add `SSL Mode=Require` if Postgres ever leaves the host | #22 | +| **L-10** no CI format gate | ✅ **Resolved** | `format` CI job (`dotnet format --verify-no-changes`) — `--no-verify` pushes can no longer bypass formatting | #23 | +| L-1 CSRF (accepted risk) | ✅ Documented | SECURITY.md rationale (SameSite=Lax + CORS + JSON) with revisit conditions | #22 | +| L-5/L-6/L-7/L-8/L-9 | ⏸ Deferred by design | Bundle-split & grid-layout removal ride the UI redesign; SAST/CodeQL, sync transaction hardening, and broader service coverage are Phase 3 recommendation candidates | — | + +**Test suite: 40 → 54 tests** (48 always-on + 3 retention + 3 live-DB in CI). +Every deploy through the pipeline stayed green; staging verified after each batch.