The v1.4.1 audit pass turned up one CRITICAL, two HIGH, and four
medical-citation drift findings. This commit addresses everything
that's safe to land in a hardening release; deeper architectural
fixes (idempotency-race transactional rewrite, encryption-key
fallback gate, refresh-token reuse-detection serialisation, moodLog
secret HMAC lookup column) are tracked for v1.4.2 in
docs/ops/v141-followup-issues.md.
Security
--------
CRITICAL — moodLog SSRF
src/lib/validations/moodlog.ts now refines the credentials URL
with isPublicUrl() so an authenticated user cannot store
http://169.254.169.254/ (cloud metadata) or RFC1918 hosts.
src/lib/moodlog/sync.ts re-checks isPublicUrl(baseUrl) at the
fetch site (so legacy rows stored before the credential guard are
also refused) and switches to redirect: "manual" + 3xx → fail so a
public host cannot 302 the request to an internal target with the
user's apiKey on the redirect hop. New unit test
src/lib/validations/__tests__/moodlog.test.ts asserts the guard
rejects 169.254.x.x, 10.x, 172.16.x.x, 192.168.x.x, 127.0.0.1, and
localhost.
HIGH — error-message secret leakage
New src/lib/logging/redact.ts redacts Bearer tokens (incl. our
hlk_/hlr_), Telegram bot URLs (bot<digits>:<token>), and
query-string secrets (?secret=, ?code=, ?token=, ?api_key=). The
redactor is called from WideEventBuilder.setError() — every error
reaching Loki is scrubbed once, centrally — and from the
reportToGlitchtip() call in api-handler.ts so the Glitchtip
incident UI is also protected. New unit suite covers the four
patterns plus over-redaction (Bearer's \\S+ greedily consumes
anything up to the next whitespace, which is the safer side of
the trade-off).
Truthfulness — citation drift fixes (audit found 0 hallucinations,
4 drift items)
- messages/{en,de}.json bpClassificationTitle and avg30dEsc:
"ESC/ESH 2018" → "ESH 2023". User-visible on the dashboard and
doctor-report PDF. The numeric thresholds are unchanged from
2018; the 2023 update is ESH-only since ESC withdrew from
joint authoring.
- src/lib/analytics/classifications.ts: section header,
getBpTargetsByAge() docblock, and inline comment updated to
cite ESH 2023 with the same "numerically unchanged" caveat.
- src/app/api/insights/targets/route.ts:375: source label "WHO"
→ "Saint-Maurice JAMA 2020". The AI-prompt anti-pattern guard
explicitly forbids citing WHO for a step number; this surface
was the last "WHO" mislabel in the codebase.
- Saint-Maurice "mortality plateau 8000-12000" attribution
softened to "continued dose-response benefit through ~12,000
steps/day, not a plateau" in src/lib/medical-citations.ts,
src/lib/analytics/effective-range.ts,
src/lib/analytics/classifications.ts, and both AI prompts. The
original JAMA 2020 paper shows continuing benefit (HR 0.49 at
8k vs 4k, HR 0.35 at 12k); the plateau-shaped finding belongs
to Paluch 2022 Lancet PH (PMID 35247352), not Saint-Maurice.
Quality gates
pnpm typecheck clean
pnpm test 669 / 669 (was 658; +11 from new suites)
pnpm test:integration 10 / 10 (untouched)
pnpm lint 0 errors
HealthLog — personal health-tracking PWA with weight, blood pressure,
pulse, mood tracking, medication compliance, Withings integration,
doctor report PDF export, and Dracula-themed UI.