Pulling video off the web, safely: SSRF guards and a loopback-only server
A private app still has to touch the internet for one thing: importing a video you linked to. That single outbound path takes untrusted input — so it gets the most paranoid code in the whole product. Here's how it's locked down.
Most of MediaFind never speaks to a server — that's the private-by-default story. But “bring this YouTube clip into my library” obviously has to make a request, and that request is built from a URL a person typed. Untrusted input plus an outbound fetch is the classic setup for SSRF — so this path is treated as hostile by default.
The attack: a URL that points somewhere it shouldn't
SSRF is when an attacker gets your process to make a request they control the destination of. A download box that accepts any URL is a textbook target. The dangerous destinations aren't on the public web at all — they're inside your own machine and network:
http://127.0.0.1:…/localhost— your own loopback services.http://169.254.169.254/— the link-local cloud-metadata address (credentials, on a server).10.x,192.168.x,172.16–31.x— private LAN hosts: routers, NAS, internal dashboards.
And a naïve blocklist on the hostname doesn't help, because of DNS rebinding: a domain can look perfectly public and still resolve to 127.0.0.1. You can't judge a URL safe by its spelling — you have to judge it by the IP it actually connects to.
The guard: resolve-then-check up front, then re-check at connect time
So the check lives where it can't be fooled: by the resolved IP, not the spelling of the host. The first line of defense runs up front, at parse time — the moment a URL is accepted, its hostname is resolved to a real address and that address is validated. If it's loopback, link-local, or private, the URL is rejected before any fetch is even attempted. Then, because a hostname that resolved to a public IP at parse time could resolve to 127.0.0.1 a moment later (DNS rebinding, a TOCTOU race), the same check runs again when the socket is about to connect: the connection re-resolves the host, pins the validated IP, and refuses it before a single byte is sent if it's now internal. Parse-time validation is the wall; connect-time re-resolution is the second wall behind it that closes the rebinding gap.
Two more details round out the fetch path:
- Hostname-verified TLS. The HTTPS handler verifies the certificate against the hostname. (A subtle bug once disabled this on a newer Python, silently breaking the scrape fallback — fixed, and a reminder that security checks need tests, not just code.)
- Two fetchers, same guard. Known sites go through yt-dlp; everything else falls back to an HTML scrape that looks for a real video URL. Both sit behind the same resolution guard, so the fallback can't be the soft underbelly.
The other front door: the local web UI
MediaFind's interface is a small local web app. That's a lovely way to build a Mac app — and also a thing you must not accidentally expose to the network. Two measures keep it private:
- Loopback-only binding. The server listens on
127.0.0.1, not0.0.0.0. It's reachable from your machine and nowhere else — another device on the same Wi-Fi can't connect to it. A non-loopback bind is gated behind a required auth token.
State-changing requests are also CSRF-protected, so a random web page you visit can't quietly POST commands to your local server in the background.
The hardening, in one table
| Surface | Control | Stops |
|---|---|---|
| Download URL | Resolved-IP guard (parse-time + connect-time) | SSRF, DNS-rebinding to internal hosts |
| HTTPS fetch | Hostname-verified TLS | Man-in-the-middle on the import path |
| Scrape fallback | Same guard as yt-dlp | A weaker second code path |
| Local server | Binds to 127.0.0.1 only | Other devices reaching your UI |
| State changes | CSRF token | Cross-site command injection |
The principle
Privacy and security are the same discipline seen from two sides. The privacy story is about the data that never leaves; the security story is about the one path that does reach out, and making sure it can only go where you intended. Untrusted input gets treated as hostile, the dangerous destinations are blocked at the layer that can't be spoofed, and the local UI stays strictly local. Paste a link with confidence — the paranoia is built in.
Import from the web, without inviting it in
The one outbound path is the most hardened code in the app. Everything else stays on your Mac.
Download for macOS