fix(mcp): make SSRF-guarded fetch structurally mandatory in OAuth auth() calls#5419
Conversation
…h() calls PR #5399 fixed the callback route forgetting to pass fetchFn into the SDK's auth(), but every call site (start + callback routes) still imported the raw SDK auth() directly and had to remember to pass fetchFn: createSsrfGuardedMcpFetch() by hand — the same omission was possible again at any future call site. Add mcpAuthGuarded() in lib/mcp/oauth/auth.ts, a thin wrapper around the SDK's auth() that always defaults fetchFn to the SSRF-guarded fetch (still overridable for tests). Both routes now import mcpAuthGuarded from @/lib/mcp/oauth instead of the raw SDK auth, so omitting the guard is no longer possible by omission.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview MCP OAuth start and callback routes now call Reviewed by Cursor Bugbot for commit acb2b20. Configure here. |
Greptile SummaryThis PR introduces
Confidence Score: 5/5Safe to merge — all The change is a focused structural hardening: a one-line wrapper centralises SSRF-guard injection, both OAuth route handlers are updated, and grep confirms no remaining direct uses of the raw SDK No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Route as start/callback route
participant Wrapper as mcpAuthGuarded()
participant Guard as createSsrfGuardedMcpFetch()
participant SDK as MCP SDK auth()
participant Remote as OAuth Server
Route->>Wrapper: "mcpAuthGuarded(provider, { serverUrl, ... })"
Wrapper->>Guard: createSsrfGuardedMcpFetch()
Guard-->>Wrapper: guardedFetch
Wrapper->>SDK: "auth(provider, { fetchFn: guardedFetch, serverUrl, ... })"
SDK->>Remote: discovery / token requests (via guardedFetch)
Remote-->>SDK: OAuth response
SDK-->>Route: "AUTHORIZED | redirect"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Route as start/callback route
participant Wrapper as mcpAuthGuarded()
participant Guard as createSsrfGuardedMcpFetch()
participant SDK as MCP SDK auth()
participant Remote as OAuth Server
Route->>Wrapper: "mcpAuthGuarded(provider, { serverUrl, ... })"
Wrapper->>Guard: createSsrfGuardedMcpFetch()
Guard-->>Wrapper: guardedFetch
Wrapper->>SDK: "auth(provider, { fetchFn: guardedFetch, serverUrl, ... })"
SDK->>Remote: discovery / token requests (via guardedFetch)
Remote-->>SDK: OAuth response
SDK-->>Route: "AUTHORIZED | redirect"
Reviews (2): Last reviewed commit: "fix(mcp): make SSRF-guarded fetch struct..." | Re-trigger Greptile |
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit acb2b20. Configure here.
Summary
/cleanup+/simplifyaudit of that fix.fetchFn: createSsrfGuardedMcpFetch()into the MCP SDK'sauth(), but every call site ofauth()(startandcallbackroutes) still imports the raw SDK function and has to remember to thread the guard through by hand — the exact same class of omission remains possible at any future call site.mcpAuthGuarded()inapps/sim/lib/mcp/oauth/auth.ts: a thin wrapper around the SDK'sauth()that always defaultsfetchFnto the SSRF-guarded fetch (still overridable, e.g. in tests). Both routes now importmcpAuthGuardedfrom@/lib/mcp/oauthinstead ofauth as mcpAuthfrom the SDK directly, so the guard can no longer be omitted by forgetting a per-call-site argument.apps/sim/lib/mcp/oauth/revoke.tsandprobe.tsare untouched — they call different SDK functions (discoverOAuthServerInfo) that already threadfetchFnthrough consistently and are out of scope for this wrapper.Test plan
apps/sim/lib/mcp/oauth/auth.test.tscovers the wrapper's defaultfetchFnand override behaviorstart/route.test.tsandcallback/route.test.tsto assert the routes callmcpAuthGuarded(mocked via the shared@/lib/mcp/oauthbarrel mock) instead of mocking the raw SDKauth()+pinned-fetchper test filemcpAuthGuardedmock function to the sharedmcpOauthMockinpackages/testingbunx vitest run apps/sim/app/api/mcp/oauth apps/sim/lib/mcp/oauth— 6 files, 36 tests, all passbun run type-check(tsc --noEmit) — clean, no errorsbunx biome checkon all changed files — clean