Repository navigation
GhostBus v0.5.3 — hardening round 2
·
53 commits
to main
since this release
Hardening round 2 — the layer under v0.5.2's defaults.
What changed
- Timing-safe secret compares everywhere. The hosted workspace-key check, the core agent-token check, and the serverless pack each compared hashes with plain
===; all now use hash-then-timingSafeEqual. No plain secret compares remain. - Hosted failed-auth throttle. After 30 failed key/admin attempts per minute from one IP, the hosted server returns 429 on admin and workspace routes (valid traffic and /health unaffected once it clears). Guessing keys at a paid host is now expensive.
- 1 MB request-body cap on every JSON route (hosted admin, in-workspace, single relay): oversized bodies get a clean 413. Note: workspace exports over 1 MB can no longer be imported over HTTP — use the CLI or place the state file (documented).
- Board + landing headers:
X-Frame-Options: DENYand a content security policy (frame-ancestors 'none', object-src 'none'; inline script/style allowed because the board is one self-contained page loading zero external resources). - Data files are 0600. Workspace state and the registry hold key/token hashes; they're now written owner-only via the atomic save path.
- Generic 500s. Internal errors log server-side (with stack) and return
{error:'internal error'}— no internals leaked to clients. Deliberate 400 validation/auth errors are unchanged. - Admin audit log. Workspace create/delete/rotate-key/export/import each emit one structured line (timestamp, action, workspace, IP) — never keys or hashes.
- SECURITY.md added: supported version, reporting address, one-business-day acknowledgement.
Verified: 54 checks in the main suite (was 51; the 3 new checks fail on v0.5.2 for exactly the intended reasons), serverless 6/6, both demos, live hosted smoke.
Scope boundary, honestly: the auth throttle is per-IP and in-memory — behind a shared NAT, one guesser can briefly 429 that IP's unauthenticated attempts. Timing safety is verified by audit + functional checks; a timing channel isn't observable from a functional test.