# SSRF via unrestricted URL scheme

User URL reaches a fetcher without a scheme allowlist, so file://, gopher:// or bare paths get through

_Area: Web infrastructure and server-side requests. Seen in 4 disclosed reports; one confirmed bounty: $22,300; latest 2026-09-10. Updated 2026-10-24._

## How it works
A server fetches a URL the user supplies. The code may check the host or IP, but never restricts the scheme. The URL passes validation and is handed to a downstream client — git, libcurl, reqwest — that understands far more schemes than http/https. `file://` reads local files, `gopher://` speaks arbitrary bytes to internal services, and bare strings may be opened as local paths. The broken assumption: "validating the host means the URL is safe." The scheme decides what the fetcher actually does.

## Where it shows up
- A URL validator called without an explicit scheme allowlist [E1](https://bugbountylibrary.com/r/h1%3A1685822.md).
- User URLs passed to libcurl/curl without protocol restriction, including via redirects [E2](https://bugbountylibrary.com/r/h1%3A115748.md).
- Media/URL fields fetched with no host or scheme checks, where bare strings fall through to local file open [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-wfgq-w7cq-qj7j.md).
- Fallback logic that treats any string resolving to an existing path as a file URL [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-wfgq-w7cq-qj7j.md).
- Error messages that differ between existing and missing files (existence oracle) [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-wfgq-w7cq-qj7j.md).

## How to review
Trace every user-controlled URL from input to the actual fetcher. The invariant: only `http`/`https` reach the client, enforced at the fetcher, not just the validator. Check that redirects are re-validated against the same allowlist, that bare strings are never opened as local files, and that error output does not reveal file existence. For each downstream consumer (git, curl, reqwest), confirm which schemes it honors and whether the code blocks the rest [E1](https://bugbountylibrary.com/r/h1%3A1685822.md)[E2](https://bugbountylibrary.com/r/h1%3A115748.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-wfgq-w7cq-qj7j.md).

## Variants seen
- Validator without scheme allowlist, then `git fetch` accepts `file://` to clone local repos by deterministic storage path [E1](https://bugbountylibrary.com/r/h1%3A1685822.md).
- libcurl follows gopher/tftp/sftp/dict, including through a 302 redirect to a dangerous scheme; this predates modern libcurl defaults [E2](https://bugbountylibrary.com/r/h1%3A115748.md).
- LLM media loader with no host validation (a superset case) plus bare-path fallback to local file open [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-wfgq-w7cq-qj7j.md).

## Impact and severity
Led to local file read and internal-service access, up to cloning any local repository [E1](https://bugbountylibrary.com/r/h1%3A1685822.md), SMTP spam and internal probing [E2](https://bugbountylibrary.com/r/h1%3A115748.md), and unauthenticated file read with an existence oracle [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-wfgq-w7cq-qj7j.md). Severity rises when the fetcher is unauthenticated and when the scheme gap chains into internal services.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1685822.md
- E2: https://bugbountylibrary.com/r/h1%3A115748.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-wfgq-w7cq-qj7j.md
