Skip to content

Screenshots egress through a SOCKS5 vetting proxy, so CDN-styled sites render styled - #1259

Merged
wintermeyer merged 1 commit into
mainfrom
worktree-github-screenshots
Jul 31, 2026
Merged

Screenshots egress through a SOCKS5 vetting proxy, so CDN-styled sites render styled#1259
wintermeyer merged 1 commit into
mainfrom
worktree-github-screenshots

Conversation

@wintermeyer

Copy link
Copy Markdown
Owner

TL;DR GitHub link cards screenshotted as bare unstyled HTML because the SSRF pin (MAP * <ip>) sent every subresource host to the seed's IP; a loopback SOCKS5 vetting proxy replaces the pin, keeping the SSRF guarantees per connection while CSS/JS load from their real CDN hosts.

Why: The v7.141.2 egress control (--host-resolver-rules=MAP * <vetted-ip>, GHSA-mmjf-8cwc-6vwv) pinned every hostname Chromium resolves to the seed's IP. Sites serving assets from a CDN domain (GitHub: github.githubassets.com) failed the TLS handshake on a certificate mismatch and rendered as 1995-style bare HTML.

How: New Vutuv.Ssrf.SocksProxy in the supervision tree. Chromium runs with --proxy-server=socks5://127.0.0.1:<port> (socks5:// = remote DNS, each hostname rides in CONNECT) plus --host-resolver-rules=MAP * ~NOTFOUND,EXCLUDE 127.0.0.1 so nothing can resolve outside the proxy. The proxy resolves and vets every connection via Vutuv.Ssrf (hostnames and IP literals) and dials exactly the vetted IP — no second lookup, DNS-rebinding TOCTOU stays closed. A missing proxy aborts the capture (fail closed). EvidenceScreenshot stays unproxied on purpose (shoots our own, possibly internal, host).

Verified end to end: real capture_framed/2 of a GitHub issue page now renders fully styled through the proxy; http://127.0.0.1:4000/ is still refused before Chromium launches. Full rationale in the SocksProxy moduledoc and docs/architecture/images.md.

Deploy: cold (new supervision-tree child). Version: 7.206.4.

An AI agent wrote this text in my name, unreviewed by me. The work behind it is mine; I only delegated the writing.

…s render styled [cold-deploy]

The SSRF egress control from v7.141.2 pinned every hostname Chromium
resolves to the seed's vetted IP (--host-resolver-rules=MAP * <ip>).
That was safe but sent every subresource host to the same address, and
most large sites serve their CSS/JS from a separate CDN domain: GitHub
loads all styling from github.githubassets.com, those fetches died on a
certificate mismatch, and every GitHub link card screenshotted as bare
unstyled HTML (any site with an asset CDN degraded the same way).

The pin is replaced by Vutuv.Ssrf.SocksProxy, a loopback SOCKS5 proxy in
the supervision tree that all capture-Chromium egress now runs through.
Chromium treats socks5:// as remote-DNS and sends each hostname inside
CONNECT, so the proxy can resolve and vet every single connection (seed
page, subresources, redirect/meta-refresh/JS-navigation targets, and IP
literals, which the resolver rule could not reliably cover) via
Vutuv.Ssrf right before dialling, and it dials exactly the IP it vetted:
no second lookup, so the DNS-rebinding TOCTOU stays closed while
cross-host assets load from their real, vetted addresses. A companion
"MAP * ~NOTFOUND" rule makes any name resolution outside the proxy fail,
and the capture aborts entirely when the proxy is down - every degraded
path fails closed. EvidenceScreenshot deliberately stays unproxied: it
shoots this installation's own, possibly internal, host.

Cold deploy: new supervision-tree child (Vutuv.Ssrf.SocksProxy).

An AI agent wrote this text in my name, unreviewed by me. The work
behind it is mine; I only delegated the writing.
@wintermeyer
wintermeyer force-pushed the worktree-github-screenshots branch from ef89575 to 3ee05ee Compare July 31, 2026 10:36
@wintermeyer
wintermeyer merged commit ff22edc into main Jul 31, 2026
1 check passed
@wintermeyer
wintermeyer deleted the worktree-github-screenshots branch July 31, 2026 10:42
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