Hosted gem scan of a Gemfile with no lock still pins shared-gem-home versions: `gem "x", "~> 2.0"` becomes the older patched `"1.0.0"`, and a gem the project never declared is appended as a new dependency
Maintainers usually reply within 1 day
A pull request for this has already been merged.
- #1304 by @mikolalysenko — merged
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 75/100
Research direction
Examine crates/socket-patch-core/src/patch/redirect/mod.rs near lines 6098–6445. The guard at line 6205 is bypassed when locked is None; add a check so lockless projects skip pinning or appending gems. Validate with the e2e_redirect_gem_build.rs fixtures that a Gemfile-only scan leaves the manifest unchanged.
Written by the indexing model from the issue text.
Description
[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
#1060 (the fix for #1055) skips a crawled gem version that the project's lock doesn't resolve (redirect_gem_version_not_locked). The check only runs when a lock exists, though: let locked = files.get(lock_name).map(...) is None for a project with a Gemfile / gems.rb and no Gemfile.lock / gems.locked, and if let Some(specs) = &locked then skips the guard completely. Library repos usually don't commit their lock, so this is the normal state of a fresh clone before bundle install.
In that state, a hosted scan on a machine whose shared gem home holds another project's copy of a patched gem does what #1055 described, and adds one more failure:
- The constraint is overwritten.
gem "vuln-gem", "~> 2.0"is rewritten tosource "<patch registry>" do gem "vuln-gem", "1.0.0" end. The nextbundle installinstalls 1.0.0, where the untouched Gemfile installs 2.0.0. - An unrelated gem is added. A Gemfile that never mentions
vuln-gem(gem "tiny-dep"only) gets a newsource … do gem "vuln-gem", "1.0.0" endblock appended through the "genuinely undeclared (a transitive dep)" branch. The nextbundle installaddsvuln-gem (= 1.0.0)!to the project's dependencies. Without the scan the project installs onlytiny-dep.
Both runs exit 0 and report redirected: 1 / action: "pinned". The only warning is redirect_gem_stale_install about the shared-home copy, which doesn't say that the project's declared dependencies changed. rollback / remove can't undo it either: a Gemfile-only pin isn't a reference without a lock (CLI_CONTRACT.md "Lockless pins"), so the user has to edit the Gemfile by hand.
Impact
- The project's dependencies change silently: it downgrades below the user's constraint, or it picks up a gem it never used.
- With
--vex, the scan exits 1 because of the stale warning, but plainscan --mode hosted/ CI runs exit 0 and leave the rewritten Gemfile for the user to commit.
Repro (real Bundler; hermetic mock upstream + patch registry, the e2e_redirect_gem_build.rs fixtures)
# upstream mock serves tiny-dep 1.0.0 and vuln-gem 1.0.0 + 2.0.0; the patch registry serves patched vuln-gem 1.0.0
export GEM_HOME=$TMP/shared-home GEM_PATH=$TMP/shared-home:<system gem dir>
# project A (another project on the same machine) puts vuln-gem 1.0.0 into the shared home
(cd a && printf 'source "<upstream>"\ngem "vuln-gem", "1.0.0"\n' > Gemfile && bundle install)
# project B: fresh clone, Gemfile only, no lock
cd b && printf 'source "<upstream>"\n\ngem "vuln-gem", "~> 2.0"\n' > Gemfile
socket-patch scan --mode hosted --json --yes --api-url <mock> --org test-org --api-token fake
# exit 0, redirect.patches = [{purl: pkg:gem/[email protected], action: pinned}]
cat Gemfile
# source "<upstream>"
# source "<patch registry>/" do
# gem "vuln-gem", "1.0.0"
# end
bundle install # → Gemfile.lock: vuln-gem (1.0.0) / DEPENDENCIES vuln-gem (= 1.0.0)!
# control: the same Gemfile without the scan → "Installing vuln-gem 2.0.0"
# project C: Gemfile declares only `gem "tiny-dep"`, no lock
socket-patch scan --mode hosted … # exit 0; appends `source "<patch registry>" do gem "vuln-gem", "1.0.0" end`
bundle install # DEPENDENCIES gains `vuln-gem (= 1.0.0)!`; the control installs only tiny-dep
Expected vs actual
- Expected (CLI_CONTRACT.md,
redirect_gem_version_not_locked): hosted mode "re-points the version the lock resolves; it never picks a version", and "the user's declared constraint and the locked version are never overwritten". With no lock there's nothing resolved to re-point. The scan should skip these gems with a warning (the way cargo refuses a lockless project withredirect_cargo_lockless_dependents), or at least never append a gem the manifest doesn't declare and never replace a declared requirement that the crawled version doesn't satisfy. - Actual: the crawled shared-home version is pinned as an exact top-level requirement in both shapes, and the scan reports success.
Matrix
| OS | Ruby | Bundler | Shape | Result |
|---|---|---|---|---|
| Linux | 3.3.6 | 2.5.22 | lockless ~> 2.0 declaration |
fails (downgrade to 1.0.0) |
| Linux | 3.3.6 | 2.5.22 | lockless, gem not declared | fails (gem appended and installed) |
| Linux | 3.3.6 | 4.0.18 | both shapes | fails (same, CHECKSUMS lock written by the install) |
| Linux | 3.3.6 | 4.0.18 | same shapes with a lock (the #1055 control) | pass (redirect_gem_version_not_locked, repo e2e suite) |
| macOS / Windows | — | — | — | untested; the logic is OS-independent |
Main b96a785, which includes #1060. Both shapes were reproduced twice, on two Bundler versions. I didn't bisect: the lockless path is outside the #1060 change, so this predates it.
Suspect code
crates/socket-patch-core/src/patch/redirect/mod.rs:6098:lockedisNonewithout a lock.crates/socket-patch-core/src/patch/redirect/mod.rs:6205:if let Some(specs) = &locked, so the #1055 guard is skipped when there's no lock.crates/socket-patch-core/src/patch/redirect/mod.rs:6387(in-place rewrite to the exact version) and:6445(the "genuinely undeclared (a transitive dep): append a block" branch, which without a lock can't tell a transitive dep from an unrelated shared-home gem).
No probe runs: found and confirmed on Linux only.
- Dominant language
- Rust
- Stars
- 8
- Forks
- 1
- Avg merge
- 19h 1m
- Merged PRs (30d)
- 473
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from SocketDev/socket-patch
-
agent:triaged bug pm:go priority:p2
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
SocketDev/socket-patch#1409 · 1 comment ·
Maintainers usually reply within 1 day
-
agent:triaged bug bughunt pm:npm priority:p3 ux
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
SocketDev/socket-patch#1072 · 2 comments ·
Maintainers usually reply within 1 day
-
agent:triaged bug bughunt pm:yarn-berry priority:p1
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
SocketDev/socket-patch#783 · 1 comment ·
Maintainers usually reply within 1 day
-
Vendored cargo warns cargo_multi_version_old_cargo ("declares no rust-version") when the root package inherits rust-version from [workspace.package]Possibly taken A pull request linked to this issue is open or already merged. Openagent:triaged bug bughunt pm:cargo priority:p2
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
SocketDev/socket-patch#651 · 3 comments ·
Maintainers usually reply within 1 day
-
agent:triaged bench perf-regression pm:nuget priority:p3
Difficulty 4/5 3-5 days Newbie friendliness 45/100
SocketDev/socket-patch#1425 · 1 comment ·
Maintainers usually reply within 1 day
All issues in SocketDev/socket-patch
Similar issues
-
ai-priority/P2 bug db/oracle user-priority/P1
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
swc-project/swc#12516 ·
Maintainers usually reply within 1 day
-
app bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
mls-rs-crypto-awslc: exact aws-lc-rs =1.16.3 pin blocks rustls security updatePossibly taken @muscariello claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 65/100