Skip to content

Security hardening from an external audit - #60

Merged
josedaniel-dev merged 1 commit into
mainfrom
fix/security-hardening
Oct 16, 2026
Merged

josedaniel-dev merged 1 commit into
mainfrom
fix/security-hardening

Conversation

@josedaniel-dev

Copy link
Copy Markdown
Collaborator

Addresses the findings of an external review of main at ea5671c. I checked each against the code before changing it; all were real, as described.

Deployment (High)

  • Compose (docker and podman) published the API and dashboard on all interfaces with no key, and PostgreSQL on 5432 with postgres/postgres. Now: backend and dashboard on ${OBSERVER_BIND:-127.0.0.1}, PostgreSQL not published, no default POSTGRES_PASSWORD (compose refuses to start without it), OBSERVATORY_API_KEY passed through. The backend logs a warning at start when no key is set. A test parses both compose files and fails on a public bind, a published database port or a literal password.
  • Dashboard dev/preview proxy lent the backend key to any caller: it now adds it only to requests from loopback and listens on 127.0.0.1 (OBSERVER_DASHBOARD_HOST). Caveat in docs/security.md: a reverse proxy on the same host makes every request look local, so authenticate in front. The container dashboard (nginx) still does not add the key.

Ingestion limits (Medium) — body-size middleware (16 MiB, also for streamed bodies), spans per trace/batch, traces per import, string lengths, JSON field size (256 KiB) and depth (12) on the generic routes; all configurable (docs/security.md). Defaults are generous so ordinary SDK payloads (50 KB prompts) still pass; tested.

Rate limiting (Medium) — the limiter trusted any Authorization value, so rotating fake tokens bought a fresh budget. It now uses the API key identity only when it is the configured key, else the client IP.

MCP (Medium) — attributes and trace metadata are now an allow-list of structural keys (scalars, single line, 200 chars) instead of passing everything; dataset notes, descriptions and expected phrases appear only as counts. OBSERVER_MCP_CONTENT=1 opts out. Span/trace names are still free text (shortened, control characters stripped).

Published internals (Medium) — removed docs/handoff-codex-2026-10-08.md (and its reference in AGENTS.md), the Manitos module path and PR/restart details from docs/manitos-integration.md and the roadmap, and a service-name command from the setup guide. Deleting files does not remove them from history; the audit found no secret that would justify rewriting it.

Tests: 9 new/updated regression tests that fail without the fixes (limits, body middleware, rate-limit identity, compose, MCP allow-list and withheld text), proxy-auth unit tests. Backend 219 passed, dashboard 39, ruff/tsc/build clean.

Not covered / for you: the audit did not run a dependency-vulnerability scan and neither did I; I could not run docker/podman here, so the compose change is checked by parsing, not by up; and OBSERVER_BIND, the API key and a front proxy remain your deployment choices.

Merge order: independent of #58 and #59, but all three touch docs/roadmap.md (and #58/#59 i18n-en.ts), so the later ones will need a rebase.

Deployment: compose binds backend and dashboard to loopback (OBSERVER_BIND), no longer publishes
PostgreSQL and has no default database password; the backend warns at start when no API key is set.
The dashboard dev/preview proxy lends the API key only to loopback requests and listens on
127.0.0.1. Generic ingestion gets limits: body size middleware, span counts, JSON size and depth,
string lengths. The rate limiter keys on the API key only when it is the configured one, otherwise on
the IP. The MCP server allow-lists attributes and metadata and withholds dataset notes and phrases.
Internal hand-off notes and operational details are removed from the docs; docs/security.md added.
@josedaniel-dev
josedaniel-dev merged commit b9c5ee4 into main Oct 16, 2026
5 checks passed
@josedaniel-dev
josedaniel-dev deleted the fix/security-hardening branch October 17, 2026 01:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant