From f5962497d6922587b0917496a44f205f40d8f43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 17 May 2026 00:39:58 +0200 Subject: [PATCH] =?UTF-8?q?v1.4.34.4=20=E2=80=94=20audit-driven=20hotfix?= =?UTF-8?q?=20bundle=20(security,=20UX,=20code-quality,=20docs)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidated hotfix landing the findings from a six-axis audit covering mobile security, UX + accessibility, code quality + docs, README + discoverability, web performance, and mobile-deep responsive behaviour. None of the changes break the public API or the existing data shape; every fix is additive and SAFE under the v1.4.34.x web freeze. Full audit reports live in `.planning/round-audit-*.md`. Security - Password rotation now wipes API tokens + refresh tokens in the same `$transaction` as session revocation. A stolen iOS Bearer no longer survives a user-initiated password change or an admin reset. - `/.well-known/` proxy bypass tightened to an explicit-allowlist `Set`. Stale `/api/auth/codex/callback` PUBLIC_PATHS entry dropped. - HSTS gains `preload`. Withings host removed from the global CSP and gated to the integration's own routes. - `getClientIp` warns once per process on `TRUST_PROXY_HOPS`/XFF mismatch + exposes a tagged helper for tighter universal buckets. - Service worker rejects off-origin notification-click URLs. Offline fallback body switched to a locale-neutral icon. Manifest gains `scope`, `id`, `display_override`. UX + accessibility - i18n leaks closed on Dialog/Sheet close-X, ChartSkeleton's loading announcement, and the notifications-section breadcrumb. - Focus rings on bottom-nav, the More overflow button, and the mobile top-bar user menu. - Tap-target floor: Input / NativeSelect / Select / DateTimeInput switch to `h-11 sm:h-10`. - Admin chip-strip auto-scroll-into-view (matches v1.4.33 settings). - List-page subtitles visible on mobile. - Insights empty-state CTA vocabulary normalised so the link opens the right measurement form (was silently failing for BODY_TEMPERATURE / BLOOD_GLUCOSE / PULSE). Mobile-deep - `viewport.themeColor` reacts to light/dark mode. - TopBar + Coach FAB reserve safe-area-inset-top / -bottom for notched iPhones. - Sonner toaster switches to `theme="system"`. Code quality - `SESSION_SECRET` dropped — the env var was documented as required but never read by code. Three secrets, not four. Self-hosters upgrading can delete the existing line. - `toJson()` helper consolidates seven Prisma JSON-cast sites. - `db-compat.ts` gains a header docstring naming it the schema-bootstrap path. - Passkey response types adopted from `@simplewebauthn/server`; `any` + eslint-disable pragmas drop out. - Zero-TODO CI gate added at `.github/workflows/no-todo-markers.yml`. Documentation + discoverability - README rework: How-it-compares matrix, Apple Health + AI Coach promoted to standalone Key-Features bullets, status block, latest release + GHCR badges, OpenAPI pointer, three-secret Quick Start. - openGraph + twitter cards now carry `images` and a `summary_large_image` Twitter card. - GitHub repo metadata: description tightened from 290 to 156 chars + keyword-front, `homepageUrl` flipped from a personal tenant to the demo site, six generic / PII-adjacent topics swapped for six high-intent ones. - Five new user-facing docs: `docs/self-hosting/getting-started.md`, `docs/self-hosting/reverse-proxy.md`, `docs/integrations/apple-health.md`, `docs/integrations/withings.md`, `docs/integrations/ai-providers.md`. - `docs/README.md` clarifies the docs tree as internal playbooks vs the user-facing `docs.healthlog.dev` site. --- .env.example | 7 +- .github/workflows/no-todo-markers.yml | 39 ++ .planning/round-audit-code-quality.md | 290 +++++++++++++ .planning/round-audit-docs-depth.md | 196 +++++++++ .planning/round-audit-docs-discoverability.md | 235 +++++++++++ .planning/round-audit-mobile-deep.md | 380 ++++++++++++++++++ .planning/round-audit-mobile-security.md | 98 +++++ .planning/round-audit-perf.md | 105 +++++ .planning/round-audit-test-coverage.md | 309 ++++++++++++++ .planning/round-audit-ux.md | 153 +++++++ CHANGELOG.md | 150 +++++++ CONTRIBUTING.md | 4 +- README.md | 41 +- docker-compose.yml | 2 - docker-entrypoint.sh | 2 +- docs/README.md | 33 ++ docs/integrations/ai-providers.md | 244 +++++++++++ docs/integrations/apple-health.md | 200 +++++++++ docs/integrations/withings.md | 195 +++++++++ docs/self-hosting/getting-started.md | 154 +++++++ docs/self-hosting/reverse-proxy.md | 198 +++++++++ docs/self-hosting/scaling.md | 3 +- messages/de.json | 2 + messages/en.json | 2 + messages/es.json | 2 + messages/fr.json | 2 + messages/it.json | 2 + messages/pl.json | 2 + package.json | 2 +- public/manifest.json | 3 + public/sw.js | 31 +- .../proxy-csp-hsts-hardening.test.ts | 103 +++++ src/__tests__/sw-security-hardening.test.ts | 97 +++++ .../__tests__/route.test.ts | 1 + .../api/auth/me/doctor-report-prefs/route.ts | 7 +- .../dashboard/chart-overlay-prefs/route.ts | 5 +- src/app/api/dashboard/widgets/route.ts | 4 +- .../healthkit/__tests__/route.test.ts | 1 + src/app/api/integrations/healthkit/route.ts | 5 +- src/app/layout.tsx | 27 +- .../__tests__/add-token-resolution.test.ts | 87 ++++ src/app/measurements/page.tsx | 36 +- src/app/medications/page.tsx | 4 +- src/app/mood/page.tsx | 4 +- src/components/admin/admin-shell.tsx | 34 +- src/components/charts/chart-skeleton.tsx | 6 +- src/components/insights/layout-coach-fab.tsx | 8 +- src/components/layout/bottom-nav.tsx | 9 +- src/components/layout/top-bar.tsx | 16 +- .../measurements/measurement-form.tsx | 37 ++ .../settings/notifications-section.tsx | 5 +- src/components/ui/date-input.tsx | 5 +- src/components/ui/dialog.tsx | 9 +- src/components/ui/input.tsx | 4 +- src/components/ui/native-select.tsx | 4 +- src/components/ui/select.tsx | 6 +- src/components/ui/sheet.tsx | 6 +- src/components/ui/sonner.tsx | 6 +- src/lib/__tests__/db-to-json.test.ts | 52 +++ src/lib/__tests__/get-client-ip.test.ts | 98 ++++- src/lib/api-response.ts | 75 ++++ src/lib/auth/__tests__/session.test.ts | 38 +- src/lib/auth/passkey.ts | 22 +- src/lib/auth/session.ts | 27 +- src/lib/db-compat.ts | 18 + src/lib/db.ts | 9 +- src/lib/jobs/apple-health-import-worker.ts | 13 +- src/proxy.ts | 53 ++- 68 files changed, 3904 insertions(+), 123 deletions(-) create mode 100644 .github/workflows/no-todo-markers.yml create mode 100644 .planning/round-audit-code-quality.md create mode 100644 .planning/round-audit-docs-depth.md create mode 100644 .planning/round-audit-docs-discoverability.md create mode 100644 .planning/round-audit-mobile-deep.md create mode 100644 .planning/round-audit-mobile-security.md create mode 100644 .planning/round-audit-perf.md create mode 100644 .planning/round-audit-test-coverage.md create mode 100644 .planning/round-audit-ux.md create mode 100644 docs/README.md create mode 100644 docs/integrations/ai-providers.md create mode 100644 docs/integrations/apple-health.md create mode 100644 docs/integrations/withings.md create mode 100644 docs/self-hosting/getting-started.md create mode 100644 docs/self-hosting/reverse-proxy.md create mode 100644 src/__tests__/proxy-csp-hsts-hardening.test.ts create mode 100644 src/__tests__/sw-security-hardening.test.ts create mode 100644 src/app/measurements/__tests__/add-token-resolution.test.ts create mode 100644 src/lib/__tests__/db-to-json.test.ts diff --git a/.env.example b/.env.example index d809bbdee..1de010c8e 100644 --- a/.env.example +++ b/.env.example @@ -4,10 +4,10 @@ # Copy this file to .env and fill in the values. # $ cp .env.example .env # -# Required variables: DATABASE_URL or POSTGRES_PASSWORD, SESSION_SECRET, +# Required variables: DATABASE_URL or POSTGRES_PASSWORD, # ENCRYPTION_KEY, API_TOKEN_HMAC_KEY # -# Generate the three secrets with: +# Generate the two secrets with: # $ openssl rand -hex 32 # any UNIX shell # $ node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))' # @@ -44,9 +44,6 @@ NODE_ENV="development" # ----------------------------------------------------------------------------- # Required Secrets — generate with `openssl rand -hex 32` # ----------------------------------------------------------------------------- -# Signs session cookies. Rotating it logs out everyone. -SESSION_SECRET="REPLACE-WITH-OUTPUT-OF-openssl-rand-hex-32" - # AES-256-GCM key for encrypting Withings tokens, GitHub PATs, AI provider # keys, and VAPID private keys at rest. Rotate carefully — old ciphertexts # need re-encryption. diff --git a/.github/workflows/no-todo-markers.yml b/.github/workflows/no-todo-markers.yml new file mode 100644 index 000000000..f72470531 --- /dev/null +++ b/.github/workflows/no-todo-markers.yml @@ -0,0 +1,39 @@ +name: No TODO markers + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +permissions: + contents: read + +jobs: + # Product code is currently zero TODO / FIXME / XXX / HACK markers and + # the convention is to keep it that way — open a real follow-up issue + # instead of leaving a marker in the source. This gate fails fast if a + # future contributor reintroduces the pattern. + grep: + name: Forbidden marker grep + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Scan src/ for TODO/FIXME/XXX/HACK markers + run: | + # Tests are exempt — they can legitimately reference an outstanding + # ticket via a TODO; `src/generated/` is third-party output from + # `prisma generate` and is regenerated on every db:generate. + if grep -rEn "\b(TODO|FIXME|XXX|HACK)\b" src/ \ + --include='*.ts' \ + --include='*.tsx' \ + --exclude-dir='__tests__' \ + --exclude-dir='generated' \ + --exclude='*.test.ts' \ + --exclude='*.test.tsx'; then + echo "::error::TODO/FIXME/XXX/HACK markers detected in product code — open a follow-up issue instead." + exit 1 + fi + echo "::notice::No forbidden markers in product code." diff --git a/.planning/round-audit-code-quality.md b/.planning/round-audit-code-quality.md new file mode 100644 index 000000000..04c99bb13 --- /dev/null +++ b/.planning/round-audit-code-quality.md @@ -0,0 +1,290 @@ +# Code Quality + Documentation Audit — 2026-05-17 + +## Executive summary + +The repo is in unusually good shape for its size: `tsc --noEmit` and `eslint` +both pass clean, there are zero `TODO`/`FIXME`/`XXX`/`HACK` markers in product +code, zero empty `catch` blocks outside one inline `