You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Hosted gem redirect ignores Bundler 4's custom lockfile (lockfile setting / BUNDLE_LOCKFILE), so it never pins the lock Bundler uses and frozen installs fail with no warning #749
[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
Bundler 4.0 added custom lockfiles: bundle config set lockfile <path> (BUNDLE_LOCKFILE: in .bundle/config), the BUNDLE_LOCKFILE env var, bundle install --lockfile, and a lockfile "<path>" Gemfile DSL method (rubygems #9059, #9111, #9146). socket-patch has no reference to any of these. The gem manifest/lock pair is always Gemfile+Gemfile.lock or gems.rb+gems.locked (LoadedManifest::pair, crates/socket-patch-core/src/formats/gem/manifest.rs:76).
So when a project sets lockfile custom.lock, scan --mode hosted:
rewrites only the Gemfile and never pins the CHECKSUMS-era lock Bundler actually reads;
emits no warning: neither redirect_gem_frozen_install nor anything else. It exits 0 with status: success, and the in-run --vex attests not_affected;
when a leftover Gemfile.lock also exists, rewrites that lock, which Bundler ignores.
The next BUNDLE_FROZEN=true or deployment bundle install then fails with exit 16 ("Run bundle install elsewhere and add the updated custom.lock to version control"). The same project installs frozen without trouble before the scan, and a default-lock project installs frozen without trouble after it.
Impact
On Bundler 4, the converged CHECKSUMS rewrite is what makes a hosted redirect frozen-installable. With a custom lockfile, production/CI installs (frozen or deployment) break after a scan that reported success with no caveat. An unfrozen install does end up patched, because the Gemfile source block wins. Post-install vex also can't find the hosted pin, because it only reads Gemfile.lock/gems.locked (BUNDLER_LOCKS, vex/discover/gem.rs:135). Without a leftover Gemfile.lock it fails with "nothing to attest" (exit 2), even though the install is patched.
Repro (Linux, Ruby 3.3.6, Bundler 4.0.17; patch API and registry mocked on loopback, real rubygems.org upstream)
Expected: docs/ecosystems.md (RubyGems row) and CLI_CONTRACT.md describe hosted gem mode as editing the manifest/lock pair Bundler loads. A converged CHECKSUMS lock is frozen-installable "as written — no caveat", and a pair that frozen mode would reject gets redirect_gem_frozen_install. A lock Bundler is configured to use should be the one that gets pinned. If that isn't supported, it should be refused in the same way redirect_gem_bundle_gemfile_unsupported refuses a foreign BUNDLE_GEMFILE.
Actual: the configured lock is invisible. The scan reports success with no warning, and the next frozen install fails.
Matrix
OS
Ruby
Bundler
Lock selector
Leftover Gemfile.lock
Scan warnings
Lock pinned
Frozen install after scan
Unfrozen install
Linux
3.3.6
4.0.17
.bundle/configlockfile
no
none
none (Gemfile only)
exit 16 (control 0)
patched
Linux
3.3.6
4.0.17
.bundle/configlockfile
yes
none
Gemfile.lock (ignored by Bundler)
exit 16
patched
Linux
3.3.6
4.0.17
env BUNDLE_LOCKFILE
yes
none
Gemfile.lock (ignored)
exit 16
patched
Linux
3.3.6
4.0.17
Gemfile lockfile "custom.lock" DSL
no
none
none
exit 16 (Bundler 4.0.17 also fails frozen here before the scan, so this is Bundler's own limit)
patched
Linux
3.3.6
4.0.17
default Gemfile.lock (control)
n/a
none
Gemfile.lock
0, patched
Linux
3.3.6
≤ 2.7
n/a (feature introduced in Bundler 4.0.0)
The config path is OS-independent, so no macOS/Windows probe was run. Vendored mode (vendor), which also hard-codes Gemfile.lock, is untested with a custom lock.
Suspect code
crates/socket-patch-core/src/formats/gem/manifest.rs:76 (LoadedManifest::pair): the lock is derived only from the manifest name. Bundler 4's SharedHelpers.default_lockfile checks BUNDLE_LOCKFILE first, and CLI promotes options[:lockfile] || ENV["BUNDLE_LOCKFILE"] || Bundler.settings[:lockfile].
crates/socket-patch-core/src/vex/discover/gem.rs:135: the BUNDLER_LOCKS loop.
[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
Bundler 4.0 added custom lockfiles:
bundle config set lockfile <path>(BUNDLE_LOCKFILE:in.bundle/config), theBUNDLE_LOCKFILEenv var,bundle install --lockfile, and alockfile "<path>"Gemfile DSL method (rubygems #9059, #9111, #9146). socket-patch has no reference to any of these. The gem manifest/lock pair is alwaysGemfile+Gemfile.lockorgems.rb+gems.locked(LoadedManifest::pair,crates/socket-patch-core/src/formats/gem/manifest.rs:76).So when a project sets
lockfile custom.lock,scan --mode hosted:Gemfileand never pins the CHECKSUMS-era lock Bundler actually reads;redirect_gem_frozen_installnor anything else. It exits 0 withstatus: success, and the in-run--vexattestsnot_affected;Gemfile.lockalso exists, rewrites that lock, which Bundler ignores.The next
BUNDLE_FROZEN=trueor deploymentbundle installthen fails with exit 16 ("Runbundle installelsewhere and add the updated custom.lock to version control"). The same project installs frozen without trouble before the scan, and a default-lock project installs frozen without trouble after it.Impact
On Bundler 4, the converged CHECKSUMS rewrite is what makes a hosted redirect frozen-installable. With a custom lockfile, production/CI installs (frozen or deployment) break after a scan that reported success with no caveat. An unfrozen install does end up patched, because the
Gemfilesource block wins. Post-installvexalso can't find the hosted pin, because it only readsGemfile.lock/gems.locked(BUNDLER_LOCKS,vex/discover/gem.rs:135). Without a leftoverGemfile.lockit fails with "nothing to attest" (exit 2), even though the install is patched.Repro (Linux, Ruby 3.3.6, Bundler 4.0.17; patch API and registry mocked on loopback, real rubygems.org upstream)
Expected vs actual
redirect_gem_frozen_install. A lock Bundler is configured to use should be the one that gets pinned. If that isn't supported, it should be refused in the same wayredirect_gem_bundle_gemfile_unsupportedrefuses a foreignBUNDLE_GEMFILE.Matrix
Gemfile.lock.bundle/configlockfile.bundle/configlockfileGemfile.lock(ignored by Bundler)BUNDLE_LOCKFILEGemfile.lock(ignored)lockfile "custom.lock"DSLGemfile.lock(control)Gemfile.lockThe config path is OS-independent, so no macOS/Windows probe was run. Vendored mode (
vendor), which also hard-codesGemfile.lock, is untested with a custom lock.Suspect code
crates/socket-patch-core/src/formats/gem/manifest.rs:76(LoadedManifest::pair): the lock is derived only from the manifest name. Bundler 4'sSharedHelpers.default_lockfilechecksBUNDLE_LOCKFILEfirst, andCLIpromotesoptions[:lockfile] || ENV["BUNDLE_LOCKFILE"] || Bundler.settings[:lockfile].crates/socket-patch-core/src/vex/discover/gem.rs:135: theBUNDLER_LOCKSloop.BUNDLE_GEMFILE/BUNDLE_CACHE_PATHfrom.bundle/configand env could readBUNDLE_LOCKFILEthe same way. This is related to the reader-side pair issue Lock inventory reads only Gemfile.lock, so a gems.rb project's gems.locked is invisible and a stale Gemfile.lock is read instead #736, but that one is aboutgems.lockedvsGemfile.lock, not a configured lock.