docs(audit): Phase 2 resolution status (#24)
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 11s
CI / format (push) Successful in 47s
CI / db-tests (push) Successful in 48s
CI / backend (pull_request) Successful in 50s
CI / frontend (pull_request) Successful in 10s
CI / format (pull_request) Successful in 47s
CI / db-tests (pull_request) Successful in 49s
Deploy Staging / deploy (push) Successful in 15s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 52s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 52s
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 11s
CI / format (push) Successful in 47s
CI / db-tests (push) Successful in 48s
CI / backend (pull_request) Successful in 50s
CI / frontend (pull_request) Successful in 10s
CI / format (pull_request) Successful in 47s
CI / db-tests (pull_request) Successful in 49s
Deploy Staging / deploy (push) Successful in 15s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 52s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 52s
This commit was merged in pull request #24.
This commit is contained in:
+27
-2
@@ -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 |
|
| 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 |
|
| 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.
|
||||||
|
|||||||
Reference in New Issue
Block a user