Skip to content

feat(bundler): checksum validation for bundler - #1655

Merged
eskultety merged 3 commits into
hermetoproject:mainfrom
Harsh-2005d:gem-check
Aug 18, 2026
Merged

feat(bundler): checksum validation for bundler#1655
eskultety merged 3 commits into
hermetoproject:mainfrom
Harsh-2005d:gem-check

Conversation

@Harsh-2005d

Copy link
Copy Markdown
Contributor

Description:

Closes #1652

Summary

The checksum field on GemDependency was always None — the lockfile parser never populated it and it was never used for verification. This PR wires up the full checksum pipeline for RubyGems dependencies.

Changes

lockfile_parser.rb

  • Extract checksums from the CHECKSUMS section via spec.source.checksum_store.to_lock(spec)
  • Emit them in sha256:<hexdigest> format in the JSON output

gem_models.py

  • Add checksum qualifier to GemDependency.purl for SBOM output

main.py

  • After downloading .gem files, verify each against its lockfile checksum using must_match_any_checksum
  • If no checksum is present in the lockfile, log a warning and skip verification

@gemini-code-assist

Copy link
Copy Markdown

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

Comment thread hermeto/core/package_managers/bundler/gem_models.py Outdated
Comment thread hermeto/core/package_managers/bundler/main.py Outdated
Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb Outdated
Comment thread hermeto/core/package_managers/bundler/main.py Outdated
@slimreaper35

This comment was marked as resolved.

@Harsh-2005d
Harsh-2005d force-pushed the gem-check branch 4 times, most recently from 183d763 to 22adc46 Compare July 14, 2026 12:28
@slimreaper35

slimreaper35 commented Jul 14, 2026

Copy link
Copy Markdown
Member

@Harsh-2005d Could you verify the integration test is passing? By temporary changing the repo-url for bundler/e2e.

@Harsh-2005d

Harsh-2005d commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

@Harsh-2005d Could you verify the integration test is passing? By temporary changing the repo-url for bundler/e2e.

there are assertion errors in test data for some packages, and if i regenerate test data from my repo url it introduces some unintended changes .

"pkg:gem/quux@0.0.1?vcs_url=git%2Bhttps://github.com/Harsh-2005d/integration-tests.git%40515cd9a5f1a688fdaeda58b50822ce266fe19025#quux",

@slimreaper35

This comment was marked as outdated.

@coderabbitai

This comment was marked as off-topic.

@coderabbitai coderabbitai 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.

Actionable comments posted: 5


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e7ddc213-1f87-4d68-9139-35e3fe507209

📥 Commits

Reviewing files that changed from the base of the PR and between f4327ee and e0eb183.

📒 Files selected for processing (4)
  • hermeto/core/package_managers/bundler/main.py
  • hermeto/core/package_managers/bundler/parser.py
  • hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb
  • tests/unit/package_managers/bundler/test_parser.py

Comment thread hermeto/core/package_managers/bundler/main.py Outdated
Comment thread hermeto/core/package_managers/bundler/main.py Outdated
Comment thread hermeto/core/package_managers/bundler/parser.py
Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb Outdated
Comment thread tests/unit/package_managers/bundler/test_parser.py

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
hermeto/core/package_managers/bundler/main.py (2)

71-74: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Make comments state rationale.

These comments restate aliases, types, or assignments. Remove them when the code is self-explanatory. Otherwise, state why the alias, environment variable, or tuple list is necessary.

Also applies to: 269-272, 290-291

Source: Coding guidelines


167-167: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the local variable annotation.

Annotate git_paths as list[tuple[DepName, FSDepName, DepURL]]. This is new Python code and must include type annotations.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 90cefa8b-910c-48ad-86af-25b2ed3aa515

📥 Commits

Reviewing files that changed from the base of the PR and between e0eb183 and 096c355.

⛔ Files ignored due to path filters (2)
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile.lock is excluded by !**/*.lock
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/in/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • hermeto/core/package_managers/bundler/main.py
  • hermeto/core/package_managers/bundler/parser.py
  • hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Containerfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/README.md
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/tmp.gemspec
  • tests/integration/bundler/test_bundler.py

Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb

@coderabbitai coderabbitai 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.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7a2612b2-949d-4c0d-b3bc-b41882580e8d

📥 Commits

Reviewing files that changed from the base of the PR and between 096c355 and 3b509af.

⛔ Files ignored due to path filters (2)
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile.lock is excluded by !**/*.lock
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/in/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • hermeto/core/package_managers/bundler/main.py
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Containerfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/README.md
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/tmp.gemspec
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json
  • tests/integration/bundler/test_bundler.py

