Repository navigation
Description
[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
scan --mode hosted (and get --mode hosted) rewrites the gem declaration of a CRLF Gemfile (a Windows checkout, or core.autocrlf=true) into a Socket source "<index>" do … end block whose lines all end in a bare LF. The lines around it keep CRLF, so the committed Gemfile ends up with mixed line endings.
This is the hosted-mode twin of #1821, which was the same defect in vendored mode and was fixed by #1822 (plan_gemfile_edit now takes the ending of the line it replaces). The hosted planner in patch/redirect/mod.rs didn't get that change. The same thing was fixed for npm's .npmrc append in #1196.
Impact
Low. Bundler parses the mixed file, the converging bundle install and frozen installs pass, a re-scan is a no-op, and rollback / remove restore the user's lines with CRLF. But every hosted redirect on a Windows/autocrlf checkout leaves a Gemfile with mixed endings in the diff (git warns about it, and .gitattributes eol=crlf / editor normalization then churns the file), which vendored mode no longer does after #1822.
Repro
With a real Bundler (2.6.9 or 4.0.23) and the hermetic mock from crates/socket-patch-cli/tests/e2e_redirect_gem_build.rs (redirect_scanned_project(…, Driver::ScanVex)):
- Use the fixture Gemfile with CRLF endings:
source "<upstream>"\r\n\r\ngem "vuln-gem"\r\nand itsbundle installed lock, with no.socket/. - Run
socket-patch scan --mode hosted --json --yes --cwd <proj> --api-url <mock> --org <org> --api-token fake. - Read the Gemfile back.
Actual (exit 0, gem redirected):
"source \"http://127.0.0.1:43679/upstream\"\r\nsource \"http://127.0.0.1:43679/patch-registry/gem/<token>/<uuid>/\" do\n gem \"vuln-gem\", \"1.0.0\"\nend\n"
With a user source … do block (Driver::ScanVexSourceBlock):
"source \"…/upstream\"\r\n\r\nsource \"…/upstream\" do\r\nsource \"…/patch-registry/gem/<token>/<uuid>/\" do\n gem \"vuln-gem\", \"1.0.0\"\nend\nend\r\n"
The Gemfile.lock keeps CRLF throughout in both cases (CRLF lock in) and stays LF (LF lock in), so only the Gemfile is affected.
Scratch test I used (appended to e2e_redirect_gem_build.rs, not committed): take a fixture from redirect_scanned_project, restore fx.pristine_lock, delete .socket/, write fx.pristine_gemfile with \n → \r\n, run run_hosted_scan, and assert that gemfile.matches("\r\n").count() == gemfile.matches('\n').count(). The assertion fails.
Expected vs actual
- Expected: the rewritten Gemfile keeps the file's line endings, as vendored mode now does (Vendored gem mode writes the rewired
gemline of a CRLF Gemfile with an LF ending, leaving the Gemfile with mixed line endings #1821 / Fix vendored Gemfile line endings on CRLF (#1821) #1822: "New and appended lines now take the line ending of the line they replace (or the file's)"), and as the hosted planner already does for Cargo manifests (redirect/mod.rs~L1784: "A file whose every line ends in CRLF … is planned as LF and written back … as CRLF"). - Actual: the block's 3 lines (and the trailing newline on the single-line arm) end in LF inside an otherwise CRLF file.
Matrix (Linux, Ruby 3.3.6, main b291caa)
| Cell | Bundler 2.6.9 | Bundler 4.0.23 |
|---|---|---|
| Single-line decl, CRLF Gemfile + LF lock: Gemfile mixed | reproduces | reproduces (×2) |
| Single-line decl, CRLF Gemfile + CRLF lock: Gemfile mixed | reproduces | reproduces (×2) |
User source … do block, CRLF Gemfile: Gemfile mixed |
reproduces | reproduces (×2) |
| Lock endings preserved | pass | pass |
Converging bundle install, same-grant re-scan no-op |
pass | pass |
rollback / remove restore the user lines in CRLF |
pass | pass |
macOS / Windows weren't probed, but the code path is OS-independent string building.
Suspect code
crates/socket-patch-core/src/patch/redirect/mod.rs:8076-8085: the in-place block literal"source \"{}\" do\n gem \"{}\", \"{}\"\nend"is spliced in withgf.replace_range(range, &block).crates/socket-patch-core/src/patch/redirect/mod.rs:8135-8150: the append arm (a transitive gem) computeseolfor the separator but still writes the block with\nand a trailing\n(I found this by reading the code, and didn't run a separate repro for it).- For comparison, the vendored fix is
crates/socket-patch-core/src/vendor/gem.rsplan_gemfile_edit(line_endings::terminator, Fix vendored Gemfile line endings on CRLF (#1821) #1822).
Note: the ledger had earlier listed this as cosmetic. It's filed now because the vendored equivalent (#1821) was accepted and fixed.
Activity
mikolalysenko commentedon Oct 29, 2026
[agent] Claiming this issue (shared root cause: hosted gem redirect builds the Socket
source … doblock with hard-coded LF endings). Branch: agent/fix-hosted-gemfile-crlf-block. Claim-ID: 2026-10-29T16:30:43Z-0855acGenerated by Claude Code
mikolalysenko commentedon Oct 29, 2026
[agent] Draft PR: #1841
Generated by Claude Code
Keep CRLF Gemfiles CRLF on hosted gem redirect