diff --git a/docs/plans/2026-03-18-security-coverage-performance-design.md b/docs/plans/2026-03-18-security-coverage-performance-design.md new file mode 100644 index 00000000..88aefda8 --- /dev/null +++ b/docs/plans/2026-03-18-security-coverage-performance-design.md @@ -0,0 +1,116 @@ +# Security Headers, Coverage Gates & Web Vitals Design + +> **Date:** 2026-03-18 +> **Product:** ResolutionFlow +> **Branch:** `feat/security-headers-coverage-performance` +> **Purpose:** Add HTTP security headers, enforce test coverage gates, and instrument Core Web Vitals reporting + +--- + +## Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Security headers priority | First | Highest trust signal for MSP buyers, real protection | +| CSP rollout strategy | Report-only first, then enforce | Avoids breaking third-party integrations (PostHog, Sentry, Google Fonts, React Flow) | +| Backend coverage gate | 80% fail threshold | Already near this level, prevents drift | +| Frontend coverage gate | Report-only (no gate yet) | Starting from zero — establish baseline first | +| Web Vitals destination | PostHog | 100% of sessions captured (vs Sentry's 20% sample), correlate with product analytics | + +--- + +## 1. Security Headers Middleware + +### New file: `backend/app/core/security_headers.py` + +Starlette middleware that adds security headers to every response. + +### Headers + +| Header | Value | Purpose | +|--------|-------|---------| +| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | Force HTTPS for 1 year | +| `X-Content-Type-Options` | `nosniff` | Prevent MIME sniffing | +| `X-Frame-Options` | `DENY` | Block iframe embedding | +| `Referrer-Policy` | `strict-origin-when-cross-origin` | Limit referrer leakage | +| `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | Disable unused browser APIs | +| `Content-Security-Policy-Report-Only` | *(see below)* | CSP in report-only mode | + +### CSP Directive (report-only) + +``` +default-src 'self'; +script-src 'self' https://us.i.posthog.com https://*.sentry.io; +style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; +font-src 'self' https://fonts.gstatic.com; +img-src 'self' data: blob:; +connect-src 'self' https://us.posthog.com https://us.i.posthog.com https://*.sentry.io https://api.resolutionflow.com; +frame-ancestors 'none'; +base-uri 'self'; +form-action 'self' +``` + +### Wiring + +- Add middleware in `main.py` after CORS middleware (so CORS preflight responses aren't affected) +- CSP directives configurable via `config.py` so promotion to enforcing mode is a config change, not a code change +- HSTS only sent when `DEBUG=false` (avoid locking localhost into HTTPS) + +### Tests + +Integration test that hits an endpoint and asserts all expected headers are present with correct values. + +--- + +## 2. Coverage Gates + +### Backend: Enforce at 80% + +- Add `--cov-fail-under=80` to the pytest command in CI +- One-line change — reporting already wired up with `pytest-cov` + +### Frontend: Report-only (establish baseline) + +- Install `@vitest/coverage-v8` as dev dependency +- Add coverage config to `vite.config.ts`: + - Reporters: `text` + `json-summary` + `html` + - Include: `src/**/*.{ts,tsx}` + - Exclude: `src/test/`, `src/types/`, `**/*.d.ts` +- Add `test:coverage` script to `package.json` +- Update CI to run `npm run test:coverage` instead of `npm test` +- Display summary in CI output — no failure threshold yet +- Add `coverage/` to `.gitignore` + +--- + +## 3. Web Vitals → PostHog + +### Install + +`web-vitals` npm package. + +### New file: `frontend/src/lib/webVitals.ts` + +- Import `onLCP`, `onINP`, `onCLS`, `onFCP`, `onTTFB` from `web-vitals` +- Each callback sends a PostHog event (`web_vitals`) with properties: + - `metric_name` — LCP, INP, CLS, FCP, TTFB + - `metric_value` — numeric value + - `metric_rating` — good / needs-improvement / poor + - `page_path` — current route +- Single `initWebVitals()` function that registers all observers + +### Wiring + +Call `initWebVitals()` in `main.tsx` after PostHog initialization. + +--- + +## Scope Summary + +| Area | Scope | Files | +|------|-------|-------| +| Security headers | New middleware + config + test | 3-4 backend files | +| Coverage gates | CI config + vitest coverage setup | CI workflow + 3 frontend config files | +| Web Vitals | New lib + dependency + main.tsx wiring | 2-3 frontend files | + +Small, contained changes across all three. No architectural changes or new database models.