Comment thread tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Containerfile Outdated
Comment thread tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/README.md Outdated
Comment thread tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json
Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb Outdated
Comment thread hermeto/core/package_managers/bundler/main.py
Comment thread hermeto/core/package_managers/bundler/main.py
Comment thread tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/tmp.gemspec Outdated
Comment thread tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json
Comment thread tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/README.md Outdated
@Harsh-2005d
Harsh-2005d force-pushed the gem-check branch 2 times, most recently from 9ffa1cb to bc70ad0 Compare August 3, 2026 12:57

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (3)
tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json (1)

47-47: ⚠️ Potential issue | 🟠 Major

Revert the temporary VCS references in both generated BOMs.

The checksum change must not change the quux or tmp commit identities. Restore the commits expected by each scenario lockfile, then regenerate both snapshots. This repeats the previous review finding.

  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json#L47-L47: restore the quux annotation subject.
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json#L60-L60: restore the tmp annotation subject.
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json#L558-L566: restore the quux component PURL.
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json#L727-L735: restore the tmp component PURL.
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json#L47-L47: restore the quux annotation subject.
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json#L60-L60: restore the tmp annotation subject.
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json#L558-L566: restore the quux component PURL.
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json#L727-L735: restore the tmp component PURL.
hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb (1)

14-14: ⚠️ Potential issue | 🟠 Major

Keep optional checksum parsing nil-safe.

checksum_store.to_lock(spec) can return nil when Gemfile.lock has no CHECKSUMS entry. The [] call executes before &.sub, so nil[/sha[^, ]*/] raises NoMethodError and prevents the parser from emitting an empty checksum map. The line also assumes that the running Bundler exposes checksum_store. Guard both cases, or enforce and document the minimum Bundler version.

Proposed fix
-      checksum = spec.source.checksum_store.to_lock(spec)[/sha[^, ]*/]&.sub("=",":")
+      lock_checksum =
+        if spec.source.respond_to?(:checksum_store)
+          spec.source.checksum_store&.to_lock(spec)
+        end
+      checksum = lock_checksum&.slice(/sha[^, ]*/)&.sub("=", ":")
tests/unit/package_managers/bundler/test_parser.py (1)

154-154: ⚠️ Potential issue | 🟠 Major

Add checksum propagation test cases.

The empty "checksums": {} values only keep existing fixtures parseable. They do not verify that parse_lockfile selects the "ruby" checksum or the binary platform checksum and passes it to the correct model. Add new test cases with a sha256:<hexdigest> value and assert both GemDependency.checksum and GemPlatformSpecificDependency.checksum.

As per coding guidelines, add new test cases instead of modifying existing ones and aim for near-full coverage of new code.

Also applies to: 303-303

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b4f2e191-83b2-4267-b416-ece57d61db1b

📥 Commits

Reviewing files that changed from the base of the PR and between 3b509af and 9ffa1cb.

⛔ Files ignored due to path filters (2)
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile.lock is excluded by !**/*.lock
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/in/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • hermeto/core/package_managers/bundler/main.py
  • hermeto/core/package_managers/bundler/parser.py
  • hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Containerfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/README.md
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json
  • tests/integration/bundler/test_bundler.py
  • tests/unit/package_managers/bundler/test_parser.py

Comment thread hermeto/core/package_managers/bundler/main.py
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (3)
hermeto/core/package_managers/bundler/parser.py (1)

62-75: ⚠️ Potential issue | 🟠 Major

Persist the checksum in the component PURL.

These constructors set checksum, but GemDependency.purl currently creates a bare PackageURL. Generated components therefore omit the checksum qualifier even when the lockfile provides a checksum.

hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb (1)

14-14: ⚠️ Potential issue | 🟠 Major

Restore the Bundler compatibility guard.

Line 14 calls checksum_store unconditionally. A Bundler runtime without this API, or a missing lock entry, can stop lockfile parsing with NoMethodError.

#!/bin/bash
set -euo pipefail

rg -n -C 3 'checksum_store|BUNDLED WITH|bundler' \
  hermeto tests \
  -g '*.rb' -g 'Gemfile.lock' -g 'Containerfile'
hermeto/core/package_managers/bundler/main.py (1)

156-158: ⚠️ Potential issue | 🟠 Major

Mark unchecked dependencies in the SBOM.

This branch only logs that verification was skipped. The later component conversion does not receive any missing-checksum state, so consumers cannot distinguish unchecked dependencies.

As per coding guidelines, “mark dependencies without user-provided checksums in the SBOM.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 70640966-5844-4df7-bc9d-a532d8bcc8fd

📥 Commits

Reviewing files that changed from the base of the PR and between dcd652c and 7589150.

⛔ Files ignored due to path filters (2)
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile.lock is excluded by !**/*.lock
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/in/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • hermeto/core/package_managers/bundler/main.py
  • hermeto/core/package_managers/bundler/parser.py
  • hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Containerfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Gemfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/README.md
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json
  • tests/integration/bundler/test_bundler.py
  • tests/unit/package_managers/bundler/test_parser.py

Comment thread tests/unit/package_managers/bundler/test_parser.py Outdated
@Harsh-2005d

