Skip to content

Harden the compact index HTTP fetcher's redirect and error handling - #9812

Merged
hsbt merged 3 commits into
masterfrom
compact-index-fetcher-hardening
Aug 26, 2026
Merged

Harden the compact index HTTP fetcher's redirect and error handling#9812
hsbt merged 3 commits into
masterfrom
compact-index-fetcher-hardening

Conversation

@hsbt

@hsbt hsbt commented Aug 26, 2026

Copy link
Copy Markdown
Member

Gem::CompactIndexClient::HTTPFetcher drives its own redirect loop and calls Gem::RemoteFetcher#request directly, so it misses two protections that #fetch_http and #fetch_path provide.

It followed redirects from https to plain http, which fetch_http rejects as a non-https downgrade. A MITM answering with a crafted 302 could serve the version index that drives resolution over cleartext.

It also let SocketError and Errno::ECONNREFUSED escape unwrapped, since the rescue that turns them into Gem::RemoteFetcher::FetchError lives in #fetch_path. Gem::Source#compact_index_versions and Gem::Resolver::APISet#versions rescue only FetchError and CompactIndexClient::Error, so on a host where just index.rubygems.org is unreachable the fallback to the Marshal index never ran and the command aborted with a raw backtrace.

hsbt and others added 3 commits August 26, 2026 17:58
Gem::RemoteFetcher#fetch_http has refused a redirect from https to plain
http since 2012, but the compact index path, which is now the default
and decides which versions resolve, followed it. One 302 from a
compromised edge or a misconfigured mirror moved the whole index onto a
channel anyone on the path can rewrite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gem::RemoteFetcher#request lets SocketError, Errno::ECONNREFUSED and
their kin through unwrapped, and every caller here rescues FetchError
alone. On a host where only index.rubygems.org is unreachable, the
fallback to the Marshal index never ran and multi-source resolution
aborted on the one source instead of recording it and moving on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dropping Gem::Net::HTTPNotModified from it, or narrowing HTTPSuccess to
HTTPOK, left the suite green even though Updater depends on both the
304 and the 206 reaching it untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hsbt
hsbt force-pushed the compact-index-fetcher-hardening branch from 2174cef to b265de4 Compare August 26, 2026 08:59
@hsbt
hsbt merged commit cb853ad into master Aug 26, 2026
107 checks passed
@hsbt
hsbt deleted the compact-index-fetcher-hardening branch August 26, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant