Repository navigation
Conversation
Previously, Bun did not store or verify integrity hashes for HTTP tarball dependencies (remote_tarball) or local tarball dependencies (local_tarball). Each `bun install` would download/read the tarball without verification, allowing silent package substitution if the tarball content changed. This security fix: 1. Computes SHA-512 integrity hash when extracting tarballs that don't already have an integrity hash (remote and local tarballs) 2. Stores the computed integrity hash in the package metadata 3. Serializes the integrity hash to bun.lock for remote_tarball and local_tarball packages (similar to how npm packages already work) 4. Parses the integrity hash from bun.lock when reading the lockfile 5. Passes the integrity to the tarball extraction task for verification on subsequent installs Now when a remote or local tarball's content changes but the lockfile integrity doesn't match, bun will fail with "Integrity check failed". 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Pass Integrity by pointer instead of by value to avoid unnecessary copies. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughCompute and propagate tarball integrity (SHA‑512) from extraction into package metadata and bun.lock, thread Integrity through enqueue/task payloads for local tarballs, add Integrity.compute, and add tests validating lockfile storage and verification failures on tampering. Changes
Suggested reviewers
Pre-merge checks✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (2)
🧰 Additional context used📓 Path-based instructions (3)src/**/*.{cpp,zig}📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
Files:
src/**/*.zig📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
Files:
**/*.zig📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)
Files:
🧠 Learnings (1)📓 Common learnings🔇 Additional comments (2)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/install/PackageManager/PackageManagerEnqueue.zig (1)
1073-1159: Use of.{}as “no integrity yet” sentinel is acceptable but a bit opaquePassing
.{} // No integrity for first-time install, will be computed during extractionintoenqueueLocalTarballfor.tarball→.localon first install is reasonable, assumingIntegritytreats that value as “no integrity / compute-only” and the extractor will compute and persist a hash rather than attempt to verify against it.For readability and future-proofing of the
Integritytype, consider replacing the raw.{}with a named value or constructor (e.g. a documentedIntegrity.empty,.none, or similar) so the “no integrity yet” semantics are obvious and remain correct ifIntegrity’s internal representation changes.src/install/PackageInstaller.zig (1)
1000-1007: Local tarball enqueue correctly uses lockfile meta integrity (minor style nit)Routing
this.lockfile.packages.items(.meta)[package_id].integrityintoenqueueTarballForReadingis the right behavior and matches the isolated-install path.If you want to tighten things up, you could read from the cached
this.metas[package_id].integrityinstead of re-slicinglockfile.packages, but that’s purely a micro-optimization / consistency tweak.src/install/lockfile/bun.lock.zig (1)
516-566: Tarball integrity serialization is correct, but parser now rejects older bun.lock files without tarball integrityThe new behavior has two pieces:
- Writer side: For
.local_tarballand.remote_tarballyou now emit:
["name@tarball", INFO, "integrity"], symmetric with the existing npm layout (which has["name@version", registry, INFO, "integrity"]).- This is internally consistent and will round-trip correctly with the updated parser.
- Parser side:
parseIntoBinaryLockfilenow treats.local_tarballand.remote_tarballthe same as.npmfor integrity:
- In the “// integrity” switch you added
.local_tarball, .remote_tarballand hard-require a third/fourth array element; if it’s missing, you log “Missing integrity” and returnerror.InvalidPackageInfo.Given the PR description explicitly says previous bun.lock did not store integrity for tarball URL dependencies, existing text lockfiles with tarball entries will not have this integrity element. Under the new parser they will now fail to load instead of being transparently upgraded.
That’s a backward-compatibility break: users with an older bun.lock that includes tarball deps can’t run
bun installonce to migrate; they’ll just getInvalidPackageInfo.Consider relaxing the parser for tarballs while still enforcing integrity for npm, for example:
- // integrity - switch (res.tag) { - .npm, .local_tarball, .remote_tarball => { - if (i >= pkg_info.len) { - try log.addError(source, value.loc, "Missing integrity"); - return error.InvalidPackageInfo; - } - const integrity_expr = pkg_info.at(i); - i += 1; - const integrity_str = integrity_expr.asString(allocator) orelse { - try log.addError(source, integrity_expr.loc, "Expected a string"); - return error.InvalidPackageInfo; - }; - - pkg.meta.integrity = Integrity.parse(integrity_str); - }, + // integrity + switch (res.tag) { + .npm => { + if (i >= pkg_info.len) { + try log.addError(source, value.loc, "Missing integrity"); + return error.InvalidPackageInfo; + } + const integrity_expr = pkg_info.at(i); + i += 1; + const integrity_str = integrity_expr.asString(allocator) orelse { + try log.addError(source, integrity_expr.loc, "Expected a string"); + return error.InvalidPackageInfo; + }; + pkg.meta.integrity = Integrity.parse(integrity_str); + }, + .local_tarball, .remote_tarball => { + // Older bun.lock files did not store tarball integrity. + // If integrity is present, parse it; if not, leave meta.integrity at its default. + if (i < pkg_info.len) { + const integrity_expr = pkg_info.at(i); + if (integrity_expr.isString()) { + const integrity_str = integrity_expr.asString(allocator).?; + pkg.meta.integrity = Integrity.parse(integrity_str); + i += 1; + } + } + },This preserves strictness for npm (integrity has always been present there), while allowing legacy tarball entries without integrity to be read, upgraded, and then re-written with a computed SHA‑512 value.
The Stringifier changes (emitting integrity for tarballs using
pkg_meta.integrityand importingIntegrityat the bottom) are otherwise correct.Also applies to: 585-622, 1855-1869, 2224-2227
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
src/install/PackageInstaller.zig(1 hunks)src/install/PackageManager/PackageManagerEnqueue.zig(6 hunks)src/install/PackageManager/processDependencyList.zig(1 hunks)src/install/extract_tarball.zig(2 hunks)src/install/install.zig(2 hunks)src/install/integrity.zig(1 hunks)src/install/isolated_install.zig(1 hunks)src/install/lockfile/bun.lock.zig(3 hunks)test/regression/issue/tarball-integrity.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
src/**/*.{cpp,zig}
📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
src/**/*.{cpp,zig}: Usebun bdorbun run build:debugto build debug versions for C++ and Zig source files; creates debug build at./build/debug/bun-debug
Run tests usingbun bd test <test-file>with the debug build; never usebun testdirectly as it will not include your changes
Execute files usingbun bd <file> <...args>; never usebun <file>directly as it will not include your changes
Enable debug logs for specific scopes usingBUN_DEBUG_$(SCOPE)=1environment variable
Code generation happens automatically as part of the build process; no manual code generation commands are required
Files:
src/install/PackageManager/processDependencyList.zigsrc/install/integrity.zigsrc/install/isolated_install.zigsrc/install/lockfile/bun.lock.zigsrc/install/install.zigsrc/install/extract_tarball.zigsrc/install/PackageInstaller.zigsrc/install/PackageManager/PackageManagerEnqueue.zig
src/**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
Use
bun.Output.scoped(.${SCOPE}, .hidden)for creating debug logs in Zig codeImplement core functionality in Zig, typically in its own directory in
src/
src/**/*.zig: Zig source code should be organized insrc/*.zigfor core runtime, JavaScript bindings, and package manager
In Zig code, be careful with allocators and use defer for cleanup to manage memory properly
UseBUN_DEBUG_QUIET_LOGS=1to disable debug logging, orBUN_DEBUG_<scopeName>=1to enable specific Output.scoped calls in debug builds
Runbun run zig:check-allto compile the Zig code on all platforms when making platform-specific changes
src/**/*.zig: Private fields in Zig are fully supported using the#prefix:struct { #foo: u32 };
Use decl literals in Zig for declaration initialization:const decl: Decl = .{ .binding = 0, .value = 0 };
Prefer@importat the bottom of the file (auto formatter will move them automatically)
Files:
src/install/PackageManager/processDependencyList.zigsrc/install/integrity.zigsrc/install/isolated_install.zigsrc/install/lockfile/bun.lock.zigsrc/install/install.zigsrc/install/extract_tarball.zigsrc/install/PackageInstaller.zigsrc/install/PackageManager/PackageManagerEnqueue.zig
**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)
**/*.zig: Expose generated bindings in Zig structs usingpub const js = JSC.Codegen.JS<ClassName>with trait conversion methods:toJS,fromJS, andfromJSDirect
Use consistent parameter nameglobalObjectinstead ofctxin Zig constructor and method implementations
Usebun.JSError!JSValuereturn type for Zig methods and constructors to enable proper error handling and exception propagation
Implement resource cleanup usingdeinit()method that releases resources, followed byfinalize()called by the GC that invokesdeinit()and frees the pointer
UseJSC.markBinding(@src())in finalize methods for debugging purposes before callingdeinit()
For methods returning cached properties in Zig, declare external C++ functions usingextern fnandcallconv(JSC.conv)calling convention
Implement getter functions with naming patternget<PropertyName>in Zig that acceptthisandglobalObjectparameters and returnJSC.JSValue
Access JavaScript CallFrame arguments usingcallFrame.argument(i), check argument count withcallFrame.argumentCount(), and getthiswithcallFrame.thisValue()
For reference-counted objects, use.deref()in finalize instead ofdestroy()to release references to other JS objects
Files:
src/install/PackageManager/processDependencyList.zigsrc/install/integrity.zigsrc/install/isolated_install.zigsrc/install/lockfile/bun.lock.zigsrc/install/install.zigsrc/install/extract_tarball.zigsrc/install/PackageInstaller.zigsrc/install/PackageManager/PackageManagerEnqueue.zig
src/**/*.{ts,zig,cpp}
📄 CodeRabbit inference engine (CLAUDE.md)
Always use absolute paths in file operations
Files:
src/install/PackageManager/processDependencyList.zigsrc/install/integrity.zigsrc/install/isolated_install.zigsrc/install/lockfile/bun.lock.zigsrc/install/install.zigsrc/install/extract_tarball.zigsrc/install/PackageInstaller.zigsrc/install/PackageManager/PackageManagerEnqueue.zig
src/**/*.{ts,zig}
📄 CodeRabbit inference engine (CLAUDE.md)
Avoid shell commands in code - don't use
findorgrep; use Bun's Glob and built-in tools instead
Files:
src/install/PackageManager/processDependencyList.zigsrc/install/integrity.zigsrc/install/isolated_install.zigsrc/install/lockfile/bun.lock.zigsrc/install/install.zigsrc/install/extract_tarball.zigsrc/install/PackageInstaller.zigsrc/install/PackageManager/PackageManagerEnqueue.zig
test/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts,jsx,tsx}: Write tests as JavaScript and TypeScript files using Jest-style APIs (test,describe,expect) and import frombun:test
Usetest.eachand data-driven tests to reduce boilerplate when testing multiple similar cases
Files:
test/regression/issue/tarball-integrity.test.ts
test/regression/issue/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When a test is for a specific numbered GitHub Issue, place it in
test/regression/issue/${issueNumber}.test.tswith a REAL issue number, not a placeholder
Files:
test/regression/issue/tarball-integrity.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: For single-file tests, prefer using-eflag overtempDir
For multi-file tests, prefer usingtempDirfromharnessandBun.spawnover other temporary directory creation methods
Always useport: 0for network tests and do not hardcode ports or use custom random port number functions
UsenormalizeBunSnapshotto normalize snapshot output of tests
Never write tests that check for no 'panic' or 'uncaught exception' or similar in the test output - that is NOT a valid test
UsetempDirfromharnessto create temporary directories, do not usetmpdirSyncorfs.mkdtempSync
When spawning processes in tests, check stdout/stderr expectations BEFORE checking exit code to get more useful error messages on test failure
Do not write flaky tests - do not usesetTimeoutin tests, insteadawaitthe condition to be met as you are testing the CONDITION not the TIME PASSING
Verify your test fails withUSE_SYSTEM_BUN=1 bun test <file>and passes withbun bd test <file>- your test is NOT VALID if it passes withUSE_SYSTEM_BUN=1
Files:
test/regression/issue/tarball-integrity.test.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Usebun:testwith files that end in*.test.{ts,js,jsx,tsx,mjs,cjs}
Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Never use hardcoded port numbers in tests. Always useport: 0to get a random port
Prefer concurrent tests over sequential tests usingtest.concurrentordescribe.concurrentwhen multiple tests spawn processes or write files, unless it's very difficult to make them concurrent
When spawning Bun processes in tests, usebunExeandbunEnvfromharnessto ensure the same build of Bun is used and debug logging is silenced
Use-eflag for single-file tests when spawning Bun processes
UsetempDir()from harness to create temporary directories with files for multi-file tests instead of creating files manually
Prefer async/await over callbacks in tests
When callbacks must be used and it's just a single callback, usePromise.withResolversto create a promise that can be resolved or rejected from a callback
Do not set a timeout on tests. Bun already has timeouts
UseBuffer.alloc(count, fill).toString()instead of'A'.repeat(count)to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds
Usedescribeblocks for grouping related tests
Always useawait usingorusingto ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Always check exit codes and test error scenarios in error tests
Usedescribe.each()for parameterized tests
UsetoMatchSnapshot()for snapshot testing
UsebeforeAll(),afterEach(),beforeEach()for setup/teardown in tests
Track resources (servers, clients) in arrays for cleanup inafterEach()
Files:
test/regression/issue/tarball-integrity.test.ts
test/regression/issue/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
Regression tests for specific issues go in
/test/regression/issue/${issueNumber}.test.ts. Do not put tests without issue numbers in the regression directory
Files:
test/regression/issue/tarball-integrity.test.ts
🧠 Learnings (31)
📓 Common learnings
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/bundle.test.ts : Organize bundle tests in bundle.test.ts for tests concerning bundling bugs that only occur in DevServer
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to test/js/bun/**/*.test.{ts,tsx} : For Bun-specific API tests, use the `test/js/bun/` directory (for http, crypto, ffi, shell, etc.)
Applied to files:
test/regression/issue/tarball-integrity.test.tssrc/install/PackageManager/PackageManagerEnqueue.zig
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>` - your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/sourcemap.test.ts : Organize source-map tests in sourcemap.test.ts for tests verifying source-maps are correct
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-10-19T02:44:46.354Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: packages/bun-otel/context-propagation.test.ts:1-1
Timestamp: 2025-10-19T02:44:46.354Z
Learning: In the Bun repository, standalone packages under packages/ (e.g., bun-vscode, bun-inspector-protocol, bun-plugin-yaml, bun-plugin-svelte, bun-debug-adapter-protocol, bun-otel) co-locate their tests with package source code using *.test.ts files. This follows standard npm/monorepo patterns. The test/ directory hierarchy (test/js/bun/, test/cli/, test/js/node/) is reserved for testing Bun's core runtime APIs and built-in functionality, not standalone packages.
Applied to files:
test/regression/issue/tarball-integrity.test.tssrc/install/PackageManager/PackageManagerEnqueue.zig
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/regression/issue/**/*.test.ts : Regression tests for specific issues go in `/test/regression/issue/${issueNumber}.test.ts`. Do not put tests without issue numbers in the regression directory
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/ecosystem.test.ts : Organize ecosystem tests in ecosystem.test.ts for tests involving ensuring certain libraries are correct; prefer testing concrete bugs over testing entire packages
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : Use `normalizeBunSnapshot` to normalize snapshot output of tests
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/esm.test.ts : Organize ESM tests in esm.test.ts for tests about various ESM features in development mode
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : When spawning Bun processes in tests, use `bunExe` and `bunEnv` from `harness` to ensure the same build of Bun is used and debug logging is silenced
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Add tests for new Bun runtime functionality
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/cli/**/*.{js,ts,jsx,tsx} : When testing Bun as a CLI, use the `spawn` API from `bun` with the `bunExe()` and `bunEnv` from `harness` to execute Bun commands and validate exit codes, stdout, and stderr
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/**/*.{js,ts,jsx,tsx} : Write tests as JavaScript and TypeScript files using Jest-style APIs (`test`, `describe`, `expect`) and import from `bun:test`
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Unit tests for specific features are organized by module (e.g., `/test/js/bun/`, `/test/js/node/`)
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Ensure V8 API tests compare identical C++ code output between Node.js and Bun through the test suite validation process
Applied to files:
test/regression/issue/tarball-integrity.test.ts
📚 Learning: 2025-11-20T19:51:32.288Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.
Applied to files:
src/install/lockfile/bun.lock.zigsrc/install/PackageManager/PackageManagerEnqueue.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to src/bun.js/bindings/generated_classes_list.zig : Include new class bindings in `src/bun.js/bindings/generated_classes_list.zig` to register them with the code generator
Applied to files:
src/install/lockfile/bun.lock.zig
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/src/napi/napi.zig : For each new V8 C++ method, add both GCC/Clang and MSVC mangled symbol names to the V8API struct in src/napi/napi.zig using extern fn declarations
Applied to files:
src/install/lockfile/bun.lock.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to src/**/*.zig : Implement core functionality in Zig, typically in its own directory in `src/`
Applied to files:
src/install/install.zig
📚 Learning: 2025-08-30T09:09:18.384Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 22231
File: src/bundler/bundle_v2.zig:48-48
Timestamp: 2025-08-30T09:09:18.384Z
Learning: In Zig, when a module exports a top-level struct, import("./Module.zig") directly returns that struct type and can be used as a type alias without needing to access a field within the module. This is a common pattern in the Bun codebase.
Applied to files:
src/install/install.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to **/*.zig : Expose generated bindings in Zig structs using `pub const js = JSC.Codegen.JS<ClassName>` with trait conversion methods: `toJS`, `fromJS`, and `fromJSDirect`
Applied to files:
src/install/install.zig
📚 Learning: 2025-11-24T18:36:38.104Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:38.104Z
Learning: Applies to src/**/*.zig : Prefer `import` at the bottom of the file (auto formatter will move them automatically)
Applied to files:
src/install/install.zig
📚 Learning: 2025-11-24T18:36:38.104Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:38.104Z
Learning: Applies to src/**/*.zig : Private fields in Zig are fully supported using the `#` prefix: `struct { #foo: u32 };`
Applied to files:
src/install/install.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to src/bun.js/api/BunObject.zig : Implement getter functions in `src/bun.js/api/BunObject.zig` that return your feature, and export them in the `exportAll()` function
Applied to files:
src/install/install.zigsrc/install/PackageManager/PackageManagerEnqueue.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to src/**/index.zig : Create an `index.zig` entry point in your module directory that exports all module functionality
Applied to files:
src/install/install.zig
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to src/**/*.zig : Zig source code should be organized in `src/*.zig` for core runtime, JavaScript bindings, and package manager
Applied to files:
src/install/install.zig
📚 Learning: 2025-09-02T17:14:46.924Z
Learnt from: taylordotfish
Repo: oven-sh/bun PR: 22227
File: src/safety/alloc.zig:93-95
Timestamp: 2025-09-02T17:14:46.924Z
Learning: In bun's Zig codebase, they use a custom extension of Zig that supports private field syntax with the `#` prefix (e.g., `#allocator`, `#trace`). This is not standard Zig syntax but is valid in their custom implementation. Fields prefixed with `#` are private fields that cannot be accessed from outside the defining struct.
Applied to files:
src/install/install.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to **/js_*.zig : Implement `JSYourFeature` struct in a file like `js_your_feature.zig` to create JavaScript bindings for Zig functionality
Applied to files:
src/install/install.zig
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Write JS builtins for Bun's Node.js compatibility and APIs, and run `bun bd` after changes
Applied to files:
src/install/PackageManager/PackageManagerEnqueue.zig
🧬 Code graph analysis (1)
test/regression/issue/tarball-integrity.test.ts (1)
test/harness.ts (2)
tempDir(277-284)bunExe(102-105)
🔇 Additional comments (8)
src/install/PackageManager/PackageManagerEnqueue.zig (3)
126-157: Integrity propagation fromenqueueTarballForReadingtoenqueueLocalTarballlooks correct; consider behavior with conflicting integrities for same pathThreading
integrity: IntegrityintoenqueueTarballForReadingand passing it through toenqueueLocalTarballis consistent with the new.tarballhandling and ensures the local tarball task has access to the integrity when scheduled.One behavioral nuance: because the task queue is keyed only by
Task.Id.forTarball(path)and you only callenqueueLocalTarballwhen!task_queue.found_existing, the first integrity seen for a given tarball path wins; later calls for the same path will reuse the existing task and silently ignore any different integrity value. If it’s possible (even due to user‑edited lockfiles) to end up with multipleResolutions or dependencies pointing at the same tarball path but with differing integrity values, you may want to assert or at least log in debug builds when that invariant is violated, rather than silently preferring the first one.If your design guarantees “one path → one integrity” this is fine as‑is, but it’s worth making that invariant explicit in comments or docs.
1293-1333: Local tarball task payload now includes integrity; plumbing looks consistentAdding the
integrity: Integrityparameter toenqueueLocalTarballand wiring it through to.local_tarball.tarball.integrityin the task request cleanly connects the enqueue sites (both first-install.{}andenqueueTarballForReadingwith a real value) to the worker that will actually open/verify the tarball.Assuming
Integrityis cheap to copy and its backing data outlives the task (which is consistent with how other fields likeresolutionare handled), this looks correct.
1888-1896: Integrity import alias is correctly added alongside other install types
const Integrity = bun.install.Integrity;is placed with the otherbun.install.*aliases and keeps the public surface of this module consistent.src/install/integrity.zig (1)
183-190: Integrity.compute implementation is consistent with existing hashing pathsSHA-512 digest computation matches the verifyByTag pattern and correctly initializes
Integritywithtag = .sha512and a zeroed buffer. No issues here.src/install/isolated_install.zig (1)
1083-1089: Isolated install now correctly threads integrity into local tarball enqueuePassing
lockfile.packages.items(.meta)[pkg_id].integrityintoenqueueTarballForReadingaligns the isolated install flow with the main installer’s integrity handling. This looks correct.src/install/PackageManager/processDependencyList.zig (1)
178-193: Persisting computed tarball integrity into package meta is correctAssigning
package.meta.integrity = data.computed_integrityfor.local_tarballand.remote_tarballensures the lockfile stores a stable integrity hash derived during extraction, which is exactly what you need for future verification.src/install/extract_tarball.zig (1)
13-36: run()’s integrity computation and propagation match the intended semantics
- If a supported integrity is provided (npm/lockfile), you verify against it and carry it through as
computed_integrity.- If not, you compute a new SHA‑512 digest over the compressed tarball bytes and store it in
computed_integrity, while skipping verification on this first install.- The extraction result always carries
computed_integrity, enabling later lockfile persistence and verification.This is aligned with npm-style integrity handling and looks solid.
src/install/install.zig (1)
205-215: Adding computed_integrity to ExtractData and re-exporting Integrity is appropriate
ExtractData.computed_integrity: Integrity = .{}cleanly carries the extraction-time hash without breaking existing call sites.- Re-exporting
Integrityfrom this module matches existing patterns (e.g.,Resolution,Store) and simplifies use asbun.install.Integrity.No issues here.
Also applies to: 278-279
| import { describe, expect, setDefaultTimeout, test } from "bun:test"; | ||
| import { mkdirSync, readFileSync, writeFileSync } from "fs"; | ||
| import { bunEnv, bunExe, tempDir } from "harness"; | ||
| import { join } from "path"; | ||
|
|
||
| setDefaultTimeout(60_000); // 60 second timeout | ||
|
|
There was a problem hiding this comment.
Test placement and timeout usage don’t fully match the stated guidelines
Two minor points:
-
Regression test location/name
The guidelines say regression tests for specific issues should live intest/regression/issue/${issueNumber}.test.tswith a real GitHub issue number, and that tests without issue numbers shouldn’t go in the regression directory. Here the file istest/regression/issue/tarball-integrity.test.tswithout an issue number.
If this is tied to a specific issue, consider renaming it accordingly (e.g.test/regression/issue/25264.test.ts). If it’s a general feature test, it may fit better under a module-focused directory (e.g.test/js/bun/...). -
setDefaultTimeout
The test guidelines explicitly say not to set custom timeouts; Bun’s test runner manages timeouts itself. Since you’re already usingtimeoutoptions onBun.spawnSynccalls, the globalsetDefaultTimeout(60_000)can likely be dropped.
Also applies to: 19-19
🤖 Prompt for AI Agents
In test/regression/issue/tarball-integrity.test.ts around lines 1 to 7, the test
file name and global timeout usage do not follow repo guidelines: rename the
file to include the actual GitHub issue number (e.g.
test/regression/issue/25264.test.ts) if this is a regression tied to an issue,
or move it out of test/regression/issue into the appropriate module-focused
directory (e.g. test/js/bun/...) if it’s a general feature test; and remove the
setDefaultTimeout(60_000) line so the test runner’s default timeout is used
(rely on explicit timeout options already passed to Bun.spawnSync calls
instead).
| using dir = tempDir("tarball-integrity-test", {}); | ||
|
|
||
| // Create a simple test tarball with npm-style 'package' directory | ||
| const tarballDir = join(String(dir), "package"); | ||
| mkdirSync(tarballDir, { recursive: true }); | ||
| writeFileSync(join(tarballDir, "package.json"), JSON.stringify({ name: "local-pkg", version: "1.0.0" })); | ||
|
|
||
| // Create the tarball in the project directory | ||
| const projectDir = join(String(dir), "project"); | ||
| mkdirSync(projectDir, { recursive: true }); | ||
|
|
||
| const tarResult = Bun.spawnSync({ | ||
| cmd: ["tar", "-czf", join(projectDir, "local-pkg.tgz"), "-C", String(dir), "package"], | ||
| cwd: String(dir), | ||
| }); | ||
| expect(tarResult.exitCode).toBe(0); | ||
|
|
||
| // Create a project that depends on the local tarball | ||
| writeFileSync( | ||
| join(projectDir, "package.json"), | ||
| JSON.stringify({ | ||
| name: "test-project", | ||
| dependencies: { | ||
| "local-pkg": "local-pkg.tgz", | ||
| }, | ||
| }), | ||
| ); | ||
|
|
||
| // Run bun install | ||
| const installResult = Bun.spawnSync({ | ||
| cmd: [bunExe(), "install"], | ||
| cwd: projectDir, | ||
| env: { | ||
| ...bunEnv, | ||
| BUN_INSTALL_CACHE_DIR: join(projectDir, ".bun-cache"), | ||
| }, | ||
| timeout: 30000, | ||
| }); | ||
|
|
||
| if (installResult.exitCode !== 0) { | ||
| console.log("stdout:", installResult.stdout.toString()); | ||
| console.log("stderr:", installResult.stderr.toString()); | ||
| } | ||
| expect(installResult.exitCode).toBe(0); | ||
|
|
||
| // Read the lockfile and verify it contains the integrity hash | ||
| const lockfilePath = join(projectDir, "bun.lock"); | ||
| const lockfileContent = readFileSync(lockfilePath, "utf8"); | ||
|
|
||
| // The lockfile should contain an integrity hash for the local tarball | ||
| expect(lockfileContent).toContain("sha512-"); | ||
| expect(lockfileContent).toContain("local-pkg.tgz"); | ||
| }); | ||
|
|
||
| test("integrity verification fails when tarball content changes", async () => { | ||
| using dir = tempDir("tarball-integrity-test", {}); | ||
|
|
||
| // Create version 1 tarball | ||
| const v1Dir = join(String(dir), "v1", "package"); | ||
| mkdirSync(v1Dir, { recursive: true }); | ||
| writeFileSync(join(v1Dir, "package.json"), JSON.stringify({ name: "pkg", version: "1.0.0" })); | ||
| Bun.spawnSync({ | ||
| cmd: ["tar", "-czf", join(String(dir), "v1.tgz"), "-C", join(String(dir), "v1"), "package"], | ||
| cwd: String(dir), | ||
| }); | ||
|
|
||
| // Create version 2 tarball (different content) | ||
| const v2Dir = join(String(dir), "v2", "package"); | ||
| mkdirSync(v2Dir, { recursive: true }); | ||
| writeFileSync( | ||
| join(v2Dir, "package.json"), | ||
| JSON.stringify({ name: "pkg", version: "2.0.0", description: "changed" }), | ||
| ); | ||
| Bun.spawnSync({ | ||
| cmd: ["tar", "-czf", join(String(dir), "v2.tgz"), "-C", join(String(dir), "v2"), "package"], | ||
| cwd: String(dir), | ||
| }); | ||
|
|
||
| // Create project | ||
| const projectDir = join(String(dir), "project"); | ||
| mkdirSync(projectDir, { recursive: true }); | ||
| writeFileSync( | ||
| join(projectDir, "package.json"), | ||
| JSON.stringify({ | ||
| name: "test-project", | ||
| dependencies: { | ||
| pkg: `file:../v1.tgz`, | ||
| }, | ||
| }), | ||
| ); | ||
|
|
||
| // First install - should succeed and create lockfile with integrity | ||
| const firstInstall = Bun.spawnSync({ | ||
| cmd: [bunExe(), "install"], | ||
| cwd: projectDir, | ||
| env: { | ||
| ...bunEnv, | ||
| BUN_INSTALL_CACHE_DIR: join(projectDir, ".bun-cache"), | ||
| }, | ||
| timeout: 30000, | ||
| }); | ||
|
|
||
| expect(firstInstall.exitCode).toBe(0); | ||
|
|
||
| // Verify lockfile has integrity | ||
| const lockfileContent = readFileSync(join(projectDir, "bun.lock"), "utf8"); | ||
| expect(lockfileContent).toContain("sha512-"); | ||
|
|
||
| // Now modify the lockfile to point to v2.tgz but keep the v1 integrity | ||
| // This simulates an attacker swapping the tarball content | ||
| const modifiedLockfile = lockfileContent.replace(/v1\.tgz/g, "v2.tgz"); | ||
| writeFileSync(join(projectDir, "bun.lock"), modifiedLockfile); | ||
|
|
||
| // Update package.json to match | ||
| writeFileSync( | ||
| join(projectDir, "package.json"), | ||
| JSON.stringify({ | ||
| name: "test-project", | ||
| dependencies: { | ||
| pkg: `file:../v2.tgz`, | ||
| }, | ||
| }), | ||
| ); | ||
|
|
||
| // Clean cache and node_modules to force re-extraction | ||
| Bun.spawnSync({ | ||
| cmd: ["rm", "-rf", "node_modules", ".bun-cache"], | ||
| cwd: projectDir, | ||
| }); | ||
|
|
||
| // Second install with different tarball content should fail integrity check | ||
| const secondInstall = Bun.spawnSync({ | ||
| cmd: [bunExe(), "install"], | ||
| cwd: projectDir, | ||
| env: { | ||
| ...bunEnv, | ||
| BUN_INSTALL_CACHE_DIR: join(projectDir, ".bun-cache"), | ||
| }, | ||
| timeout: 30000, | ||
| }); | ||
|
|
||
| // The install should fail because the integrity check failed | ||
| expect(secondInstall.exitCode).not.toBe(0); | ||
| const stderr = secondInstall.stderr.toString(); | ||
| expect(stderr).toContain("Integrity check failed"); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check for existing tar and rm usage in tests
rg -nP 'Bun\.spawnSync\(\s*{\s*cmd:\s*\["tar"' test || echo "No tar usage found"
rg -nP 'Bun\.spawnSync\(\s*{\s*cmd:\s*\["rm' test || echo "No rm usage found"Repository: oven-sh/bun
Length of output: 91
🏁 Script executed:
# Check for Windows-specific guards in tests
rg -n 'process\.platform|skip.*[Ww]indows|win32' test --type ts --type js | head -20Repository: oven-sh/bun
Length of output: 2256
🏁 Script executed:
# Check the specific test file mentioned in the review
cat -n test/regression/issue/tarball-integrity.test.ts | head -180Repository: oven-sh/bun
Length of output: 6902
🏁 Script executed:
# Check if the test file has any Windows skips
grep -n "skipIf\|skip.*[Ww]indows" test/regression/issue/tarball-integrity.test.ts || echo "No Windows skips found"Repository: oven-sh/bun
Length of output: 77
🏁 Script executed:
# Check if there's a describe or test.skip pattern at the top
head -30 test/regression/issue/tarball-integrity.test.tsRepository: oven-sh/bun
Length of output: 1385
Fix missing exit-code assertions and add error diagnostics for shell operations
The test scenarios are sound, but the second test has gaps in error handling that make failures harder to diagnose:
- Lines 82, 94 (
tarcommands for v1/v2 tarballs): MissingexitCodechecks. Unlike the first test'starcall (line 36), these don't verify success. Iftarfails or is unavailable, subsequent steps will fail with confusing diagnostics. - Line 146 (
rm -rfcleanup): NoexitCodeassertion. If cleanup fails silently, the secondbun installresult is unreliable. - No error logging: Unlike the
bun installfailures (lines 60–62), tar/rm failures won't log stderr, making it hard to distinguish between platform issues and actual integrity failures.
Add exitCode checks and error logging for both tar calls and the rm call in the second test, similar to the pattern used for bun install.
Additionally, consider whether this test should skip on Windows (where tar and rm may not be available or behave differently). Check your CI configuration to determine if Windows runs are expected.
🤖 Prompt for AI Agents
In test/regression/issue/tarball-integrity.test.ts around lines 21-166, the
second test is missing exitCode assertions and stderr logging for the tar
commands (v1/v2 at ~lines 82 and 94) and the cleanup rm -rf (around line 146),
which can hide failures; add checks that each Bun.spawnSync call returns
exitCode === 0 and, if not, log stdout/stderr like the existing bun install
block, for the two tar spawns and the rm spawn; mirror the pattern used earlier
to print installResult.stdout/stderr when non-zero; also consider skipping the
test on Windows when tar/rm are unavailable.
… quality - Move test from test/regression/issue/ to test/cli/install/ - Use tempDirWithFiles and pack helper from harness - Use fs/promises rm instead of spawning rm command - Remove setDefaultTimeout - Use replaceAll for lockfile modification - Add verification that lockfile was properly modified 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
test/cli/install/tarball-integrity.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
test/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts,jsx,tsx}: Write tests as JavaScript and TypeScript files using Jest-style APIs (test,describe,expect) and import frombun:test
Usetest.eachand data-driven tests to reduce boilerplate when testing multiple similar cases
Files:
test/cli/install/tarball-integrity.test.ts
test/cli/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
When testing Bun as a CLI, use the
spawnAPI frombunwith thebunExe()andbunEnvfromharnessto execute Bun commands and validate exit codes, stdout, and stderr
Files:
test/cli/install/tarball-integrity.test.ts
test/cli/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
For CLI command tests, use the
test/cli/directory (install, run, test, etc.)
Files:
test/cli/install/tarball-integrity.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: For single-file tests, prefer using-eflag overtempDir
For multi-file tests, prefer usingtempDirfromharnessandBun.spawnover other temporary directory creation methods
Always useport: 0for network tests and do not hardcode ports or use custom random port number functions
UsenormalizeBunSnapshotto normalize snapshot output of tests
Never write tests that check for no 'panic' or 'uncaught exception' or similar in the test output - that is NOT a valid test
UsetempDirfromharnessto create temporary directories, do not usetmpdirSyncorfs.mkdtempSync
When spawning processes in tests, check stdout/stderr expectations BEFORE checking exit code to get more useful error messages on test failure
Do not write flaky tests - do not usesetTimeoutin tests, insteadawaitthe condition to be met as you are testing the CONDITION not the TIME PASSING
Verify your test fails withUSE_SYSTEM_BUN=1 bun test <file>and passes withbun bd test <file>- your test is NOT VALID if it passes withUSE_SYSTEM_BUN=1
Files:
test/cli/install/tarball-integrity.test.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Usebun:testwith files that end in*.test.{ts,js,jsx,tsx,mjs,cjs}
Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Never use hardcoded port numbers in tests. Always useport: 0to get a random port
Prefer concurrent tests over sequential tests usingtest.concurrentordescribe.concurrentwhen multiple tests spawn processes or write files, unless it's very difficult to make them concurrent
When spawning Bun processes in tests, usebunExeandbunEnvfromharnessto ensure the same build of Bun is used and debug logging is silenced
Use-eflag for single-file tests when spawning Bun processes
UsetempDir()from harness to create temporary directories with files for multi-file tests instead of creating files manually
Prefer async/await over callbacks in tests
When callbacks must be used and it's just a single callback, usePromise.withResolversto create a promise that can be resolved or rejected from a callback
Do not set a timeout on tests. Bun already has timeouts
UseBuffer.alloc(count, fill).toString()instead of'A'.repeat(count)to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds
Usedescribeblocks for grouping related tests
Always useawait usingorusingto ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Always check exit codes and test error scenarios in error tests
Usedescribe.each()for parameterized tests
UsetoMatchSnapshot()for snapshot testing
UsebeforeAll(),afterEach(),beforeEach()for setup/teardown in tests
Track resources (servers, clients) in arrays for cleanup inafterEach()
Files:
test/cli/install/tarball-integrity.test.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to test/js/bun/**/*.test.{ts,tsx} : For Bun-specific API tests, use the `test/js/bun/` directory (for http, crypto, ffi, shell, etc.)
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-10-19T02:44:46.354Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: packages/bun-otel/context-propagation.test.ts:1-1
Timestamp: 2025-10-19T02:44:46.354Z
Learning: In the Bun repository, standalone packages under packages/ (e.g., bun-vscode, bun-inspector-protocol, bun-plugin-yaml, bun-plugin-svelte, bun-debug-adapter-protocol, bun-otel) co-locate their tests with package source code using *.test.ts files. This follows standard npm/monorepo patterns. The test/ directory hierarchy (test/js/bun/, test/cli/, test/js/node/) is reserved for testing Bun's core runtime APIs and built-in functionality, not standalone packages.
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>` - your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : When spawning Bun processes in tests, use `bunExe` and `bunEnv` from `harness` to ensure the same build of Bun is used and debug logging is silenced
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Add tests for new Bun runtime functionality
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/cli/**/*.{js,ts,jsx,tsx} : When testing Bun as a CLI, use the `spawn` API from `bun` with the `bunExe()` and `bunEnv` from `harness` to execute Bun commands and validate exit codes, stdout, and stderr
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/bundle.test.ts : Organize bundle tests in bundle.test.ts for tests concerning bundling bugs that only occur in DevServer
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Use `bun:test` with files that end in `*.test.{ts,js,jsx,tsx,mjs,cjs}`
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Ensure V8 API tests compare identical C++ code output between Node.js and Bun through the test suite validation process
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Unit tests for specific features are organized by module (e.g., `/test/js/bun/`, `/test/js/node/`)
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/**/*.{js,ts,jsx,tsx} : Write tests as JavaScript and TypeScript files using Jest-style APIs (`test`, `describe`, `expect`) and import from `bun:test`
Applied to files:
test/cli/install/tarball-integrity.test.ts
🧬 Code graph analysis (1)
test/cli/install/tarball-integrity.test.ts (1)
test/harness.ts (3)
tempDirWithFiles(259-266)pack(1304-1326)bunExe(102-105)
Per review feedback, tests should not assert absence of "panic" - they should rely on exit code and content assertions instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
test/cli/install/tarball-integrity.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
test/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts,jsx,tsx}: Write tests as JavaScript and TypeScript files using Jest-style APIs (test,describe,expect) and import frombun:test
Usetest.eachand data-driven tests to reduce boilerplate when testing multiple similar cases
Files:
test/cli/install/tarball-integrity.test.ts
test/cli/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
When testing Bun as a CLI, use the
spawnAPI frombunwith thebunExe()andbunEnvfromharnessto execute Bun commands and validate exit codes, stdout, and stderr
Files:
test/cli/install/tarball-integrity.test.ts
test/cli/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
For CLI command tests, use the
test/cli/directory (install, run, test, etc.)
Files:
test/cli/install/tarball-integrity.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: For single-file tests, prefer using-eflag overtempDir
For multi-file tests, prefer usingtempDirfromharnessandBun.spawnover other temporary directory creation methods
Always useport: 0for network tests and do not hardcode ports or use custom random port number functions
UsenormalizeBunSnapshotto normalize snapshot output of tests
Never write tests that check for no 'panic' or 'uncaught exception' or similar in the test output - that is NOT a valid test
UsetempDirfromharnessto create temporary directories, do not usetmpdirSyncorfs.mkdtempSync
When spawning processes in tests, check stdout/stderr expectations BEFORE checking exit code to get more useful error messages on test failure
Do not write flaky tests - do not usesetTimeoutin tests, insteadawaitthe condition to be met as you are testing the CONDITION not the TIME PASSING
Verify your test fails withUSE_SYSTEM_BUN=1 bun test <file>and passes withbun bd test <file>- your test is NOT VALID if it passes withUSE_SYSTEM_BUN=1
Files:
test/cli/install/tarball-integrity.test.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Usebun:testwith files that end in*.test.{ts,js,jsx,tsx,mjs,cjs}
Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Never use hardcoded port numbers in tests. Always useport: 0to get a random port
Prefer concurrent tests over sequential tests usingtest.concurrentordescribe.concurrentwhen multiple tests spawn processes or write files, unless it's very difficult to make them concurrent
When spawning Bun processes in tests, usebunExeandbunEnvfromharnessto ensure the same build of Bun is used and debug logging is silenced
Use-eflag for single-file tests when spawning Bun processes
UsetempDir()from harness to create temporary directories with files for multi-file tests instead of creating files manually
Prefer async/await over callbacks in tests
When callbacks must be used and it's just a single callback, usePromise.withResolversto create a promise that can be resolved or rejected from a callback
Do not set a timeout on tests. Bun already has timeouts
UseBuffer.alloc(count, fill).toString()instead of'A'.repeat(count)to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds
Usedescribeblocks for grouping related tests
Always useawait usingorusingto ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Always check exit codes and test error scenarios in error tests
Usedescribe.each()for parameterized tests
UsetoMatchSnapshot()for snapshot testing
UsebeforeAll(),afterEach(),beforeEach()for setup/teardown in tests
Track resources (servers, clients) in arrays for cleanup inafterEach()
Files:
test/cli/install/tarball-integrity.test.ts
🧠 Learnings (21)
📓 Common learnings
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to test/js/bun/**/*.test.{ts,tsx} : For Bun-specific API tests, use the `test/js/bun/` directory (for http, crypto, ffi, shell, etc.)
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-10-19T02:44:46.354Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: packages/bun-otel/context-propagation.test.ts:1-1
Timestamp: 2025-10-19T02:44:46.354Z
Learning: In the Bun repository, standalone packages under packages/ (e.g., bun-vscode, bun-inspector-protocol, bun-plugin-yaml, bun-plugin-svelte, bun-debug-adapter-protocol, bun-otel) co-locate their tests with package source code using *.test.ts files. This follows standard npm/monorepo patterns. The test/ directory hierarchy (test/js/bun/, test/cli/, test/js/node/) is reserved for testing Bun's core runtime APIs and built-in functionality, not standalone packages.
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>` - your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : When spawning Bun processes in tests, use `bunExe` and `bunEnv` from `harness` to ensure the same build of Bun is used and debug logging is silenced
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Add tests for new Bun runtime functionality
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/bundle.test.ts : Organize bundle tests in bundle.test.ts for tests concerning bundling bugs that only occur in DevServer
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/cli/**/*.{js,ts,jsx,tsx} : When testing Bun as a CLI, use the `spawn` API from `bun` with the `bunExe()` and `bunEnv` from `harness` to execute Bun commands and validate exit codes, stdout, and stderr
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Use `bun:test` with files that end in `*.test.{ts,js,jsx,tsx,mjs,cjs}`
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Ensure V8 API tests compare identical C++ code output between Node.js and Bun through the test suite validation process
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : Never write tests that check for no 'panic' or 'uncaught exception' or similar in the test output - that is NOT a valid test
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-10-26T01:32:04.844Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 24082
File: test/cli/test/coverage.test.ts:60-112
Timestamp: 2025-10-26T01:32:04.844Z
Learning: In the Bun repository test files (test/cli/test/*.test.ts), when spawning Bun CLI commands with Bun.spawnSync for testing, prefer using stdio: ["inherit", "inherit", "inherit"] to inherit stdio streams rather than piping them.
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : When spawning processes in tests, check stdout/stderr expectations BEFORE checking exit code to get more useful error messages on test failure
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-09-20T00:58:38.042Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always check exit codes and test error scenarios in error tests
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-09-20T00:57:56.685Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 22568
File: test/js/valkey/valkey.test.ts:268-276
Timestamp: 2025-09-20T00:57:56.685Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods like ctx.redis.set() - the maintainer has explicitly requested to stop looking at this error pattern.
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/ecosystem.test.ts : Organize ecosystem tests in ecosystem.test.ts for tests involving ensuring certain libraries are correct; prefer testing concrete bugs over testing entire packages
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Unit tests for specific features are organized by module (e.g., `/test/js/bun/`, `/test/js/node/`)
Applied to files:
test/cli/install/tarball-integrity.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/**/*.{js,ts,jsx,tsx} : Write tests as JavaScript and TypeScript files using Jest-style APIs (`test`, `describe`, `expect`) and import from `bun:test`
Applied to files:
test/cli/install/tarball-integrity.test.ts
🧬 Code graph analysis (1)
test/cli/install/tarball-integrity.test.ts (1)
test/harness.ts (3)
tempDirWithFiles(259-266)pack(1304-1326)bunExe(102-105)
🔇 Additional comments (2)
test/cli/install/tarball-integrity.test.ts (2)
1-15: LGTM: Imports and documentation are appropriate.The imports correctly use
bun:testand harness utilities, and the comment block clearly explains the test's purpose.
58-146: LGTM: Integrity verification test is well-designed.The test correctly simulates an integrity attack scenario by:
- Installing a tarball (v1) and capturing its integrity hash
- Modifying the lockfile to point to different content (v2) while preserving v1's integrity
- Cleaning cache/node_modules to force re-extraction
- Verifying the install fails with "Integrity check failed"
The assertion ordering is correct—checking stdout/stderr for the error message before checking the exit code, which provides better error messages on test failure.
As per coding guidelines, checking stdout/stderr expectations before exit code is the recommended approach.
- Mark tests as concurrent to run in parallel - Remove String() coercions since tempDirWithFiles returns string 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Make integrity hash optional for local_tarball and remote_tarball in lockfile parsing (may be missing in migrated lockfiles) - Only write integrity to lockfile when it's valid (supported tag) - Fix Windows path separator issue in test by normalizing paths to forward slashes when comparing against lockfile content - Update snapshot for plaintext lockfiles test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
This doesn't seem to fix the issue. Example:
Add local/remote tarball dependency, run install, edit integrity in lockfile to make it incorrect, delete node_modules, run install again -> installs successfully.
I would expect this to fail to install because the integrity in the lockfile does not match the integrity coming from the tarball.
|
Closing this PR because it has been inactive for more than 90 days. |
Summary
This PR adds integrity hash storage and verification for HTTP tarball and local tarball dependencies, bringing Bun's lockfile behavior in line with npm and Yarn.
Previously,
bun.lockdid not store integrity hashes for tarball URL dependencies (e.g.,"pkg": "https://example.com/pkg.tgz"). Now, Bun computes and stores SHA-512 integrity hashes for these dependencies, just like it already does for npm registry packages.Changes
bun.lockforremote_tarballandlocal_tarballpackagesExample
Before:
{ "packages": { "pkg": ["pkg@https://example.com/pkg.tgz", {}] } }After:
{ "packages": { "pkg": ["pkg@https://example.com/pkg.tgz", {}, "sha512-abc123...=="] } }Comparison with other package managers
Test plan
🤖 Generated with Claude Code