Screenshots egress through a SOCKS5 vetting proxy, so CDN-styled sites render styled - #1259
Merged
Merged
Conversation
…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
force-pushed
the
worktree-github-screenshots
branch
from
July 31, 2026 10:36
ef89575 to
3ee05ee
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.SocksProxyin 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.1so nothing can resolve outside the proxy. The proxy resolves and vets every connection viaVutuv.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).EvidenceScreenshotstays unproxied on purpose (shoots our own, possibly internal, host).Verified end to end: real
capture_framed/2of 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 theSocksProxymoduledoc anddocs/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.