Skip to content

fix(eve): SSRF-guard outbound fetches through a safe-fetch wrapper#662

Open
dnukumamras wants to merge 3 commits into
mainfrom
dnukumamras/ssrf-controls-design
Open

fix(eve): SSRF-guard outbound fetches through a safe-fetch wrapper#662
dnukumamras wants to merge 3 commits into
mainfrom
dnukumamras/ssrf-controls-design

Conversation

@dnukumamras

@dnukumamras dnukumamras commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What

Adds SSRF hardening and controls to eve's outbound HTTP. Requests whose host comes from author, tenant, or model input could otherwise be steered at an internal-only service, a cloud metadata endpoint (169.254.169.254), or a host that resolves into a private range — and several of these requests carry the connection's credentials.

This routes those requests through a new #shared/safe-fetch.js wrapper (zero new runtime deps — node:dns + node:net + global fetch, reusing the existing network-address.ts IP blocklist).

Every hop, safeFetch / assertUrlSafeToFetch:

  • pins https (plain http only on loopback, or with allowHttp for the web-fetch tool),
  • DNS-resolves the host and rejects any private / link-local / carrier-grade-NAT / reserved address (incl. cloud metadata), catching hostnames that resolve private, not just IP literals (including IPv4 tunneled through IPv4-compatible / mapped / SIIT / 6to4 / NAT64 IPv6 forms),
  • follows redirects manually and re-validates each hop, dropping Authorization/Cookie on cross-origin redirects,
  • bounds the request with a timeout and a response-size cap that stay in force across the caller's body read.

Guarded call sites

  • OpenAPI spec fetch + credentialed operation calls
  • web_fetch tool (model-supplied URL)
  • remote-agent (subagent) dispatch
  • OIDC discovery + a jwks_uri preflight (the jwks_uri is read from the fetched discovery doc)
  • MCP connections (URL preflight; @ai-sdk/mcp owns the socket)
  • session callbacks (DNS check at the async POST site)

