GitHub Advisoryadvisory · RubyGems
GHSA-2mwr-xjcg-37j7 Mechanize sends credential headers to another host after an HTTP redirect
MEDIUM
Official page ↗
- Aliases
- CVE-2026-107715
- Ecosystem
- RubyGems
- Packages
- mechanize
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N
- Weaknesses
- CWE-200, CWE-522
- Reviewed
- 2026-10-08
Affected packages
• RubyGems mechanize: < 2.14.1 → fixed in 2.14.1
Description
Summary
`mechanize` leaked credentials to the redirect target when an HTTP redirect crossed to another host. Credentials set through `Mechanize#request_headers=` leaked even when they were `Authorization`.
Details
Two defects, both in `lib/mechanize/http/agent.rb`.
1. `Mechanize#request_headers=` bypassed the redirect strip entirely. `#request_add_headers` copied `@request_headers` onto every request unconditionally, with no host check, including the request issued after a redirect. The strip in `#response_redirect` mutated only the per-request headers hash and never touched agent state. Because `request_headers=` is the documented way to set a default credential for every request, the header the code explicitly protected — `Authorization` — was the one most likely to leak.
2. The strip list omitted `Proxy-Authorization` and `Cookie2`. Only `CREDENTIAL_HEADERS = ['Authorization']` and `COOKIE_HEADERS = ['Cookie']` were removed from the per-request headers hash on a cross-host redirect.
Cookies held in `Mechanize#cookie_jar` and credentials held in `Mechanize::HTTP::AuthStore` are not affected. Both are looked up per-URI, so they never follow a redirect to a foreign host. The exposure was limited to headers the caller set by hand.
PoC
```ruby
agent = Mechanize.new
agent.request_headers = { 'Authorization' => 'Bearer secret' }
agent.get('https://example.test/redirects-to-attacker')
the request to the attacker's host carries "Authorization: Bearer secret"
```
Impact
An attacker who controls a redirect target — through an open redirect on the site being fetched, an attacker-supplied fetch URL, DNS rebinding, or MITM — captures bearer tokens and session cookies from any `mechanize` agent that sets credentials through `request_headers=` or the per-request `headers` argument. Disclosure only; no integrity or availability impact.
Patches
Fixed in `mechanize` v2.14.1.
• Credentials and cookies are withheld from a request that follows a redirect across an origin, from both header sources: the per-request `headers` argument and `Mechanize#request_headers=`.
• `CREDENTIAL_HEADERS` gains `Proxy-Authorization`; `COOKIE_HEADERS` gains `Cookie2`.
`Proxy-Authorization` is withheld here although curl does not withhold it, because `Net::HTTP` tunnels `https:` requests with `CONNECT`, so a caller-supplied `Proxy-Authorization` travels inside the tunnel to the origin server rather than to the proxy.
Headers set through `Mechanize#request_headers=` no longer reach a redirect target on another origin when they are credentials. Callers that relied on the previous behavior will see those headers withheld.
What this fix does not cover
Only the headers in `CREDENTIAL_HEADERS` and `COOKIE_HEADERS` are withheld. A caller-supplied header that carries a credential under some other name — `X-API-Key`, `X-Vault-Token`, or any bespoke token header — still follows a redirect to another origin, matching the behavior of curl's `CURLOPT_HTTPHEADER`. If you set such a header, do not enable redirect following for requests that carry it, or scope it to a single request whose destination you control.
Workarounds
Set `Mechanize#redirect_ok = false` and handle redirects explicitly, or avoid `request_headers=` for credentials and pass them per-request only to hosts you intend to authenticate to.
Credit
Reported by @SnailSploit.
Related
Alias · CVE (1)
- CVE-2026-107715 The Mechanize library is used for automating interaction with websites. vulnerability
References
- https://github.com/sparklemotion/mechanize/security/advisories/GHSA-2mwr-xjcg-37j7 web
- https://github.com/sparklemotion/mechanize/pull/676 web
- https://github.com/sparklemotion/mechanize/commit/02a1235842d6eda8d4a5a3d8f13aba2cecf52e4f web
- https://github.com/sparklemotion/mechanize/commit/94e0902867296be804f36eccbb47acf7d5018745 web
- https://github.com/sparklemotion/mechanize/commit/ac49abf2869297d83c3b11bbfb8b18e63b588c95 web
- https://github.com/sparklemotion/mechanize package
- https://github.com/sparklemotion/mechanize/releases/tag/v2.14.1 web