## Ticket: CVE-2026-25765 — Faraday SSRF via protocol-relative URL

**Advisory**: GHSA-33mh-2634-fwr2 — https://github.com/lostisland/faraday/security/advisories/GHSA-33mh-2634-fwr2
**CVE**: CVE-2026-25765 | **CWE-918** (Server-Side Request Forgery)
**Severity**: Medium — CVSS 5.8
**Package**: `faraday` (RubyGems) | **Repository**: https://github.com/lostisland/faraday

### Impact

`faraday`'s `build_exclusive_url` uses Ruby's `URI#merge` to combine the
connection's base URL with a per-request path. Per RFC 3986, a
protocol-relative URL such as `//evil.com/path` is a *network-path reference*:
when merged, its authority **overrides** the base URL's authority.

As a result, a user-controlled path passed to `get()` / `post()` can redirect
the outgoing request to an attacker-chosen host. An application that builds a
Faraday connection against a trusted base host but forwards an
attacker-influenced path string is exposed to server-side request forgery —
the request leaves the configured host entirely.

### Affected / fixed versions

Affected: `faraday` `1.0.0`–`1.10.4` and `2.0.0`–`2.14.0`.
Fixed: **`1.10.5`** and **`2.14.1`**.

Reproduce on a vulnerable build (**`2.14.0`**) and verify the fix on
**`2.14.1`**.

### Where to look

The fix ships in commit
[`a6d3a3a0bf59c2ab307d0abd91bc126aef5561bc`](https://github.com/lostisland/faraday/commit/a6d3a3a0bf59c2ab307d0abd91bc126aef5561bc).
Inspect the patch to confirm the root cause and the guard:

```bash
git clone https://github.com/lostisland/faraday.git
cd faraday && git show a6d3a3a0bf59c2ab307d0abd91bc126aef5561bc
# or: git diff v2.14.0 v2.14.1
```

The change makes `build_exclusive_url` stop letting a protocol-relative
request path override the base URL's authority.

### Reproduction approach

No external network, database, or browser is needed. A minimal local TCP/HTTP
listener bound to `127.0.0.1` stands in for the attacker host so the PoC can
observe whether the request lands there.

1. In a scratch directory, install the **vulnerable** build:
   `gem install faraday -v 2.14.0`
2. Start a local listener on `127.0.0.1:<port>`.
3. Build a Faraday connection with base URL `http://safe.local`, then issue a
   request whose path is the protocol-relative URL `//127.0.0.1:<port>/x`.
   Record which host actually received the request.
4. Repeat with the **fixed** build: `gem install faraday -v 2.14.1`.

### Expected result

| Build | request path `//127.0.0.1:<port>/x` on base `http://safe.local` | Observable |
|-------|-----------------------------------------------------------------|-----------|
| `faraday 2.14.0` (vulnerable) | protocol-relative authority overrides the base host | request lands on **`127.0.0.1:<port>`** — the local listener receives it |
| `faraday 2.14.1` (fixed) | protocol-relative path no longer overrides the authority | request stays bound to **`safe.local`** |

- **Vulnerable indicator**: the local `127.0.0.1` listener receives the
  request even though the base URL was `http://safe.local`.
- **Fixed indicator**: the request stays bound to the base host; the listener
  receives nothing.

### Expected artifacts

- `reproduction_steps.sh` — installs both versions, starts the local listener,
  and runs the PoC, printing which host received the request for each.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable and
  fixed indicators captured.
- Logs capturing the script output for `2.14.0` and `2.14.1`.
