Skip to content

uploadproxy: never connect a client to loopback or link-local addresses - #374

Merged
neamul602 merged 1 commit into
devfrom
fix/373-proxy-ssrf
Nov 2, 2026
Merged

neamul602 merged 1 commit into
devfrom
fix/373-proxy-ssrf

Conversation

@neamul602

Copy link
Copy Markdown
Contributor

Follow-up to #373, whose SonarCloud gate failed on S5144 (server-side request forgery) in Proxy.connect: the proxy dials whatever host a client asks for.

A forward proxy has to do that, and this one only listens on loopback. But it runs as root, so a local client could use it to reach things it couldn't reach itself: services behind root-only firewall rules, or a link-local cloud metadata service (169.254.169.254).

Change

  • A net.Dialer.Control hook (guardDial) checks the address actually dialled, after DNS. Names that resolve to these addresses, and DNS rebinding, are therefore covered too.
  • It refuses loopback, link-local, unspecified and multicast addresses, for tunnels (CONNECT gets 502) and inspected requests alike.
  • Private ranges stay allowed, because intranet upload destinations are legitimate.
  • Options.AllowLocalTargets turns it off for tests only; their test sites run on 127.0.0.1.

Test plan

  • An inspected request to a loopback site gets 502 with nothing delivered. A tunnel to localhost by name is refused. Address table: 127.0.0.1, ::1, 169.254.169.254, 0.0.0.0, fe80::1 and 224.0.0.1 are refused; 10.x, 192.168.x and public IPv4/IPv6 are allowed.
  • golangci-lint: 0 issues (linux, darwin, windows). gosec: 0. Tests pass on macOS and Linux.
  • Whether SonarCloud's taint analysis recognises the hook as the fix shows on this PR's run.

Refs #344

🤖 Generated with Claude Code

SonarCloud (S5144, SSRF) flagged that the proxy dials whatever a client
asks for. A forward proxy must, but this one runs as root: a local client
could use it to reach what it couldn't reach itself (root-only firewall
rules, a link-local metadata service). A dialer Control hook now checks
the address actually dialled (after DNS, so names and rebinding are
covered) and refuses loopback, link-local, unspecified and multicast
addresses, for tunnels and inspected requests alike. Private ranges stay
allowed: intranet upload destinations are legitimate. Tests opt in to
local targets (their sites run on 127.0.0.1).

Refs #344

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3993efed-f057-4cf6-ab0a-8d9482e54b27

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@sonarqubecloud

sonarqubecloud Bot commented Nov 2, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@neamul602
neamul602 merged commit 425094a into dev Nov 2, 2026
22 of 23 checks passed
@neamul602
neamul602 deleted the fix/373-proxy-ssrf branch November 2, 2026 11:05
neamul602 pushed a commit that referenced this pull request Nov 2, 2026
Both came up on #379 (dev -> main).

- CodeQL go/allocation-size-overflow (high), scanworker writeMsg: the
  frame buffer was sized 4+len(body) with no bound when sending, although
  the comment said frames were capped. writeMsg now refuses a frame over
  the protocol's largest limit (9 MB) before allocating.
- SonarCloud S5144 (SSRF) in Proxy.connect: tunnels dialled the client's
  CONNECT target string. The connect-time guard (#374) already refused
  local addresses, but out of SonarCloud's sight. vetTarget now checks the
  port (1-65535), resolves the host itself, keeps only allowed addresses
  (allowedIP, shared with guardDial) and returns an IP literal, which is
  what gets dialled. guardDial still checks the dialled address.

Refs #344 #328

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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.

1 participant