Fix hosted gem redirect breaking multi-line and conditional gem lines (#340) - #637
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Hosted scan rewrote a `gem` declaration that continued on the next line, leaving the continuation orphaned after the new source block so bundler refused the Gemfile. It also dropped `if`/`unless` modifiers, declaring the gem unconditionally. Both now skip the redirect with a redirect_gem_unrecognized_declaration warning and leave the Gemfile untouched. The check is shared with vendored mode, which also now catches continuations after `=>`, a key, `\` or an open bracket, and modifiers separated by tabs. Fixes #340 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Burn-down agent: Ready for review at
Generated by Claude Code |
|
Codex follow-up review: correction pushed as The remaining interpolated-heredoc case is fixed. The shared hosted/vendor guard now refuses a possible Validation: 267 core tests and four native CLI tests passed, with five Ruby 3.4.10 / Bundler 4.0.15 fixture runs. Native cases cover colon-adjacent modifiers, plain and interpolated heredocs, and the original multiline/conditional declarations; they require a named refusal, unchanged files, zero redirects/VEX statements, and a successful native install. The actual-rewriter and native interpolation regressions both failed on the exact author head before this correction. Targeted Clippy passes with the existing macOS Ready to merge as-is from this review. 482 successful checks and 7 skipped; 12 workflows succeeded and one label-triggered Benchmarks workflow was skipped. Bugbot is clear on this commit; no unresolved review threads or new actionable findings. Ready for review has been restored. GitHub still requires the normal human approval before merge. |
`gem "x", "1.0.0" if::FEATURE` and `if:flag == ...` are `if` modifiers,
not hash keys, because nothing separates them from the preceding value.
The guard now counts `word:` as a key only where an argument can start
(after `,`, `(` or `{`) and never as `word::`. A heredoc opener is also
refused: its body follows on later lines, past the inserted `end`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ZK1CuReoar7pKs2eZwepT
|
Both findings from the Codex review of
All four shapes are added to The other agent run said it was working on the same fix. I checked that the branch had no new commits before pushing, so please build on Generated by Claude Code |
|
BugBot review Generated by Claude Code |
|
Cursor (@cursor) review Please review follow-up |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 464896d. Configure here.
Review follow-up
464896d: closes the remaining interpolated-heredoc case in the shared Gemfile tail guard. Hosted and vendored modes leave unsupported declarations unchanged; native CLI tests also require zero redirects and VEX statements and a successful Bundler install. Preserves the author’s colon-modifier and ordinary-heredoc fixes. Validated with 267 core tests and four native tests covering five fixtures (Ruby 3.4.10 / Bundler 4.0.15); targeted Clippy and changed-hunk formatting checks pass. Ready to merge as-is from this review. 482 successful checks, 6 skipped, and all twelve workflows successful. Bugbot is clear on this commit; no unresolved review threads or new actionable findings.LLM Description written by Claude Code:claude-opus-5-5
Fixes #340
Summary
Hosted
scanno longer rewrites a Gemfilegemdeclaration that continues on the next line or carries a modifier. Before this fix:gem "vuln-gem",↵require: falsebecame a source block followed by an orphanedrequire: false, so every laterbundle installfailed to parse the Gemfile, while scan exited 0 and VEX attested the patch;gem "vuln-gem" if ENV["WITH_VULN"] != "0"became an unconditional declaration, so the condition was silently dropped.Both shapes now skip the redirect with the existing
redirect_gem_unrecognized_declarationwarning (its detail now names the reason) and leave the Gemfile and lock byte-identical.Root cause
The hosted rewriter (
patch/redirect/mod.rs,gem_line_re→gem_line_trailing_options) replaces the declaration's single line with asource … do … endblock, but it had no check that the line holds the whole declaration. Vendored mode had one (vendor::gem::rest_blocks_edit), but it used substring checks (ends_with(','),contains(" if ")) that missed several shapes.Fix
gem_line_tail_blocks_edit(next togem_line_trailing_options) scans the tail outside string literals and comments. It refuses:(/[/{, or a heredoc (<<);,,=>,key:,\);,-led tail;if/unless/while/until/rescue/and/or/dokeyword. Symbols (:unless), string contents, and hash keys (if:only where an argument can start, after,,(or{, and neverif::) don't count. So"1.0.0" if::FEATUREand"1.0.0" if:flag == …are refused as modifiers.redirect_gem_source_optionand own-vendored-wiring messages are unchanged.rest_blocks_editdelegates its structural checks to the same helper and keeps its source-token refusal. Behaviour there is stricter only on shapes that were already broken: a[continuation,=>continuation, tab-separatedunless,rescuemodifier, label-style modifiers, heredocs.Test evidence
=>,key:,\, brackets, paren form, heredoc)patch::redirect::tests::gemfile_multi_line_or_conditional_declaration_fails_closedrequire: false)if::X,unless::X,if:x == …)gemfile_single_line_declaration_lookalikes_still_rewritevendor::gemtest block after theconditionalassertione2e_redirect_gem_build::gem_hosted_multi_line_declaration_is_refused_and_still_installse2e_redirect_gem_build::gem_hosted_conditional_declaration_is_refused_and_still_installsI got the red results by checking out
main'scrates/socket-patch-core/srcwith only the new tests applied. For84a43d4, theif::FEATUREcase was confirmed red against369daa5's guard.Local runs:
cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features --no-fail-fast(at369daa5): 9726 passed, 12 failed. All 12 are permission-denial or write-failure simulations (covgap_commands_vendor*_state_write_failure_*,repair_*unremovable*,*write_failure*,relax_loop_must_not_traverse_symlinked_root, …). They can't fail as intended in this sandbox, which runs as root (uid 0), and none of them touch gem code. CI runs as non-root, and all of these pass there. At84a43d4, the core lib tests (same 4 root-only failures) and 91 CLI gem tests were re-run.cargo fmt:mainitself isn't rustfmt-clean (about 500 diffs) and CI doesn't gate on it, so this PR formats only its own hunks.🤖 Generated with Claude Code
https://claude.ai/code/session_015ZK1CuReoar7pKs2eZwepT
Note
Medium Risk
Changes Gemfile rewrite eligibility for hosted and vendored gem flows; incorrect classification could skip needed redirects or still break rare Gemfile shapes, but behavior is intentionally fail-closed with warnings.
Overview
Fixes #340: hosted gem redirect no longer rewrites
gemlines whose declaration is not confined to a single safe line. Multi-line continuations,if/unlessmodifiers (includingif::CONST), heredocs, and interpolated heredoc openers now fail closed withredirect_gem_unrecognized_declaration(detail names the reason) instead of producing broken Gemfiles or dropping conditions.Adds shared
gem_line_tail_blocks_edit, which scans the argument tail outside strings and comments for continuations, modifiers, and heredocs. The hosted rewriter calls it before moving a line into asource … doblock; vendored Gemfile editing delegates the same structural rules viarest_blocks_edit, replacing weaker substring checks.Unit tests cover refused shapes, lookalikes that still rewrite, and tail-parser edge cases. E2E
e2e_redirect_gem_buildgains drivers for multi-line, conditional, scoped-constant modifier, and heredoc fixtures (unchanged Gemfile/lock, no VEX, bundler still installs).Reviewed by Cursor Bugbot for commit 464896d. Configure here.