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
From PR #403's final adversarial security review. Each item is safe-at-loopback but MUST land before the gateway is exposed through a tunnel.
Host/Origin validation on the OAuth routes./mcp inherits rmcp's DNS-rebinding guard (allowed_hosts = ["localhost","127.0.0.1","::1"]), but the merged /register, /authorize, /token routers get nothing — a rebinding page can reach them same-origin and read the responses. Add a guard + a test pinning the behavior.
StreamableHttpServerConfig::allowed_hosts must include the tunnel hostname. rmcp's default is loopback-only, so /mcp will reject every tunnelled request until this is set. Functional landmine, not just security — the tunnel simply won't work without it.
Registration caps / rate limiting on /register. Unauthenticated remote write amplification: clients.json is fully re-serialized per write (O(N²) disk) and re-parsed under the global mutex on every later request, so a bloated store degrades /mcp too. PR feat(gateway): OAuth 2.1 authorization + resource server with pairing (Gateway PR 3) #403 added a per-registration redirect_uris cap and wired purge_expired; a client-count cap and a rate limit are still open.
Strengthen the consent page's identity signal. It currently shows only the redirect host; show the full escaped redirect_uri and label client_name as self-asserted. Attacker-controlled text in an <h1> is the whole basis of the user's trust decision.
Decide the CSRF posture explicitly. There is no CSRF token; the pairing code in the POST body is what defeats a blind cross-site auto-submit. That works, but it is a static, long-lived, reused secret. Add Origin/Sec-Fetch-Site checking on POST /authorize and/or rotate the pairing code after a successful authorization.
Document the global-lockout DoS tradeoff. Post-tunnel, anyone holding a client_id can keep the owner permanently locked out at 5 failures/60s. The global (not per-IP) lockout is the right shape for a single-user gateway, but the tradeoff should be written down.
Also worth folding in: RFC 8707 resource is validated but never bound onto TokenRecord (harmless with one resource, a latent trap with two); token_refresh_grant doesn't check client_id against the token's record (OAuth 2.1 §4.3.1 conformance, no escalation possible); no IPv6 loopback (http://[::1]) in is_loopback_redirect_uri (RFC 8252 §7.3 interop).
From PR #403's final adversarial security review. Each item is safe-at-loopback but MUST land before the gateway is exposed through a tunnel.
Host/Originvalidation on the OAuth routes./mcpinherits rmcp's DNS-rebinding guard (allowed_hosts = ["localhost","127.0.0.1","::1"]), but the merged/register,/authorize,/tokenrouters get nothing — a rebinding page can reach them same-origin and read the responses. Add a guard + a test pinning the behavior.StreamableHttpServerConfig::allowed_hostsmust include the tunnel hostname. rmcp's default is loopback-only, so/mcpwill reject every tunnelled request until this is set. Functional landmine, not just security — the tunnel simply won't work without it./register. Unauthenticated remote write amplification:clients.jsonis fully re-serialized per write (O(N²) disk) and re-parsed under the global mutex on every later request, so a bloated store degrades/mcptoo. PR feat(gateway): OAuth 2.1 authorization + resource server with pairing (Gateway PR 3) #403 added a per-registrationredirect_uriscap and wiredpurge_expired; a client-count cap and a rate limit are still open.public_urlhardening beyond PR feat(gateway): OAuth 2.1 authorization + resource server with pairing (Gateway PR 3) #403's validation — re-review once a real tunnel URL is in play (path-prefixed deployments also breakrender_consent_form's root-relativeaction="/authorize").redirect_uriand labelclient_nameas self-asserted. Attacker-controlled text in an<h1>is the whole basis of the user's trust decision.Origin/Sec-Fetch-Sitechecking onPOST /authorizeand/or rotate the pairing code after a successful authorization.client_idcan keep the owner permanently locked out at 5 failures/60s. The global (not per-IP) lockout is the right shape for a single-user gateway, but the tradeoff should be written down.Also worth folding in: RFC 8707
resourceis validated but never bound ontoTokenRecord(harmless with one resource, a latent trap with two);token_refresh_grantdoesn't checkclient_idagainst the token's record (OAuth 2.1 §4.3.1 conformance, no escalation possible); no IPv6 loopback (http://[::1]) inis_loopback_redirect_uri(RFC 8252 §7.3 interop).🤖 Generated with Claude Code