Copy link
Copy Markdown
Contributor Author

@slimreaper35 @eskultety ready for review. Thanks.

@slimreaper35

Copy link
Copy Markdown
Member

@Harsh-2005d Functionally, it looks good to me, but I am sure @eskultety will point out that commit 2/3 does not pass integration tests because of SBOM mismatch that is fixed in commit 3/3.

@eskultety

Copy link
Copy Markdown
Member

@Harsh-2005d Functionally, it looks good to me, but I am sure @eskultety will point out that commit 2/3 does not pass integration tests because of SBOM mismatch that is fixed in commit 3/3.

Yes, I will :) .

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
hermeto/core/package_managers/bundler/main.py (1)

155-157: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Mark unchecked dependencies in the SBOM.

This branch only logs and skips verification. The supplied GemDependency.purl implementation has no checksum qualifier, and the visible flow does not show a missing-checksum SBOM property. Add the required marker or verify that to_component() adds it.

As per coding guidelines, “mark dependencies without user-provided checksums in the SBOM.”

#!/bin/bash
set -euo pipefail

rg -n -C 6 'def to_component|def purl|checksum|properties|hashes' hermeto/core/package_managers/bundler hermeto/core

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3153bcb5-5151-43a4-a029-8e4df0ab0fe8

📥 Commits

Reviewing files that changed from the base of the PR and between 7589150 and 7520289.

📒 Files selected for processing (7)
  • hermeto/core/package_managers/bundler/main.py
  • hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Containerfile
  • tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/README.md
  • tests/integration/bundler/scenarios/bundler_e2e_ruby33/out/bom.json
  • tests/integration/bundler/scenarios/bundler_e2e_ruby40/out/bom.json
  • tests/unit/package_managers/bundler/test_parser.py

Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb Outdated
Comment thread tests/integration/bundler/scenarios/bundler_checksum_mismatch/in/Containerfile Outdated
Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb Outdated
@Harsh-2005d

Harsh-2005d commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

New changes update the regex matching and add support for multiple checksums for Bundler hash verification. Although Bundler currently does not configure multiple checksum algorithms, this keeps the implementation future-proof if support is added later.

@Harsh-2005d
Harsh-2005d force-pushed the gem-check branch 2 times, most recently from ea3e415 to 4d3c207 Compare August 14, 2026 10:06
@taylormadore

Copy link
Copy Markdown
Member

@Harsh-2005d Can you rebase on main to pick-up a change to the CI?

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/unit/package_managers/bundler/test_parser.py (2)

157-212: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover the no-checksum parser path.

The new assertions cover only populated checksum values. Add a dedicated case with an empty checksums mapping. Assert checksum is None for regular and platform-specific dependencies. This verifies optional CHECKSUMS support before download verification.

As per coding guidelines, “Aim for near-full coverage of new code.”

Also applies to: 322-354

Source: Coding guidelines


73-73: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the new Ruff warnings from the mock callback.

Ruff reports args as unused and rejects Any in *args and **kwargs. Rename the unused capture and use object or the project-approved callback protocol.

Proposed fix
-    def _assert_bundle_is_hidden(*args: Any, **kwargs: Any) -> subprocess.CompletedProcess[str]:
+    def _assert_bundle_is_hidden(
+        *_args: object, **kwargs: object
+    ) -> subprocess.CompletedProcess[str]:

Source: Linters/SAST tools


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7dfc5a32-02f0-422d-9b6e-74f2b945e9fd

📥 Commits

Reviewing files that changed from the base of the PR and between 4d3c207 and 7d5f1a9.

📒 Files selected for processing (3)
  • hermeto/core/package_managers/bundler/main.py
  • hermeto/core/package_managers/bundler/parser.py
  • tests/unit/package_managers/bundler/test_parser.py

Comment thread tests/unit/package_managers/bundler/test_parser.py
Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb Outdated
Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb
Comment thread hermeto/core/package_managers/bundler/scripts/lockfile_parser.rb Outdated

@eskultety eskultety left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A couple more nitpicks, but ACK.

Update lockfile_parser.rb to extract checksums
Verify downloaded .gem files against lockfile checksums
warn and skip verification when no checksum is present

Assisted-by: Claude

Signed-off-by: Harsh Dahiya <dahiyaharsh2005@gmail.com>
The test verifies that Hermeto correctly fails when given
a malformed checksum for a gem.

Signed-off-by: Harsh Dahiya <dahiyaharsh2005@gmail.com>
Updated the e2e test to verify checksum handling

Signed-off-by: Harsh Dahiya <dahiyaharsh2005@gmail.com>
@eskultety
eskultety added this pull request to the merge queue Aug 18, 2026
Merged via the queue into hermetoproject:main with commit 17973fc Aug 18, 2026
14 checks passed
@Harsh-2005d
Harsh-2005d deleted the gem-check branch August 18, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Validate bundler checksums from Gemfile.lock

5 participants