Skip to content

security(install): verify lockfile tarball URLs#842

Merged
jdx merged 3 commits into
mainfrom
security/verify-lockfile-tarball-url
Jun 6, 2026
Merged

security(install): verify lockfile tarball URLs#842
jdx merged 3 commits into
mainfrom
security/verify-lockfile-tarball-url

Conversation

@jdx

@jdx jdx commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • verify explicit registry tarball URLs from lockfiles against per-version registry metadata before fetching
  • add ERR_AUBE_TARBALL_URL_MISMATCH and regenerate error-code docs data
  • cover tampered lockfile tarball URLs in lockfile settings BATS

Tests

  • cargo fmt --check
  • cargo test -p aube-codes
  • cargo check -p aube
  • cargo build
  • test/bats/bin/bats test/lockfile_settings.bats

This PR was generated by Codex.


Note

Medium Risk
Adds a supply-chain guard on the install fetch path with extra registry metadata calls when tarball URLs are pinned; behavior change for tampered lockfiles and npmjs.org mirror URL rules.

Overview
Lockfile tarball URL verification runs during install fetch when the lockfile pins an explicit registry tarball URL (e.g. lockfile-include-tarball-url=true or npm-alias entries). Before downloading, install fetches per-version registry metadata and compares dist.tarball to the lockfile URL; mismatches abort with ERR_AUBE_TARBALL_URL_MISMATCH (exit 34).

Matching allows an exact string match, or—when the lockfile URL is on registry.npmjs.org—the same /-/…tgz path on another host (e.g. Verdaccio mirror) so local mirrors still work. Tampered paths or arbitrary hosts pretending to mirror npm are rejected.

Docs and docs/error-codes.data.json include the new code. A BATS test corrupts a lockfile tarball URL and expects frozen install to fail with the new error.

Reviewed by Cursor Bugbot for commit 40a4064. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Install now validates lockfile tarball URLs against registry metadata; mismatches abort install with error ERR_AUBE_TARBALL_URL_MISMATCH. Mirror-style URLs with matching tarball paths on the public registry are accepted.
  • Tests

    • Added an integration test that corrupts a lockfile tarball URL and asserts installation fails with ERR_AUBE_TARBALL_URL_MISMATCH.
  • Documentation

    • Error catalog updated to include ERR_AUBE_TARBALL_URL_MISMATCH and its description.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Complex PR? Review this PR in Change Stack to move by importance, not file order.

Review Change Stack

📝 Walkthrough

Walkthrough

Validates lockfile-provided tarball URLs against registry dist.tarball during fetch. Adds ERR_AUBE_TARBALL_URL_MISMATCH (exit 34), integrates verification into the fetch path when a tarball_url_override is present, documents the error, and adds unit and integration tests asserting mismatch failures.

Changes

Tarball URL validation against registry metadata

Layer / File(s) Summary
Error code definition and catalog registration
crates/aube-codes/src/errors.rs
Introduces ERR_AUBE_TARBALL_URL_MISMATCH constant and registers it in the ALL error catalog with category Tarball / store, description, and exit_code: 34.
Tarball URL validation logic and integration
crates/aube/src/commands/install/fetch.rs
Adds verify_lockfile_tarball_url that fetches single-version registry metadata and compares dist.tarball to the lockfile tarball_url_override. Integrates an early check into tarball fetch tasks to abort with ERR_AUBE_TARBALL_URL_MISMATCH on mismatch, and adds unit tests for the matching predicate.
Error code documentation
docs/error-codes.data.json
Adds the ERR_AUBE_TARBALL_URL_MISMATCH error entry with category, description, and exit_code: 34.
Integration test for tarball URL validation
test/lockfile_settings.bats
Adds a Bats test that corrupts a lockfile tarball URL and asserts aube install --frozen-lockfile fails with ERR_AUBE_TARBALL_URL_MISMATCH.

Sequence Diagram

