Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

Gem `vendor --dry-run` eject previews `eject_planned` (exit 0) for a hosted gem declared in a `group` block, but the real `vendor` refuses `gemfile_declaration_not_editable` (exit 1)

Fermée
#1,761 2 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

Une pull request liée a déjà été fusionnée.

  • #1762 par @mikolalysenko — fusionnée

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
18/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
À l'abandon
Stack technique
ruby, rust
Domaine
cli

Piste de recherche

Start in crates/socket-patch-cli/src/commands/vendor.rs at the run_eject preview loop (around line 1840), which calls takeover_dry_refusal, and compare it with the per-purl path that calls gem_takeover_refusal before the restore (around line 3307). The regression test is the Driver::ScanVexGroupBlock case in e2e_redirect_gem_build.rs. Done means vendor --dry-run reports a failed event with gemfile_declaration_not_editable and exits 1, matching the wet run. A linked pull request (#1762) is already open, so check it before starting.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

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

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

Summary

In a hosted Bundler project whose patched gem is declared inside a group … do block, socket-patch vendor --dry-run (the hosted-pins eject) reports the gem as applied with eject_planned and exits 0. The real vendor then refuses the same gem with gemfile_declaration_not_editable, rolls back (eject_rolled_back), and exits 1. scan --mode vendored --dry-run and get <uuid> --mode vendored --dry-run on the same project correctly preview the refusal (#775). Only the plain vendor eject preview misses it.

Impact

CI or a user that gates on vendor --dry-run is told the eject will succeed, but it can never succeed for this project. The wet run is fail-safe: the project stays hosted and the files are byte-identical. So this is a wrong preview, not data loss.

Expected vs actual

  • Expected (CLI_CONTRACT.md, "Eject a hosted project (v5.0)", step 2): "Backend refusals are also previewed against the restored text in memory … (3) --dry-run stops here". The gem declaration refusal is a backend refusal, so the dry run should report a failed event with gemfile_declaration_not_editable and error.code: "eject_refused" (exit 1), as the wet run's outcome does. scan/get --mode vendored already preview it this way.
  • Actual: the dry run returns status: success, one applied event with errorCode: "eject_planned", and exit 0. The wet run returns partialFailure with gemfile_declaration_not_editable plus the eject_rolled_back warning, and exit 1.

Repro

Use the e2e_redirect_gem_build.rs fixture Driver::ScanVexGroupBlock (real gem build, wiremock patch API with mount_vendored_grant). The project's Gemfile looks like this after the hosted scan:

source "https://rubygems.org"
group :default do
  source "<patch-registry>/gem/<org>/<uuid>/" do
    gem "vuln-gem", "1.0.0"
  end
end

The lock is converged, either through CHECKSUMS (Bundler 2.6+) or through a real unfrozen bundle install on a CHECKSUMS-less lock. Then run:

socket-patch vendor --dry-run --json --yes --api-url $API --org org --api-token fake --patch-server-url $API
# exit 0, events: [{action: applied, errorCode: eject_planned}]
socket-patch vendor --json --yes --api-url $API --org org --api-token fake --patch-server-url $API
# exit 1, events: [{action: failed, errorCode: gemfile_declaration_not_editable}], warning eject_rolled_back
socket-patch scan --mode vendored --dry-run --json ...
# previews gemfile_declaration_not_editable (correct)

A minimal scratch test, appended to e2e_redirect_gem_build.rs, reproduced this on each run, using the helpers already in that file:

let fx = redirect_scanned_project("g", Spelling::Gemfile, false, true, None, Driver::ScanVexGroupBlock).await.unwrap();
mount_vendored_grant(&fx).await;
let (conv, install) = fresh_checkout_bundle_install(&fx); // converge the lock
assert!(install.status.success());
// copy Gemfile + Gemfile.lock + .bundle to a new dir, then run `vendor --dry-run` and `vendor` there

Matrix (Linux, Ruby 3.3.6)

Bundler Lock shape vendor --dry-run vendor scan --mode vendored --dry-run
4.0.18 CHECKSUMS (rubygems.org upstream via SOCKET_RUBYGEMS_URL) eject_planned, exit 0 ❌ refused, exit 1, rolled back —
4.0.18 converged by an unfrozen bundle install eject_planned, exit 0 ❌ refused, exit 1, rolled back refusal previewed ✅
2.5.22 converged by an unfrozen bundle install eject_planned, exit 0 ❌ refused, exit 1, rolled back refusal previewed ✅

The logic doesn't depend on the OS, so macOS and Windows weren't probed. For comparison, a gems.rb twin is correctly refused in both the dry run and the wet run (gemfile_not_loaded), and a top-level declaration ejects, installs frozen with the patched bytes, and vendor --reverts cleanly.

First bad commit

It isn't new in #1648. The parent commit 8244b94 behaves the same way, but its contract said the dry run stops before any backend preview. #1648 (b787454) added the "backend refusals are also previewed" promise and the takeover_dry_refusal preview loop, and gems fall through that loop.

Suspect code

  • crates/socket-patch-cli/src/commands/vendor.rs:1840 (the run_eject preview loop) calls takeover_dry_refusal → dispatch_vendor_one with dry_run = true. The gem backend's dry run doesn't evaluate the Gemfile declaration gate, so the outcome isn't Refused. A debug print showed reverted=["Gemfile","Gemfile.lock"], all staged, and an outcome that isn't Refused.
  • The per-purl takeover handles this case by calling gem_takeover_refusal (manifest gate plus gem_vendor_target_preflight) before the restore (vendor.rs:3307), and scan/get use gem_takeover_preview_refusals. run_eject never calls gem_takeover_refusals_for on its pins.

No probe runs: the logic is OS-independent.

Langage dominant
Rust
Étoiles
8
Forks
2
Merge moyen
19 h 19 min
PR mergées (30 j)
697

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de SocketDev/socket-patch

Toutes les issues de SocketDev/socket-patch

Issues similaires

Plus d'issues Rust

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.