Hacktoberfest 2026: die Issues, die Maintainer für den Oktober markiert haben – offen und einsteigerfreundlich. Hacktoberfest-Issues durchsuchen

Gem hosted `rollback` / `remove` turn a redirected transitive gem into a top-level exact pin, because the restore looks for a blank line the rewriter never writes

Offen
#457 3 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Maintainer antworten meist innerhalb von 1 Tag

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Anfängerfreundlichkeit
78/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
ruby, rust
Bereich
cli, testing

Rechercherichtung

Read crates/socket-patch-core/src/patch/redirect/mod.rs:5070 and crates/socket-patch-core/src/patch/redirect/upstream/gem.rs:480,691, then run provably_transitive_needs_a_blank_line_before_the_block. Reproduce rollback and remove with an LF-terminated Gemfile and CHECKSUMS lock; done means both restore the Gemfile and lock byte-identically without creating a direct dependency, while existing blank-line behavior remains covered.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

agent:claimed agent:triaged bug bughunt pm:bundler priority:p1

[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).

Summary

When scan --mode hosted redirects a transitive gem, it appends a source "<patch registry>" do … end block to the end of the Gemfile. When the Gemfile already ends in a newline (the normal case), it writes no blank separator line. The upstream restore that rollback and remove share only treats a block as "the rewriter's append for a transitive gem" (Decl::Transitive) when a blank line precedes the block (provably_appended). Because the rewriter's own append never has one, the restore falls through to Decl::Direct. The transitive gem comes back as a new top-level declaration, gem "<name>", "<version>", and the lock gains a DEPENDENCIES entry <name> (= <version>).

This only affects converged locks, which means locks with a CHECKSUMS section (Bundler 2.6+ with checksums enabled, the Bundler 4 default). CHECKSUMS-less locks take the mixed-state path, which reads DEPENDENCIES and gets this right.

Impact

  • After rollback / remove, the project is not back on its upstream registry state, which is what CLI_CONTRACT.md's "Hosted unwind coverage" promises. It has a new direct dependency pinned to exactly the version that had the vulnerability.
  • That pin freezes the vulnerable version. In the repro, bundle update rack keeps rack 3.2.1 after the rollback, while the byte-identical control upgrades to rack 3.2.7. A user who removes the hosted patch to take the upstream fix can't get it until they find and delete a declaration they never wrote.
  • It's silent: status: success, reverted: [pkg:gem/[email protected]], no warning.

Repro (Linux, Ruby 3.3.6, Bundler 4.0.17; real rubygems.org upstream, local mock for the patch API and the patch-registry compact index)

# Project: rack 3.2.1 is transitive via rackup; the Gemfile ends with a declaration line + "\n".
printf 'source "https://rubygems.org"\n\ngem "rackup", "2.2.1"\n' > Gemfile
bundle config set --local path vendor/bundle
bundle install                       # Bundler 4 writes CHECKSUMS
cp Gemfile Gemfile.pristine; cp Gemfile.lock lock.pristine; rm -rf vendor
socket-patch scan --mode hosted --json --yes --api-url $API --org test-org --api-token fake
#   -> redirected 1, rewrittenFiles [Gemfile, Gemfile.lock]; block appended right after `gem "rackup"` (no blank line)
socket-patch rollback --json --yes --patch-server-url $API --api-url $API --org test-org --api-token fake
#   -> status success, hosted.reverted [pkg:gem/[email protected]]
diff Gemfile.pristine Gemfile
#   3a4
#   > gem "rack", "3.2.1"
diff lock.pristine Gemfile.lock
#   12a13
#   >   rack (= 3.2.1)
bundle update rack && grep '    rack (' Gemfile.lock   # rack (3.2.1): stuck on the vulnerable version

Control: the same flow with the Gemfile ending in \n\n (one trailing blank line) restores byte-identically (Gemfile and lock), and bundle update rack moves to 3.2.7.
socket-patch remove pkg:gem/[email protected] gives the same result as rollback.

Expected vs actual

  • Expected (CLI_CONTRACT.md, "Hosted unwind coverage", gem bullet): "the spec moves back into the upstream GEM section …, the source "<patch registry>" do … end block is undone … and the DEPENDENCIES pin loses its !". For a gem the rewriter appended because it was transitive, the code's own intent (Decl::Transitive: "Gone: the block was the rewriter's append for a transitive gem") is that the block and the DEPENDENCIES entry both go away. The documented "comes back as the exact pin" caveat covers a gem that had a declaration, not one the user never declared.
  • Actual: a new gem "rack", "3.2.1" line and a rack (= 3.2.1) DEPENDENCIES entry.

Matrix

OS Ruby Bundler Lock Result
Linux 3.3.6 4.0.17 CHECKSUMS (default) reproduces (rollback twice, remove once)
Linux 3.3.6 2.6.9 bundle lock --add-checksums reproduces
Linux 3.3.6 4.0.17 CHECKSUMS, Gemfile ends with a blank line pass (byte-identical restore)
Linux 3.3.6 2.6.9 no CHECKSUMS (mixed state) n/a: the lock isn't converged, and rollback can't see a Gemfile-only pin (documented)

The logic is plain text processing with no OS dependency, so macOS and Windows should behave the same. I didn't bisect: the upstream restore is new in v5 (#277), and main 2463257 is the first commit that has it.

Suspect code

  • crates/socket-patch-core/src/patch/redirect/mod.rs:5070: the transitive append is let sep = if gf.ends_with('\n') { "" } else { "\n" };, which leaves no blank line before the block.
  • crates/socket-patch-core/src/patch/redirect/upstream/gem.rs:480 (provably_appended) and :691: the restore requires a blank line before the block to choose Decl::Transitive. The unit test provably_transitive_needs_a_blank_line_before_the_block builds its "appended" fixture as gem "puma"\n\n{block}, a shape the rewriter never produces on an LF-terminated Gemfile.

One possible fix: have the rewriter's append write a blank separator, so its output is provably distinguishable from an in-place rewrite (which swallows the preceding blank lines). Hosted Gemfiles written by earlier runs would still need a fallback.

Vorherrschende Sprache
Rust
Sterne
8
Forks
0
Ø Merge
19 Std. 56 Min.
Gemergte PRs (30 T.)
51

Entwicklungsumgebung

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus SocketDev/socket-patch

Alle Issues in SocketDev/socket-patch

Ähnliche Issues

Weitere Issues zu Rust

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.