Skip to content

feat(web_fetch): proxy support with SSRF-safe HTTP CONNECT / SOCKS5 tunnelling#3581

Merged
esengine merged 2 commits into
main-v2from
fix/web-fetch-proxy-rebased
Jun 8, 2026
Merged

feat(web_fetch): proxy support with SSRF-safe HTTP CONNECT / SOCKS5 tunnelling#3581
esengine merged 2 commits into
main-v2from
fix/web-fetch-proxy-rebased

Conversation

@esengine

@esengine esengine commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Rebased @ningqipeng's #3357 onto current main-v2 (resolving the boot/workspace conflicts from the bash-timeout changes) and fixed the SOCKS5 branch.

What it does

Wires web_fetch to the [network] proxy config while preserving the SSRF guard:

  • HTTP/HTTPS proxy → dial proxy, CONNECT with the original hostname (proxy resolves DNS — needed behind the GFW). IP-literal targets stay SSRF-checked.
  • SOCKS5/5h → tunnel via the proxy; IP-literal targets SSRF-checked, hostnames resolved by the proxy.
  • No proxy → unchanged dial-time SSRF guard (resolve locally, block internal IPs, dial the vetted IP to defeat rebinding).

Fix on top of the original

The original SOCKS branch only set Transport.Proxy, so the proxy itself was dialed through the SSRF-checking directDialContext — a SOCKS proxy on a private/LAN address (common; anything but loopback) was refused by the guard. This routes SOCKS through golang.org/x/net/proxy with a plain dialer to the trusted proxy and keeps the IP-literal target check in a wrapping DialContext, mirroring the HTTP CONNECT path. Added tests for both the preserved block and the proxy-not-blocked case.

Testing

  • go test ./internal/tool/builtin ./internal/boot ./internal/netclient green
  • New: TestSSRFBlocksPrivateTargetThroughSOCKS5, TestSOCKS5ProxyOnPrivateAddressNotSSRFBlocked

Closes #3357

Original work by @ningqipeng.

ningqipeng and others added 2 commits June 8, 2026 05:10
…unnelling

web_fetch intentionally bypasses netclient proxy settings (source comment
in netclient.go). This means users behind corporate firewalls or GFW
cannot use web_fetch to reach external sites (GitHub, npm, etc.), even
when they have a working proxy configured in [network].

Wire web_fetch to the existing [network] proxy configuration while
preserving full SSRF protection:

1. netclient.ResolveProxyURL — new exported function that resolves
   ProxySpec to URL string (reuses existing customProxyURL logic)
2. webFetch.proxyURL — new field set during tool registration
3. ssrfGuardedClient(proxyURL) — enhanced to tunnel through proxy:
   - HTTP/HTTPS proxy: dial proxy -> CONNECT with ORIGINAL hostname
     (proxy resolves DNS remotely, essential for GFW users) -> tunnel
   - IP literal targets still get SSRF-checked via net.ParseIP
   - SOCKS5/SOCKS5h: Transport.Proxy (Go standard library handles it)
   - No proxy: existing direct SSRF-safe behavior unchanged
4. ConfineWebFetch + boot.go plumbing for both workspace and fallback
   paths

| File | Change |
|------|--------|
| internal/netclient/netclient.go | +ResolveProxyURL() |
| internal/tool/builtin/webfetch.go | webFetch.proxyURL, ssrfGuardedClient proxy tunnel |
| internal/tool/builtin/workspace.go | Workspace.ProxyURL |
| internal/tool/builtin/confine.go | +ConfineWebFetch() |
| internal/boot/boot.go | addBuiltins proxyURL param + fallback fix |
| internal/boot/boot_test.go | test signature update |
| internal/tool/builtin/web_fetch_proxy_test.go | new proxy tests |

- go build ./... compiles
- go test ./internal/tool/builtin/ PASS
- go test ./internal/boot/ PASS
- SSRF still blocks 169.254.169.254, 10.0.0.1, 192.168.1.1 through proxy
The SOCKS branch only set Transport.Proxy, so the proxy itself was dialed
through the SSRF-checking directDialContext — a SOCKS proxy on a private/LAN
address (anything but loopback) was refused by the guard. Route SOCKS through
golang.org/x/net/proxy with a plain dialer to the trusted proxy, and keep the
IP-literal target SSRF check in a wrapping DialContext, mirroring the HTTP
CONNECT path. Adds tests for both the preserved block and the proxy-not-blocked
case.
@esengine esengine requested a review from SivanCola as a code owner June 8, 2026 12:15
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development skills Skill system (internal/skill, internal/tool) config Configuration & setup (internal/config) labels Jun 8, 2026
@esengine esengine merged commit ba094b9 into main-v2 Jun 8, 2026
10 checks passed
@esengine esengine deleted the fix/web-fetch-proxy-rebased branch June 8, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config Configuration & setup (internal/config) skills Skill system (internal/skill, internal/tool) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant