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
維護者通常 1 天內回覆
已經有一個關聯 PR 被合併了。
- #1304 來自 @mikolalysenko —— 已合併
評估
研究方向
檢查 crates/socket-patch-core/src/patch/redirect/mod.rs 的 6098–6445 行附近。第 6205 行的守衛在 locked 為 None 時會被繞過;加入檢查以使無鎖專案跳過釘選或追加 gems。使用 e2e_redirect_gem_build.rs 的 fixtures 驗證僅掃描 Gemfile 時 manifest 保持不變。
由索引模型根據 Issue 內容生成。
描述
[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.
- 主要語言
- Rust
- 星號
- 8
- 分支
- 0
- 平均合併
- 19 小時 21 分鐘
- 30 天內合併 PR
- 421
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
SocketDev/socket-patch 的其他 Issue
-
agent:triaged bug bughunt pm:npm priority:p3
難度 2/5 1-3 小時 新手友好度 75/100
SocketDev/socket-patch#1072 · 1 則留言 ·
維護者通常 1 天內回覆
-
Hosted gem `rollback` / `remove` strips the `DEPENDENCIES` `!` of a gem the user declared inside a `source "https://rubygems.org" do` block, so every frozen install fails after the unwind可能已有人在做 @mikolalysenko 於 1 天前認領。 未關閉agent:triaged bug bughunt pm:bundler priority:p1
難度 2/5 1-3 小時 新手友好度 80/100
SocketDev/socket-patch#1056 · 1 則留言 ·
維護者通常 1 天內回覆
-
agent:triaged bug bughunt pm:bundler priority:p1
難度 2/5 1-3 小時 新手友好度 85/100
SocketDev/socket-patch#896 · 1 則留言 ·
維護者通常 1 天內回覆
-
agent:triaged bug bughunt pm:yarn-berry priority:p1
難度 2/5 1-3 小時 新手友好度 73/100
SocketDev/socket-patch#783 · 1 則留言 ·
維護者通常 1 天內回覆
-
agent:triaged bug bughunt pm:cargo priority:p2
難度 2/5 1-3 小時 新手友好度 84/100
SocketDev/socket-patch#651 · 3 則留言 ·
維護者通常 1 天內回覆
查看 SocketDev/socket-patch 的全部 Issue
相似的 Issue
-
[Bug]: Web chat input doesn't regain focus after a reply finishes可能已有人在做 @GaijinSystems 今天認領。 未關閉
難度 2/5 1-3 小時 新手友好度 76/100
zeroclaw-labs/zeroclaw#11658 ·
維護者通常 2 天內回覆
-
good first issue help wanted
難度 2/5 1-3 小時 新手友好度 72/100
-
難度 2/5 1-3 小時 新手友好度 82/100
bytecodealliance/wasm-tools#2768 ·
維護者通常 1 天內回覆
-
documentation
難度 2/5 1-3 小時 新手友好度 70/100
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 62/100
NuSkooler/enigma-bbs#907 ·
維護者通常 1 天內回覆