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
Gem hosted and vendored rewrites delete a second gem declaration that shares the patched gem's line after ;, so the next bundle install drops that dependency #826
[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
When the patched gem shares a Gemfile line with another declaration, separated by ;, both gem rewriters replace the whole physical line with the patched gem's new declaration. Everything after the ; is silently deleted:
gem"colorize","0.8.1";gem"rainbow","3.1.1"
Hosted (scan --mode hosted) rewrites it to source "<patch-registry>" do / gem "colorize", "0.8.1" / end, and gem "rainbow" is gone.
Vendored (vendor) rewrites it to gem "colorize", "0.8.1", path: ".socket/vendor/…", and again gem "rainbow" is gone.
The lock still lists rainbow under DEPENDENCIES. A frozen install fails with exit 16 ("You have deleted from the Gemfile: * rainbow"). The unfrozen install, which hosted mode prescribes for CHECKSUMS-less locks (redirect_gem_frozen_install), succeeds and removes rainbow from the bundle, after which require "rainbow" raises LoadError. The scan itself exits 0 with status: success and redirected: 1, and gives no warning.
Hosted rollback doesn't recover it: it restores gem "colorize", "0.8.1" and nothing else. Vendored vendor --revert should restore the ledger's verbatim original line; I haven't verified that.
Impact
A dependency that has nothing to do with the patch disappears from the project. Under frozen CI that's a red build. Otherwise it's a runtime LoadError, or a silently missing gem that is only required lazily. ;-joined declarations are unusual but valid Ruby, and Bundler accepts them.
A related shape: with options before the ; (gem "colorize", "0.8.1", require: false; gem "rainbow", "3.1.1"), gem_line_trailing_options returns the whole rest verbatim (require: false; gem "rainbow", "3.1.1"). Hosted mode then moves gem "rainbow"into the Socket source … do block. A frozen install exits 16; on Bundler 4.0.17 the unfrozen install still resolves rainbow from rubygems.org, but it is now source-pinned (rainbow (= 3.1.1)!).
Repro (hosted, real rubygems.org upstream, mocked patch API + patch registry on loopback)
Vendored (I used a scratch copy of e2e_vendor_gem_build.rs's capstone whose only change is the fixture Gemfile line gem "rack", "~> 3.1"; gem "colorize", "0.8.1"):
Gemfile after `vendor --offline`:
source "https://rubygems.org"
gem "rack", "3.2.7", path: ".socket/vendor/gem/<uuid>/rack-3.2.7"
Gemfile.lock still has DEPENDENCIES colorize (= 0.8.1)
fresh-checkout frozen `bundle install`: "You have deleted from the Gemfile: * colorize (= 0.8.1)"
Expected vs actual
Expected: a declaration that the rewriter can't move intact is refused before any write, with redirect_gem_unrecognized_declaration (hosted) or gemfile_declaration_not_editable (vendored). That's the same fail-closed posture the vendored rest_blocks_edit takes for continuations and if/unless modifiers, and that Hosted gem redirect breaks a multi-line gem declaration (the Gemfile stops parsing) and drops a trailing if/unless modifier #340 / PR Fix hosted gem redirect breaking multi-line and conditional gem lines (#340) #637 add to hosted. The alternative is to rewrite only the declaration's own statement and keep the rest of the line. CLI_CONTRACT.md and docs/ecosystems.md describe the hosted gem redirect as moving the one declaration into a per-dep source block. Nothing documents that other declarations on the same line can be removed.
Actual: another gem's declaration is deleted, with exit 0, status: success and no warning.
Matrix (Linux, Ruby 3.3.6; the rewrite is pure string handling, so no OS dependence is expected)
Mode
Bundler
Manifest
Result
hosted
4.0.17 (CHECKSUMS lock)
Gemfile
fail (×3): sibling deleted; frozen exit 16, unfrozen drops it
hosted
4.0.17
gems.rb
fail
hosted
2.6.9 (no CHECKSUMS)
Gemfile
fail
hosted
2.4.22 (no CHECKSUMS)
Gemfile
fail: the prescribed unfrozen install drops it; LoadError
vendored
4.0.17
Gemfile
fail
vendored
2.4.22
Gemfile
fail
hosted, ; declaration first (gem "rainbow"…; gem "colorize"…), 4.0.17
pass: refused with redirect_gem_unrecognized_declaration
still fails (the new gem_line_tail_blocks_edit refuses if but not ;)
First bad
Not bisected. Present on main 045d7ec.
Suspect code
crates/socket-patch-core/src/patch/redirect/mod.rs:5164gem_line_trailing_options: for a tail of , "0.8.1"; gem "rainbow", "3.1.1", it consumes the quoted version, then finds no , before ; and returns "", so the replacement keeps nothing past the version. When an option precedes the ;, it returns the remainder verbatim, including the second statement.
Hosted call site: crates/socket-patch-core/src/patch/redirect/mod.rs:5706. The whole matched line is replaced.
Vendored: crates/socket-patch-core/src/vendor/gem.rs:1532rest_blocks_edit has no ; check, and :1421 builds new_line from the same helper.
[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
When the patched gem shares a Gemfile line with another declaration, separated by
;, both gem rewriters replace the whole physical line with the patched gem's new declaration. Everything after the;is silently deleted:scan --mode hosted) rewrites it tosource "<patch-registry>" do/gem "colorize", "0.8.1"/end, andgem "rainbow"is gone.vendor) rewrites it togem "colorize", "0.8.1", path: ".socket/vendor/…", and againgem "rainbow"is gone.The lock still lists
rainbowunderDEPENDENCIES. A frozen install fails with exit 16 ("You have deleted from the Gemfile: * rainbow"). The unfrozen install, which hosted mode prescribes for CHECKSUMS-less locks (redirect_gem_frozen_install), succeeds and removesrainbowfrom the bundle, after whichrequire "rainbow"raisesLoadError. The scan itself exits 0 withstatus: successandredirected: 1, and gives no warning.Hosted
rollbackdoesn't recover it: it restoresgem "colorize", "0.8.1"and nothing else. Vendoredvendor --revertshould restore the ledger's verbatim original line; I haven't verified that.Impact
A dependency that has nothing to do with the patch disappears from the project. Under frozen CI that's a red build. Otherwise it's a runtime
LoadError, or a silently missing gem that is only required lazily.;-joined declarations are unusual but valid Ruby, and Bundler accepts them.A related shape: with options before the
;(gem "colorize", "0.8.1", require: false; gem "rainbow", "3.1.1"),gem_line_trailing_optionsreturns the whole rest verbatim (require: false; gem "rainbow", "3.1.1"). Hosted mode then movesgem "rainbow"into the Socketsource … doblock. A frozen install exits 16; on Bundler 4.0.17 the unfrozen install still resolves rainbow from rubygems.org, but it is now source-pinned (rainbow (= 3.1.1)!).Repro (hosted, real rubygems.org upstream, mocked patch API + patch registry on loopback)
Vendored (I used a scratch copy of
e2e_vendor_gem_build.rs's capstone whose only change is the fixture Gemfile linegem "rack", "~> 3.1"; gem "colorize", "0.8.1"):Expected vs actual
redirect_gem_unrecognized_declaration(hosted) orgemfile_declaration_not_editable(vendored). That's the same fail-closed posture the vendoredrest_blocks_edittakes for continuations andif/unlessmodifiers, and that Hosted gem redirect breaks a multi-linegemdeclaration (the Gemfile stops parsing) and drops a trailingif/unlessmodifier #340 / PR Fix hosted gem redirect breaking multi-line and conditional gem lines (#340) #637 add to hosted. The alternative is to rewrite only the declaration's own statement and keep the rest of the line. CLI_CONTRACT.md and docs/ecosystems.md describe the hosted gem redirect as moving the one declaration into a per-depsourceblock. Nothing documents that other declarations on the same line can be removed.status: successand no warning.Matrix (Linux, Ruby 3.3.6; the rewrite is pure string handling, so no OS dependence is expected)
Gemfilegems.rbGemfileGemfileLoadErrorGemfileGemfile;declaration first (gem "rainbow"…; gem "colorize"…), 4.0.17redirect_gem_unrecognized_declaration464896d, 4.0.17gem_line_tail_blocks_editrefusesifbut not;)First bad
Not bisected. Present on main
045d7ec.Suspect code
crates/socket-patch-core/src/patch/redirect/mod.rs:5164gem_line_trailing_options: for a tail of, "0.8.1"; gem "rainbow", "3.1.1", it consumes the quoted version, then finds no,before;and returns"", so the replacement keeps nothing past the version. When an option precedes the;, it returns the remainder verbatim, including the second statement.crates/socket-patch-core/src/patch/redirect/mod.rs:5706. The whole matched line is replaced.crates/socket-patch-core/src/vendor/gem.rs:1532rest_blocks_edithas no;check, and:1421buildsnew_linefrom the same helper.gem_line_tail_blocks_edittokenizes the tail but treats;as an ordinary character. A top-level;outside quotes should probably refuse there (and inrest_blocks_edit).No probe runs: Linux only, because the defect is OS-independent string handling. Related: #340 (same single-line tail recognizer, different trigger).