Commit Graph
6 Commits
Author SHA1 Message Date
Marc-André Bombeck 6c27e053b7 test: route the global fetch stub onto the undici pinned engine
The requirePublicHost egress paths now dispatch through undici's own
fetch, so the existing global fetch stubs in the AI, GeoIP, moodlog,
send, and ntfy suites no longer intercept them. Delegate undici's fetch
back to the installed global stub so each suite keeps asserting its
request shape and canned responses without exercising the network.
2026-06-28 15:18:20 +02:00
Marc-André BombeckandClaude Opus 4.7 8528c3b4fe chore(release): v1.5.6
Pure-history medication detail page, legacy step consolidation, and the
finish of the outbound-fetch migration.

- /medications/[id] becomes a read-only history surface; every setting
  moves into a new advanced-settings sheet, and the edit trigger becomes
  a two-option picker. The list page is unchanged.
- A pg-boss job folds pre-v1.5.0 granular step rows into one canonical
  daily total per user per day and soft-deletes the originals; migration
  0087 adds a partial index for the converging discovery scan.
- Every remaining server-side fetch routes through safeFetch (Withings,
  Codex OAuth + SSE, bug-report, Umami, GlitchTip, Loki); a new
  healthlog/safe-fetch-required ESLint rule guards the boundary.
- Fixes: intake-edit dialog seeded from real values, consolidation skips
  a unique-constraint day instead of stranding it, avatar upload reads
  the body once under a bounded cap, egress lint rejects absolute URLs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 22:07:10 +02:00
Marc-André BombeckandClaude Opus 4.7 425503e095 feat(security): safeFetch wrapper with manual redirect and timeout defaults
Closes issue 218. Flips the convention from "remember to opt in to
redirect: manual" to "remember to opt out". Every outbound fetch that
doesn't pin a hard-coded host now routes through safeFetch and inherits
AbortSignal.timeout(15_000) so a tar-pit upstream can no longer pin a
worker indefinitely. The local AI client also gates on
requirePublicHost: true since its host is user-supplied; operators
running self-hosted Ollama / LM Studio on an RFC1918 address keep their
escape hatch via ALLOW_LOCAL_AI_PRIVATE_HOSTS=true (also enforced at
write-time in /api/user/ai-provider).

14 outbound call sites migrated. The four that already pinned manual
redirect (the historic templates) keep their semantics; the wrapper
preserves an explicit init.redirect. Adds undici as an explicit
dependency so the connect-time pin landing alongside this can rely on
the documented API rather than a transitive hop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:32:11 +02:00
Marc-André BombeckandClaude Opus 4.7 a4e4aaf009 fix(security): replace isAllowedUmamiUrl with the canonical isPublicUrl guard
`/api/send` is the public Umami proxy. The hand-rolled allowlist it
carried predated the central `isPublicUrl` helper in
`src/lib/validations/notifications.ts` and was missing every
notation-bypass class the helper catches:

- 100.64.0.0/10 CGNAT
- 0.0.0.0/8 (only the literal `0.0.0.0` was matched)
- 169.254/16 outside the AWS metadata IP (e.g. azure metadata is
  `169.254.169.254` too, but other addresses in the block are
  routable to other internal endpoints)
- IPv6 ULA (`fc00::/7`)
- IPv4-mapped IPv6 (`::ffff:127.0.0.1`)
- Leading-zero IPv4 octal bypass (`http://010.0.0.1` → 8.0.0.1)
- Hex IPv4 (`http://0x7f.0.0.1`)
- Pure-decimal IPv4 (`http://2130706433`)

The dev-mode escape hatch is also dropped — serving the proxy against
a localhost / RFC1918 Umami target was never a useful configuration
anyway, since the proxy sends the visitor's user-agent + body upstream
which only makes sense against an externally-reachable analytics host.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:08:26 +02:00
Marc-André Bombeck ca03c6099f chore(release): v1.4.24
Security + accessibility hardening release. Closes the highest-priority
findings from a focused security audit of the v1.4.23 release:

- Bearer auth fail-closed when a route declares no scope (api-handler)
- Public /api/send Umami proxy now rate-limits 120 req/min/IP
- Proxy nonce generation switched to btoa(...) for Edge portability
- Session-expiry delete race uses deleteMany + catch (no more 500s)
- POST /api/import returns the standard apiError envelope on 422
- Runtime Docker image pins Prisma 7.8 + worker deps to minors
- Notifications page renders a real load-error alert
- Accessibility: switch aria-labels, htmlFor pairing, mobile admin
  Overview link, deep-link anchors on notification settings
- Chart overlays: per-chart comparisonBaseline pref, BMI/bodyFat/
  sleep/steps overlay keys, TrendCard layout tolerates long labels

Verification: pnpm typecheck && pnpm lint && pnpm test (2244/2244)
&& pnpm format:check all green.
2026-05-12 22:33:35 +02:00
Marc-André Bombeck 38309932f3 Initial release v1.0.0
HealthLog — personal health-tracking PWA with weight, blood pressure,
pulse, mood tracking, medication compliance, Withings integration,
doctor report PDF export, and Dracula-themed UI.
2026-03-10 20:14:33 +01:00