sequenceDiagram
  participant FetchTask
  participant Validation
  participant Registry
  FetchTask->>Validation: verify_lockfile_tarball_url(lockfile_url, registry_name, version)
  Validation->>Registry: fetch single-version metadata (name, version)
  Registry-->>Validation: metadata with dist.tarball
  Validation->>Validation: compare lockfile_url with dist.tarball (exact or npm mirror path)
  alt URLs match
    Validation-->>FetchTask: validation passes
  else URLs mismatch or metadata lacks URL
    Validation-->>FetchTask: error ERR_AUBE_TARBALL_URL_MISMATCH (redacted URLs)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through tarball paths with care,
I fetch the metadata from the air,
If lockfile and registry disagree,
I thump my paw and stop at thirty‑four, you see,
Frozen installs stay safe and fair.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'security(install): verify lockfile tarball URLs' directly and clearly summarizes the main change: adding tarball URL verification to the install process for security purposes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security/verify-lockfile-tarball-url

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 370ef30. Configure here.

Comment thread crates/aube/src/commands/install/fetch.rs
Comment thread crates/aube/src/commands/install/fetch.rs
@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a supply-chain guard that fetches per-version registry metadata to cross-check any explicit tarball URL stored in the lockfile before allowing the download to proceed, failing with the new ERR_AUBE_TARBALL_URL_MISMATCH (exit 34) if the URLs don't match.

  • fetch.rs: introduces verify_lockfile_tarball_url, called inside the fetch task whenever tarball_url_override is present; adds lockfile_tarball_url_matches_metadata with a special mirror-bypass for public npm registry lockfile URLs, covered by five unit tests.
  • errors.rs / error-codes.data.json: registers ERR_AUBE_TARBALL_URL_MISMATCH at exit code 34, correctly ordered after ERR_AUBE_GIT_ERROR (33) in the ALL slice.
  • lockfile_settings.bats: adds an integration test that corrupts a lockfile tarball URL and asserts frozen install aborts with the new error code.

Confidence Score: 5/5

Safe to merge; the new verification gate is correctly wired to only execute on uncached packages with lockfile-pinned URLs, the exit-code ordering is correct, and the mirror-bypass logic is well-tested.

The core logic is sound: tampered lockfile URLs are caught before any download, integrity checks still run after, and warm-cache installs are unaffected. The one observation about verification occupying a semaphore slot alongside the download is a throughput concern on large cold installs but does not affect correctness or security.

No files require special attention; the suggestion to move verify_lockfile_tarball_url above sem.acquire() in fetch.rs is a performance improvement, not a blocker.

Important Files Changed

Filename Overview
crates/aube/src/commands/install/fetch.rs Adds verify_lockfile_tarball_url called inside the semaphore-gated task; network errors at the metadata-fetch step are wrapped under ERR_AUBE_TARBALL_URL_MISMATCH (already flagged in a previous review). Otherwise the logic is sound: verification only runs for uncached packages and the URL used for download is always the verified lockfile URL.
crates/aube-codes/src/errors.rs Adds ERR_AUBE_TARBALL_URL_MISMATCH with exit_code 34 in the correct position after ERR_AUBE_GIT_ERROR (exit_code 33) in the ALL slice, preserving numerical ordering.
docs/error-codes.data.json Adds ERR_AUBE_TARBALL_URL_MISMATCH entry with exit_code 34, matching the Rust definition and correctly positioned after exit_code 33.
test/lockfile_settings.bats Adds an integration test that corrupts a lockfile tarball URL via perl in-place edit and verifies frozen install fails with ERR_AUBE_TARBALL_URL_MISMATCH. Correctly clears node_modules and the store before the second install.

Fix All in Claude Code

Reviews (3): Last reviewed commit: "fix(install): allow npm lockfiles across..." | Re-trigger Greptile

