Harden the compact index HTTP fetcher's redirect and error handling - #9812
Merged
Conversation
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
force-pushed
the
compact-index-fetcher-hardening
branch
from
August 26, 2026 08:59
2174cef to
b265de4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gem::CompactIndexClient::HTTPFetcherdrives its own redirect loop and callsGem::RemoteFetcher#requestdirectly, so it misses two protections that#fetch_httpand#fetch_pathprovide.It followed redirects from https to plain http, which
fetch_httprejects 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
SocketErrorandErrno::ECONNREFUSEDescape unwrapped, since the rescue that turns them intoGem::RemoteFetcher::FetchErrorlives in#fetch_path.Gem::Source#compact_index_versionsandGem::Resolver::APISet#versionsrescue onlyFetchErrorandCompactIndexClient::Error, so on a host where justindex.rubygems.orgis unreachable the fallback to the Marshal index never ran and the command aborted with a raw backtrace.