You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Security hardening: auth replay, encryption guards, rate limits, CORS (#209)
* Security hardening: auth replay, encryption guards, rate limits, CORS, error sanitization (#209)
Address 18 security audit findings (7 HIGH, 8 MEDIUM, 3 LOW) across the
MCP HTTP server, plus test and observability improvements.
HIGH:
- H1: Fail-closed encryption — encrypt/decrypt raise RuntimeError in HTTP
mode when UPLOAD_SECRET is missing. Required regardless of --no-auth.
- H2: ALLOW_NO_AUTH strict check — only "1" disables auth.
- H3: Redis password required in authenticated HTTP mode (hard error).
- H4: HTTPS-only URL validation on mcp_server_url and supabase_url.
- H5: Referrer-Policy no-referrer meta tag in both widget templates.
- H6: Auth code and refresh token re-storage uses SET NX to prevent
concurrent overwrites.
- H7: OAuth state consumed atomically in handle_start, re-stored to
narrow replay window.
MEDIUM:
- M1: Security events (revoked tokens, failed polls, rate limits) at WARNING.
- M2: Stack traces suppressed in error responses; generic messages returned.
- M3: app.openLink() calls guarded with https scheme check.
- M4: rel="noopener noreferrer" on linkified anchors in results widget.
- M5: Removed in-memory rate limit fallback — Redis is a hard dependency.
- M6: Rate limit TTL always refreshed (removed nx=True on EXPIRE).
- M7: CORS wildcard origin for widget endpoints (auth via Bearer tokens).
- M8: Content-Type allowlist on upload endpoint (415 for unsupported types).
LOW:
- L1: Per-user upload rate limiting via atomic Redis pipeline.
- L4: Remote Redis without SSL fails in HTTP mode (warns in stdio).
Additional:
- Upload tool fails early when client has no API token.
- Direct path_params lookup for state in handle_start.
- E2e integration test: swap results tool to HTTP variant in _http_state.
- Fernet cache cleared in test override_settings for correct encryption.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix upload URL consumed before rate-limit check
Peek upload metadata (non-destructive GET) during validation, then
atomically pop (GETDEL) only after content-type, token, and rate-limit
checks pass. This makes 429/415/403 responses retryable without burning
the one-time upload URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
0 commit comments