Comment on lines +990 to +999
miette!(
code = aube_codes::errors::ERR_AUBE_TARBALL_URL_MISMATCH,
"{}@{}: failed to verify lockfile tarball URL against registry metadata: {}",
registry_name,
version,
e
)
})?;
let Some(expected_url) = meta.dist.as_ref().map(|dist| dist.tarball.as_str()) else {
return Err(miette!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Network failures misattributed to ERR_AUBE_TARBALL_URL_MISMATCH

When fetch_single_version_metadata fails for reasons unrelated to a URL mismatch — network timeout, DNS failure, HTTP 5xx, or even a 404 for the specific version — the error is wrapped and emitted as ERR_AUBE_TARBALL_URL_MISMATCH. The code's own description ("A registry lockfile entry's explicit tarball URL didn't match the registry metadata") only covers the true mismatch case; wrapping network errors under it tells the user their lockfile was tampered with when the real cause is a registry connectivity problem. This is particularly confusing in a security-sensitive code path where distinguishing "tampered lockfile" from "registry unreachable" matters. The map_err on the metadata fetch should use a network/registry error code (e.g. ERR_AUBE_REGISTRY_REQUEST_FAILED) or just propagate the upstream error unchanged.

Fix in Claude Code

Comment thread crates/aube-codes/src/errors.rs Outdated

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

🧹 Nitpick comments (1)
crates/aube/src/commands/install/fetch.rs (1)

986-997: 💤 Low value

Error mapping could be more specific, but fail-closed approach is security-appropriate.

All registry fetch errors (network failures, 404s, timeouts, etc.) are mapped to ERR_AUBE_TARBALL_URL_MISMATCH. While the error message does include the underlying cause, a user facing a network outage might find it initially confusing to see "tarball URL mismatch" rather than "cannot reach registry."

However, from a security perspective, this fail-closed approach is reasonable: if the system cannot verify the lockfile URL against registry metadata, blocking the install is the safer choice. The underlying error is preserved in the message chain, so diagnosing the root cause remains possible.

Consider whether a more specific error message like "failed to fetch registry metadata to verify lockfile tarball URL: {e}" would improve clarity without weakening the security posture.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aube/src/commands/install/fetch.rs` around lines 986 - 997, The error
mapping for client.fetch_single_version_metadata currently always uses
ERR_AUBE_TARBALL_URL_MISMATCH which can mislead users on network failures;
update the map_err closure to preserve the same error code but change the
message to something like "failed to fetch registry metadata to verify lockfile
tarball URL: {}" while continuing to include the underlying error (e) and the
registry_name/version context, so the call that produces meta remains
fail-closed but has a clearer, more accurate top-level message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/aube/src/commands/install/fetch.rs`:
- Around line 986-997: The error mapping for
client.fetch_single_version_metadata currently always uses
ERR_AUBE_TARBALL_URL_MISMATCH which can mislead users on network failures;
update the map_err closure to preserve the same error code but change the
message to something like "failed to fetch registry metadata to verify lockfile
tarball URL: {}" while continuing to include the underlying error (e) and the
registry_name/version context, so the call that produces meta remains
fail-closed but has a clearer, more accurate top-level message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 449cf4cb-77c9-4d0b-8a48-a54868260c6e

📥 Commits

Reviewing files that changed from the base of the PR and between e1e2fb7 and 370ef30.

📒 Files selected for processing (4)
  • crates/aube-codes/src/errors.rs
  • crates/aube/src/commands/install/fetch.rs
  • docs/error-codes.data.json
  • test/lockfile_settings.bats

jdx commented Jun 5, 2026

Copy link
Copy Markdown
Owner Author

Addressed review feedback in c243ee5:

  • moved the new error-code metadata after ERR_AUBE_GIT_ERROR so bespoke exit codes remain numerically ordered
  • clarified the fail-closed metadata fetch error message while keeping ERR_AUBE_TARBALL_URL_MISMATCH

Validation rerun:

  • cargo fmt --check
  • cargo test -p aube-codes
  • cargo check -p aube
  • test/bats/bin/bats test/lockfile_settings.bats

This comment was generated by Codex.

jdx commented Jun 6, 2026

Copy link
Copy Markdown
Owner Author

Addressed the failing BATS shards by allowing lockfile tarball URLs from the public npm registry to verify against a configured registry mirror when the tarball path matches exactly. Exact URL matching still passes, path tampering still fails, and arbitrary non-npm hosts do not get mirror leniency.

Verification run:

  • cargo fmt --check
  • cargo check -p aube
  • cargo test -p aube tarball_url_match
  • cargo clippy -p aube --all-targets -- -D warnings
  • test/bats/bin/bats test/package_lock_write.bats
  • test/bats/bin/bats test/fetch.bats
  • test/bats/bin/bats test/import.bats
  • test/bats/bin/bats test/lockfile_settings.bats

One note: full cargo clippy --all-targets -- -D warnings still trips on an unrelated pre-existing items_after_test_module lint in crates/aube-linker/src/materialize.rs; I left that outside this PR.

This comment was generated by Codex.

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

🧹 Nitpick comments (1)
crates/aube/src/commands/install/fetch.rs (1)

1110-1116: 💤 Low value

Consider adding a test case documenting mirror→npm rejection.

The current tests cover the npm→mirror direction but don't explicitly test the reverse (lockfile from mirror, expected from npm). While rejects_mirror_match_from_arbitrary_host partially covers this, an explicit test would document that mirror leniency is intentionally one-directional:

📝 Suggested additional test
#[test]
fn tarball_url_match_rejects_mirror_lockfile_against_public_npm() {
    // Mirror leniency is one-directional: npm→mirror allowed, mirror→npm not.
    // A lockfile created against a mirror must match exactly when switching to npm.
    assert!(!lockfile_tarball_url_matches_metadata(
        "http://localhost:4873/is-odd/-/is-odd-3.0.1.tgz",
        "https://registry.npmjs.org/is-odd/-/is-odd-3.0.1.tgz",
    ));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aube/src/commands/install/fetch.rs` around lines 1110 - 1116, Add an
explicit unit test that ensures mirror→npm URL matches are rejected by
lockfile_tarball_url_matches_metadata; create a test (e.g.,
tarball_url_match_rejects_mirror_lockfile_against_public_npm) that asserts
lockfile_tarball_url_matches_metadata("http://localhost:4873/...tgz",
"https://registry.npmjs.org/...tgz") returns false, documenting that mirror
leniency is one-directional (npm→mirror allowed, mirror→npm not) and referencing
the existing function lockfile_tarball_url_matches_metadata to locate the
matching logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/aube/src/commands/install/fetch.rs`:
- Around line 1110-1116: Add an explicit unit test that ensures mirror→npm URL
matches are rejected by lockfile_tarball_url_matches_metadata; create a test
(e.g., tarball_url_match_rejects_mirror_lockfile_against_public_npm) that
asserts lockfile_tarball_url_matches_metadata("http://localhost:4873/...tgz",
"https://registry.npmjs.org/...tgz") returns false, documenting that mirror
leniency is one-directional (npm→mirror allowed, mirror→npm not) and referencing
the existing function lockfile_tarball_url_matches_metadata to locate the
matching logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e616b6c6-edec-41e2-ba47-963ba696bc23

📥 Commits

Reviewing files that changed from the base of the PR and between c243ee5 and 40a4064.

📒 Files selected for processing (1)
  • crates/aube/src/commands/install/fetch.rs

@jdx jdx merged commit 15fc05c into main Jun 6, 2026
19 checks passed
@jdx jdx deleted the security/verify-lockfile-tarball-url branch June 6, 2026 00:50
@greptile-apps greptile-apps Bot mentioned this pull request Jun 6, 2026
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.

1 participant