Plus CI guard rule 36: bare fetch( is forbidden in the network-facing directories (runtime/connections/, execution/, runtime/governance/auth/) — it must go through the wrapper.

Behavior changes (pre-1.0)

  • Session callbacks and non-loopback OpenAPI/callback URLs now require https.
  • OpenAPI spec and operation response bodies are capped (10 MB default).

Not included / deferred

  • Connect-time IP pinning (DNS-rebinding TOCTOU): needs a custom undici dispatcher = a runtime dep, which conflicts with eve's nitro-only rule. The resolve-then-check preflight blocks the common cases; the sub-second rebind window is a documented residual.
  • allowLoopback production-hardening switch is present but not yet wired to a deploy-time setting.
  • Request-header sanitization and opaque errors.

Test plan

  • packages/eve/src/shared/safe-fetch.test.ts (new): reserved-IP + DNS-resolves-private rejection, loopback toggle, protocol pin, redirect revalidation, cross-origin credential drop, timeout (incl. across the body read), size cap.
  • network-address.ts unit tests for the IPv6-tunneled IPv4 encodings.
  • Migrated call-site tests updated for the new call convention; pnpm typecheck, pnpm guard:invariants, pnpm docs:check, and unit tests pass.

Route eve's user/tenant/model-influenced outbound requests through a new
#shared/safe-fetch.js wrapper. It pins https (plain http only on loopback),
DNS-resolves the host and rejects private/link-local/reserved ranges (incl.
cloud metadata), re-validates every redirect hop while dropping credentials
cross-origin, and bounds time + body size. Migrates the OpenAPI spec and
operation calls, the web_fetch tool, remote-agent dispatch, OIDC discovery
(plus a jwks_uri preflight), MCP connections, and session callbacks, and adds
guard rule 36 forbidding bare fetch() in the network-facing directories.

Session callbacks and non-loopback OpenAPI/callback URLs now require https.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: dnukumamras <mukund.sarma@vercel.com>
@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
eve-docs Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 8:10pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
eve-docs-1644 Skipped Skipped Open in v0 Jul 9, 2026 8:10pm
eve-docs-4759 Skipped Skipped Open in v0 Jul 9, 2026 8:10pm

Comment thread packages/eve/src/shared/safe-fetch.ts Outdated
…rols-design

Signed-off-by: dnukumamras <mukund.sarma@vercel.com>

# Conflicts:
#	packages/eve/src/runtime/connections/openapi-client.ts
#	scripts/guard-invariants.mjs
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Bundle + Package Summary: apps/fixtures/weather-agent

Key takeaways

  • No notable deltas vs main (f00f084).

Delta vs main (f00f084)

Area Metric Baseline Current Delta
Package Packed tarball 3.59 MB 3.60 MB +6.0 kB ⚠️
Package Unpacked publish size 13.03 MB 13.05 MB +13.6 kB ⚠️
Package Installed footprint 52.13 MB 52.14 MB +13.6 kB ⚠️
Package Published files 2504 2506 +2
Package Installed files 5735 5737 +2
Runtime Unique function payloads 2 2 0
Runtime Total function bytes 8.96 MB 8.97 MB +12.3 kB ⚠️
Runtime Public routes 9 9 0
Changed function payloads vs main (f00f084) (2)
Function Status Baseline Current Delta Route changes
functions/__server.func changed 3.32 MB 3.32 MB +6.2 kB ⚠️ none
functions/.well-known/workflow/v1/flow.func changed 5.65 MB 5.65 MB +6.1 kB ⚠️ none

eve init install

Metric Baseline Current Delta
Installed footprint 90.39 MB 90.41 MB +13.6 kB ⚠️
Installed packages 122 122 0
dependencies 4 4 0
devDependencies 2 2 0
Dependency package bytes 27.21 MB 27.23 MB +13.6 kB ⚠️
devDependency package bytes 5.04 MB 5.04 MB 0 B ➖
Build Metadata
  • Preset: vercel
  • Nitro: nitro@3.0.260610-beta
  • Output directory: apps/fixtures/weather-agent/.vercel/output
  • Build metadata timestamp: 2026-07-09T20:12:05.431Z
  • Route aliases: 9 public, 1 internal (10 total aliases)
  • Vercel routes in config: 10
  • Severity legend: 🔴 dominant/large, 🟠 notable, 🟡 watch, ⚪ small
Package Drill-Down

Package Details

  • Package: eve@0.22.3
  • Package directory: packages/eve
  • Tarball: 3.60 MB (eve-0.22.3.tgz)
  • Unpacked payload: 13.05 MB across 2506 published files
  • Installed footprint: 52.14 MB across 5737 installed files
  • Installed root package: 11.77 MB
  • Installed dependencies: 40.37 MB
  • Runtime dependencies: 1
  • Peer dependencies: 5 (4 optional)

Installed footprint is measured from an isolated temporary npm install of the packed tarball.

Heavy installed dependencies

  • @rolldown/binding-linux-x64-gnu: 19.13 MB (36.7%)
  • eve: 11.77 MB (22.6%)
  • ai: 6.36 MB (12.2%)
  • zod: 5.04 MB (9.7%)
  • nitro: 2.41 MB (4.6%)
Publish payload breakdown
Published file size
🟠 dist/src/compiled/experimental-ai-sdk-code-mo... [####....................] 1.51 MB 11.5%
🟡 dist/src/compiled/@vercel/sandbox/index.js       [##......................] 632.2 kB 4.8%
🟡 dist/src/compiled/_chunks/workflow/undici-fb5... [#.......................] 502.4 kB 3.9%
🟡 dist/src/compiled/@chat-adapter/slack/index.js   [#.......................] 433.8 kB 3.3%
🟡 dist/src/compiled/_chunks/workflow/dist-DJU_7... [#.......................] 276.2 kB 2.1%
🔴 Other published files                            [########################] 9.70 MB 74.3%
Installed footprint breakdown
Installed package size
🔴 @rolldown/binding-linux-x64-gnu [########################] 19.13 MB 36.7%
🔴 eve                             [###############.........] 11.77 MB 22.6%
🔴 ai                              [########................] 6.36 MB 12.2%
🔴 zod                             [######..................] 5.04 MB 9.7%
🟠 nitro                           [###.....................] 2.41 MB 4.6%
🟡 rolldown                        [#.......................] 785.9 kB 1.5%
🔴 Other installed packages        [########................] 6.65 MB 12.7%
Runtime dependencies (1)
Package Range Notes
nitro 3.0.260610-beta
Peer dependencies (5)
Package Range Notes
@opentelemetry/api ^1.0.0 optional peer
ai catalog:
braintrust ^3.0.0 optional peer
just-bash ^3.0.0 optional peer
microsandbox ^0.5.0 optional peer
eve init install drill-down

eve init install details

  • Command: eve init my-agent
  • Package manager: npm
  • Installed footprint: 90.41 MB across 7555 installed files
  • Installed packages: 122 total (116 transitive-only)
  • dependencies: 4 direct packages totaling 27.23 MB
  • devDependencies: 2 direct packages totaling 5.04 MB
  • Other transitive package files: 58.14 MB

Installed footprint is measured from an isolated temporary eve init my-agent using the current packed eve tarball.

Heavy installed dependencies

  • @typescript/typescript-linux-x64: 27.95 MB (30.9%)
  • @rolldown/binding-linux-x64-gnu: 19.13 MB (21.2%)
  • eve: 11.77 MB (13.0%)
  • zod: 9.00 MB (10.0%)
  • ai: 6.36 MB (7.0%)
Installed footprint breakdown
Installed package size
🔴 @typescript/typescript-linux-x64 [########################] 27.95 MB 30.9%
🔴 @rolldown/binding-linux-x64-gnu  [################........] 19.13 MB 21.2%
🔴 eve                              [##########..............] 11.77 MB 13.0%
🔴 zod                              [########................] 9.00 MB 10.0%
🔴 ai                               [#####...................] 6.36 MB 7.0%
🟠 @types/node                      [##......................] 2.54 MB 2.8%
🔴 Other installed packages         [############............] 13.66 MB 15.1%
dependencies (4)
Package Range Installed size Share
@vercel/connect 0.2.2 98.6 kB 0.1%
ai ^7.0.0 6.36 MB 7.0%
eve file:eve-0.22.3.tgz 11.77 MB 13.0%
zod 4.4.3 9.00 MB 10.0%
devDependencies (2)
Package Range Installed size Share
@types/node 24.x 2.54 MB 2.8%
typescript 7.0.2 2.50 MB 2.8%
Function Drill-Down

Payload Size Graph

Unique function payload size and share of total
🔴 functions/.well-known/workflow/v1/flow.func     [########################] 5.65 MB 63.0%
🟠 functions/__server.func                         [##############..........] 3.32 MB 37.0%

Top Function Payloads

🔴 functions/.well-known/workflow/v1/flow.func • 1 public route • 5.65 MB
Metric Value
Public routes /.well-known/workflow/v1/flow
Runtime nodejs24.x
Handler index.mjs
Payload 5.65 MB
Function files 5.65 MB across 31 files
Traced dependencies 0 B
Signal 🔴 Bundled file __eve_nitro_handler__.mjs is 2.83 MB (50.1%)

🔴 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🔴 __eve_nitro_handler__.mjs       [########################] 2.83 MB 50.1%
🟡 _chunks/sandbox.mjs             [######..................] 766.3 kB 13.6%
🟡 _libs/@ai-sdk/gateway+[...].mjs [####....................] 413.6 kB 7.3%
🟡 _chunks/dist-DJU_7R9s.mjs       [###.....................] 335.6 kB 5.9%
🟡 _libs/ai.mjs                    [##......................] 270.1 kB 4.8%
🟠 Other bundled files             [#########...............] 1.03 MB 18.3%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x",
  "environment": {
    "NODE_OPTIONS": "--experimental-require-module"
  },
  "maxDuration": "max",
  "experimentalTriggers": [
    {
      "type": "queue/v2beta",
      "topic": "__eve776561746865722d6167656e74_wkf_workflow_*",
      "consumer": "default",
      "retryAfterSeconds": 5,
      "initialDelaySeconds": 0
    }
  ]
}

🟠 functions/__server.func • 8 public routes, 1 internal alias • 3.32 MB
Metric Value
Public routes /
/eve/v1/callback/[token]
/eve/v1/connections/[name]/callback/[token]
/eve/v1/health
/eve/v1/info
/eve/v1/session
/eve/v1/session/[sessionId]
/eve/v1/session/[sessionId]/stream
Internal aliases /__server
Runtime nodejs24.x
Handler index.mjs
Payload 3.32 MB
Function files 3.32 MB across 21 files
Traced dependencies 0 B
Signal 🟠 Bundled file _chunks/dev-runtime-artifacts.mjs is 947.3 kB (28.5%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 _chunks/dev-runtime-artifacts.mjs [########################] 947.3 kB 28.5%
🟠 index.mjs                         [####################....] 781.2 kB 23.5%
🟠 _chunks/sandbox.mjs               [###################.....] 766.3 kB 23.1%
🟡 _chunks/dist-DJU_7R9s.mjs         [#########...............] 335.6 kB 10.1%
⚪ _chunks/resume-hook-DjPY9VGg.mjs  [###.....................] 119.2 kB 3.6%
🟡 Other bundled files               [#########...............] 373.3 kB 11.2%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x"
}

Comment thread packages/eve/src/shared/network-address.ts
@caleb-vercel

Copy link
Copy Markdown
Contributor

I think you'll need someone from the core Eve team to approve

Address PR review:
- safeFetch tied its timeout timer and abort listener to header arrival, so the
  caller's later readBodyTextSafe was neither bounded by timeoutMs nor
  cancellable by the caller signal. Hand cleanup off to the returned body's
  lifetime (unref'd timer for the never-read case) so both keep applying through
  the body read.
- normalizeAddress only unwrapped dotted `::ffff:` IPv4, so private/metadata
  IPv4 written as IPv4-compatible (`[::169.254.169.254]`), 6to4, NAT64, or
  SIIT-translated IPv6 slipped past the reserved-range blocklist. Parse the IPv6
  to bytes and extract the embedded IPv4 for every such form, preserving `::`
  and `::1`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: dnukumamras <mukund.sarma@vercel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants