gabriel / musehub public
14-application-security.md markdown
70 lines 4.9 KB
Raw
sha256:7d5985ef251de9f0154f9b185a75cf36174bf73e0c34f5eca133e7bd20224bf4 Merge branch 'feat/opengraph-repo-cards' into dev Human 22 days ago

Section 14 — Application Security

Companion to musehub-production-readiness-checklist.md.

✅ Fixed — /_debug/memory was exposed unconditionally in production

musehub/main.py registered /_debug/memory (live RSS + tracemalloc top allocations) without the if settings.debug: guard that correctly protects /docs and /redoc in the same file — reachable by anyone in production, no auth check. Fixed in this session by wrapping the route registration in the same if settings.debug: guard used elsewhere in the file. Deployed to staging/production whenever the next regular deploy runs.

What's already solid — confirmed by code review

  • No SQL injection surface found. No raw string-formatted SQL anywhere in musehub/ (grepped for f"..."/.format()/% patterns near SQL keywords — none found outside an unrelated file-write). The app uses SQLAlchemy's ORM/parameterized queries throughout.
  • Path traversal explicitly rejected in the coordination module — opaque alphanumeric ID validation with an explicit comment noting traversal-char rejection.
  • Archive/decompression bomb protection exists: mpack_max_decompressed_bytes (4 GB cap) in musehub/config.py — mpacks are quarantined if decompressed size exceeds this, directly answering the checklist's "protect against unsafe archive extraction."
  • No SSRF-prone outbound URL fetching found in the app's request-handling code (grepped for httpx.get/requests.get/etc. — none matched); musehub/worker.py's Cloudflare Worker interaction wasn't independently reviewed for this in this pass, worth a closer look given it's the one component that talks to an external URL.
  • Debug mode is off by default (debug: bool = False in Settings), and /docs//redoc are correctly registered only if settings.debug: — Swagger/ReDoc won't leak in production.
  • MSign auth is well-implemented for privileged actions: musehub/api/routes/mcp.py requires a fresh signed request (not just a valid session) for sensitive tool calls — this directly satisfies "verify privileged actions require fresh authorization," which most apps get wrong.
  • No passwords stored anywhere — MSign is signature-based, so "secure password hashing," "email verification," and "secure account recovery" (as password-reset-flow concepts) are N/A. Account recovery in this model is really "recover your Ed25519 key," which is covered by the mnemonic-backup flow documented in the global ecosystem guide, not by this app.
  • Rate limiting on auth-adjacent endpoints: confirmed in Section 5 (slowapi limits, bot throttling exempting only authenticated MSign traffic).
  • CORS fails closed: cors_origins: list[str] = [] by default, with an explicit warning log if a wildcard is ever configured alongside debug=false.
  • Security headers and CSP already set (Section 5): X-Frame-Options, X-Content-Type-Options, CSP, HSTS.
  • Upload limits exist: mpack_max_bytes (512 MB), mpack_max_commits (100k), mpack_max_objects (1M), per-user (10 GB) and per-repo (5 GB) quotas, per-user daily upload cap (50 GB) — thorough, deliberate limits, not just a single blanket body-size cap.

Gaps / not reviewed in this pass

  • [ ] CSRF protection — not reviewed; likely lower risk given no cookie-based sessions exist (MSign is header-based, not vulnerable to classic CSRF the way cookie auth is), but not explicitly confirmed for any state-changing endpoint that might still rely on ambient browser credentials
  • [ ] Uploaded content scanning (malware/content scanning beyond size/structure limits) — not done
  • [ ] WebSocket/SSE authentication — MCP/SSE endpoints exist (3600s timeouts, Section 5); auth model for the streaming case specifically wasn't independently verified in this pass
  • [ ] Data classification and retention — not documented
  • [ ] Account and data deletion path — not verified whether one exists
  • [ ] OWASP-oriented review — this pass covers several OWASP Top 10 categories (injection, broken auth, security misconfiguration via the debug-endpoint finding) but isn't a formal OWASP pass
  • [ ] Session expiration / token revocation — N/A in the traditional sense (no sessions); MSign signatures are per-request and time-stamped (ts=...), which is a different but likely equivalent guarantee — not independently verified for replay-window correctness in this pass

The actual work items here

  1. Fix /_debug/memory's missing guard — small, fast, real fix.
  2. Review musehub/worker.py's outbound Cloudflare Worker calls for SSRF exposure specifically.
  3. Confirm MSign's timestamp/replay-window logic (ts=... in the signature) actually prevents replay attacks, not just informs logging.
  4. Do a dedicated OWASP-oriented pass closer to launch (Section 17), building on this section's findings rather than starting cold.
File History 1 commit
sha256:be1b2fdf6ab1fc652b3b35a945d0277a36fbf5d96e63692020d36cd7ebe03534 Merge 'docs/security-monitoring-verified' into 'dev' — prop… Human 2 days ago