Skip to content

Fix VEX gemspec reading commented-out name/version (#1573) - #1582

Merged
Mikola Lysenko (mikolalysenko) merged 4 commits into
mainfrom
agent/fix-gemspec-comment-parse
Oct 15, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 4 commits into
mainfrom
agent/fix-gemspec-comment-parse

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1573

Summary

vex (and scan / apply / vendor --vex without --vex-product) names a gem library's top-level product from its root *.gemspec. A commented-out assignment used to win over the live one:

  • # spec.version = "0.0.1" above spec.version = Acme::VERSION gave pkg:gem/acme@0.0.1. It now gives pkg:gem/acme, because a computed version is dropped as documented.
  • # spec.name = "acme-legacy" above spec.name = "acme" gave pkg:gem/acme-legacy. It now gives pkg:gem/acme.

Root cause

parse_gemspec (crates/socket-patch-core/src/vex/product.rs) took the first line whose left side of = ended in .name / .version and whose right side was a quoted literal. It never skipped Ruby comments. Because it also skipped computed right-hand sides and kept searching, the first literal anywhere in the file won, even when Ruby's effective value was a later computed one.

Fix

  • parse_gemspec now reads the file through formats::gem::manifest::for_each_code_line, the Ruby line lexer the Gemfile readers already use (made pub(crate)). That lexer skips # comments outside strings, =begin / =end blocks, heredoc bodies and everything after __END__.
  • The last live assignment wins, as in Ruby (Gem::Specification.load). If it is computed, the version is dropped and the name falls back to the file stem.
  • When the file names the Gem::Specification.new do |spec| block parameter, only assignments to that receiver count, so a helper's ext.name = … can't rename the product.
  • Each line's code is split into statements on ; (outside strings), after any block opener, so a one-line Gem::Specification.new { |s| s.name = "a"; s.version = "1" } is read. This came from Bugbot's finding on c6b44b2.
  • =~ and == comparisons are still not taken for assignments.

No wrapper changes are needed: npm/, pypi/ and gem/ don't read gemspecs.

/code-review high

I ran it on the first revision. Its main point was that the PR duplicated the crate's existing Ruby lexer, so the second commit reuses it. That also fixes the findings about __END__, lower-case and backtick heredocs, and the per-line quote handling. I also took the receiver finding (match the spec's block parameter), dropped the redundant \r strip, and dropped the Vec::remove(0) allocation note along with the duplicated code. Not changed: a bare <<ID closing on an indented ID line, and 1<<SHIFT read as a heredoc opener. Both are limitations of the shared lexer that every Gemfile reader already has, and neither shape is plausible in a gemspec. Changing them belongs in a lexer fix, not this PR.

Test evidence

  • New vex::product::tests::detect_gemspec_ignores_commented_assignments covers both shapes from VEX gemspec product detection reads commented-out # spec.name = / # spec.version = lines, so the document names the wrong product or version #1573 and the variants the issue lists: trailing # comments, =begin/=end, heredocs (bare and quoted), << inside a string, a __END__ DATA section, another receiver's .name / .version, assignments on the block-opener line and after ;, last-assignment-wins, a trailing comment after a live literal, and the stem fallback.
    • Red on main: assertion failed: commented literal version above a computed one; left: Some("pkg:gem/acme@0.0.1") right: Some("pkg:gem/acme")
    • Green with the fix: cargo test -p socket-patch-core --all-features --lib vex::product gives 146 passed (194 with formats::gem).
  • cargo fmt --all -- --check: clean.
  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test --workspace --all-features --no-fail-fast: 14,575 passed, 10 failed. All 10 come from the sandbox and none touch this change. Nine are permission-based failure-injection tests (chmod-made unwritable or unremovable paths) that can't fail when tests run as root. The tenth, e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managed, needs patches-api.socket.dev, which this sandbox can't reach (tunnel error). CI runs these normally.

Per-issue checklist

🤖 Generated with Claude Code


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
A gem library's VEX product comes from its root *.gemspec. The reader
took the first line that looked like `spec.name = "..."` or
`spec.version = "..."`, comments included, so a commented-out
`# spec.version = "0.0.1"` above `spec.version = Acme::VERSION` gave
pkg:gem/acme@0.0.1, and a commented `# spec.name = "acme-legacy"`
renamed the product. Consumers matching VEX statements to their SBOM
then never applied them.

The gemspec is now read as Ruby sees it: `#` comments outside strings,
=begin/=end blocks and heredoc bodies are skipped, and the last live
assignment wins, so a computed version drops the version as documented.

Fixes #1573

Assisted-by: Claude Code:claude-opus-5-5
Read the gemspec through formats::gem's for_each_code_line, the lexer
the Gemfile readers already use, instead of a second copy. It also
stops at __END__ and knows lower-case and backtick heredocs, so a
DATA section or such a heredoc can't set the product's version.

Only assignments to the Gem::Specification.new block parameter count
when the file names one, so a later `ext.name = ...` in the same file
can't rename the product now that the last assignment wins.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/vex/product.rs
A one-line spec such as
`Gem::Specification.new { |s| s.name = "a"; s.version = "1" }`, or an
assignment after the `do |s|` on the opener line, was not read: the
product fell back to the file name with no version. Each line's code
is now split into statements on `;`, after any block opener.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 9de072d. Configure here.

Merged via the queue into main with commit 717b4a4 Oct 15, 2026
54 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-gemspec-comment-parse branch October 15, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VEX gemspec product detection reads commented-out # spec.name = / # spec.version = lines, so the document names the wrong product or version

3 participants