Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a ChangesPackageVersionGuard mechanism in pacquet resolvers
OSV vulnerability index integration in pnpr
Sequence Diagram(s)sequenceDiagram
participant Client
participant Server as serve_resolve
participant Resolver
participant StreamObserver
participant PickGuard as pick_from_registry_with_guard
participant OsvIndex
Client->>Server: POST /v1/resolve
Server->>Resolver: get_or_init~config, osv_index~
Resolver->>StreamObserver: new~package_version_guard = osv_index~
rect rgba(100, 150, 255, 0.5)
Note over Resolver,OsvIndex: Frozen-lockfile fast path
Resolver->>OsvIndex: vulnerability_ids per locked package
alt violations found
Resolver-->>Client: violations NDJSON frame
end
end
rect rgba(100, 200, 100, 0.5)
Note over Resolver,PickGuard: Streaming resolve with guard
Resolver->>PickGuard: pick_from_registry_with_guard~opts~
loop until allowed or blocked loop detected
PickGuard->>PickGuard: pick_package~blocked_versions~
PickGuard->>OsvIndex: check~name, version~
OsvIndex-->>PickGuard: Allow | Reject~vuln-id~
alt Reject
PickGuard->>PickGuard: add to blocked_versions, retry
else Allow
PickGuard-->>Resolver: Ok~Some~picked~~
end
end
Resolver->>OsvIndex: re-check final resolved lockfile
alt violations remain
Resolver-->>Client: violations frame
else clean
Resolver-->>Client: done frame
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Micro-Benchmark ResultsLinux |
Integrated-Benchmark Report (Linux)Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.77929627388,
"stddev": 0.17592110392384822,
"median": 3.71520301438,
"user": 3.5873603000000003,
"system": 3.2935554600000003,
"min": 3.5447367358800004,
"max": 4.03794142788,
"times": [
3.9412288928800003,
3.5447367358800004,
3.7415845768800002,
3.6506903208800003,
4.00563084888,
4.03794142788,
3.68882145188,
3.89828604288,
3.61778998688,
3.66625245388
]
},
{
"command": "pacquet@main",
"mean": 3.72330693588,
"stddev": 0.140212723346024,
"median": 3.73274631088,
"user": 3.6242045999999988,
"system": 3.2430264600000003,
"min": 3.4748588548800003,
"max": 3.97229639188,
"times": [
3.4748588548800003,
3.78534334788,
3.76808983488,
3.68418409488,
3.97229639188,
3.6240155248800003,
3.69740278688,
3.8110350838800002,
3.8243910368800003,
3.5914524018800003
]
},
{
"command": "pnpr@HEAD",
"mean": 2.19650243038,
"stddev": 0.12694122913890757,
"median": 2.22590151088,
"user": 2.7356004999999994,
"system": 2.8479946600000003,
"min": 1.9695319638800002,
"max": 2.36932296788,
"times": [
2.09847719888,
2.27697503488,
2.27723521788,
2.03186474388,
2.24448454188,
2.3033481738800003,
2.20731847988,
1.9695319638800002,
2.18646598088,
2.36932296788
]
},
{
"command": "pnpr@main",
"mean": 2.16024535598,
"stddev": 0.1471414124046399,
"median": 2.1479110068800003,
"user": 2.7176210999999997,
"system": 2.86421486,
"min": 1.9386273048800002,
"max": 2.37317107788,
"times": [
2.21658402488,
2.37317107788,
2.12952230588,
2.06268301288,
2.3274530108800002,
2.16629970788,
2.31633618988,
1.9386273048800002,
2.07759214288,
1.99418478188
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6387057114,
"stddev": 0.008740309327898921,
"median": 0.6360895310000001,
"user": 0.37119453999999996,
"system": 1.3188551400000001,
"min": 0.6303576725000001,
"max": 0.6609313635,
"times": [
0.6333812975,
0.6398334395,
0.6390009145000001,
0.6609313635,
0.6341864685,
0.6333317795000001,
0.6438551165,
0.6376145765000001,
0.6345644855,
0.6303576725000001
]
},
{
"command": "pacquet@main",
"mean": 0.6386409484000001,
"stddev": 0.014719587116125744,
"median": 0.6429621240000001,
"user": 0.37120704000000004,
"system": 1.32222334,
"min": 0.6170171625,
"max": 0.6582654195000001,
"times": [
0.6170171625,
0.6257712805000001,
0.6541208875000001,
0.6487979235000001,
0.6411653525000001,
0.6447588955000001,
0.6317173595000001,
0.6582654195000001,
0.6468746605000001,
0.6179205425000001
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6944316351000002,
"stddev": 0.038046613770136274,
"median": 0.6875403725,
"user": 0.38684614000000006,
"system": 1.3455187400000002,
"min": 0.6523991645,
"max": 0.7903572155,
"times": [
0.7158559725000001,
0.6936268005,
0.6813348615,
0.6878290435000001,
0.6590365785000001,
0.6899616245000001,
0.6523991645,
0.6866633885000001,
0.6872517015,
0.7903572155
]
},
{
"command": "pnpr@main",
"mean": 0.6920508202000001,
"stddev": 0.015064247773236495,
"median": 0.6930731655,
"user": 0.38564403999999997,
"system": 1.35543784,
"min": 0.6680630755000001,
"max": 0.7110352775000001,
"times": [
0.7027929095000001,
0.6740710635,
0.7045079585,
0.6930677035,
0.6745400285,
0.6930786275,
0.7062984615000001,
0.6680630755000001,
0.6930530965,
0.7110352775000001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.201662724319999,
"stddev": 0.05039624538164557,
"median": 4.21111332182,
"user": 3.7807196,
"system": 3.302732,
"min": 4.0934825763200005,
"max": 4.27129733732,
"times": [
4.24274864932,
4.20864786932,
4.15916231132,
4.0934825763200005,
4.19190182432,
4.21357877432,
4.24342122832,
4.17719187432,
4.27129733732,
4.215194798320001
]
},
{
"command": "pacquet@main",
"mean": 4.2170165075199995,
"stddev": 0.03898269463677993,
"median": 4.20519594082,
"user": 3.7652476,
"system": 3.2986818999999996,
"min": 4.16972120832,
"max": 4.29130182732,
"times": [
4.29130182732,
4.24119373132,
4.24873525832,
4.20708436632,
4.24883472632,
4.203307515320001,
4.18482242632,
4.19904633532,
4.17611768032,
4.16972120832
]
},
{
"command": "pnpr@HEAD",
"mean": 2.12387479432,
"stddev": 0.11803726143039499,
"median": 2.1320591658199994,
"user": 2.5640123000000004,
"system": 2.7974074,
"min": 1.9787571733200002,
"max": 2.29128264432,
"times": [
1.9787571733200002,
2.19876861932,
2.28112415432,
2.02427112532,
2.0855931863199997,
2.1848457773199996,
2.02938139632,
2.1785251453199996,
2.29128264432,
1.98619872132
]
},
{
"command": "pnpr@main",
"mean": 2.2290029364199997,
"stddev": 0.15380304271650982,
"median": 2.17120579182,
"user": 2.5728108,
"system": 2.8068101,
"min": 2.01690745232,
"max": 2.44529160832,
"times": [
2.3348821593199998,
2.1865079663199998,
2.4241143353199996,
2.3765821783199996,
2.15590361732,
2.01690745232,
2.05889164132,
2.14955554932,
2.44529160832,
2.14139285632
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.35742015306,
"stddev": 0.01604640374790457,
"median": 1.3563179115600001,
"user": 1.3226398199999998,
"system": 1.7310781,
"min": 1.33572261156,
"max": 1.38241917856,
"times": [
1.38241917856,
1.3471921255600001,
1.36366268356,
1.34897313956,
1.37086250956,
1.37260953456,
1.36858298656,
1.3415565445600002,
1.33572261156,
1.34262021656
]
},
{
"command": "pacquet@main",
"mean": 1.37404683496,
"stddev": 0.029139093611300418,
"median": 1.36691721356,
"user": 1.33908122,
"system": 1.7249868,
"min": 1.32992000256,
"max": 1.43222951556,
"times": [
1.32992000256,
1.36486145556,
1.40575452456,
1.43222951556,
1.36082152056,
1.35159845256,
1.36897297156,
1.3605117795600001,
1.39225657456,
1.37354155256
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6888770649600001,
"stddev": 0.06860446679149358,
"median": 0.66900654956,
"user": 0.33756781999999996,
"system": 1.2846052999999997,
"min": 0.6519194635600001,
"max": 0.8822440905600001,
"times": [
0.6605356585600001,
0.6519194635600001,
0.8822440905600001,
0.6756916435600001,
0.6796925635600001,
0.66440330956,
0.6802172355600001,
0.6688919855600001,
0.66912111356,
0.65605358556
]
},
{
"command": "pnpr@main",
"mean": 0.6699981473600001,
"stddev": 0.056640057971595555,
"median": 0.6523050635600001,
"user": 0.33277782,
"system": 1.2804619000000002,
"min": 0.64630375356,
"max": 0.83065780456,
"times": [
0.65207850456,
0.64630375356,
0.6499056165600001,
0.6525316225600001,
0.64890716656,
0.6590957155600001,
0.6465367485600001,
0.6540619065600001,
0.6599026345600001,
0.83065780456
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.0307558831800003,
"stddev": 0.03916782819931989,
"median": 3.0254025243799996,
"user": 1.72666182,
"system": 2.0250726400000003,
"min": 2.97041617788,
"max": 3.12458824588,
"times": [
3.01924826588,
3.02719812388,
3.0483988808799998,
3.03811272988,
3.0236069248799997,
3.00693599488,
3.01572060788,
2.97041617788,
3.12458824588,
3.03333287988
]
},
{
"command": "pacquet@main",
"mean": 3.06732448228,
"stddev": 0.039937548653473674,
"median": 3.06500473338,
"user": 1.75336882,
"system": 2.02350644,
"min": 3.01199282288,
"max": 3.15153498188,
"times": [
3.01199282288,
3.06196011888,
3.0936191428799997,
3.0680493478799997,
3.02249764488,
3.06026411788,
3.07829049988,
3.0374620598799997,
3.15153498188,
3.08757408588
]
},
{
"command": "pnpr@HEAD",
"mean": 0.65535373638,
"stddev": 0.01073436687485528,
"median": 0.6565608768800001,
"user": 0.32975132,
"system": 1.26738594,
"min": 0.6431016578800001,
"max": 0.6714833658800001,
"times": [
0.6609377888800001,
0.6637672948800001,
0.6431016578800001,
0.6451853248800001,
0.6624561638800001,
0.64465164888,
0.6714833658800001,
0.6437961358800001,
0.6521839648800001,
0.6659740178800001
]
},
{
"command": "pnpr@main",
"mean": 0.6580434983800001,
"stddev": 0.01853696362203864,
"median": 0.65181942788,
"user": 0.34061832,
"system": 1.26261454,
"min": 0.6445071358800001,
"max": 0.7072098118800001,
"times": [
0.6486913858800001,
0.65148201388,
0.64894444888,
0.7072098118800001,
0.6497614608800001,
0.6445071358800001,
0.6698363998800001,
0.6550239338800001,
0.65282155088,
0.6521568418800001
]
}
]
} |
|
| Branch | pr/12506 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,201.66 ms(-0.67%)Baseline: 4,230.16 ms | 5,076.20 ms (82.77%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 3,030.76 ms(+0.87%)Baseline: 3,004.53 ms | 3,605.43 ms (84.06%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,357.42 ms(+1.69%)Baseline: 1,334.86 ms | 1,601.83 ms (84.74%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 3,779.30 ms(-8.19%)Baseline: 4,116.29 ms | 4,939.55 ms (76.51%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 638.71 ms(+1.80%)Baseline: 627.43 ms | 752.91 ms (84.83%) |
|
| Branch | pr/12506 |
| Testbed | pnpr |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 2,123.87 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 655.35 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 688.88 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 2,196.50 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 694.43 ms |
PR Summary by QodoAdd local OSV-based vulnerability blocking during npm resolution Description
Diagram
High-Level Assessment
Files changed (21)
|
Code Review by Qodo
1. Tarball version check bypass
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #12506 +/- ##
==========================================
- Coverage 88.14% 87.92% -0.23%
==========================================
Files 314 315 +1
Lines 43559 44138 +579
==========================================
+ Hits 38397 38809 +412
- Misses 5162 5329 +167 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pacquet/crates/package-manager/src/add.rs (1)
411-428:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftApply the version guard before serializing
add’s manifest specifier.This direct
pick_packagepath always usesblocked_versions: None. In an OSV-enabled add flow, a user range likeacme@^1.0.0can pre-pick a rejected1.1.0and serialize^1.1.0; the later guarded install then cannot repick a safe1.0.0that satisfied the original range. Thread the samePackageVersionGuardinto this pre-resolution path, or route this pick through the guarded retry loop beforepicked.serialize(pin).Based on PR objectives, the resolver-time guard is intended to reject vulnerable versions while preserving normal repick semantics.
🤖 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 `@pacquet/crates/package-manager/src/add.rs` around lines 411 - 428, The PickPackageOptions struct passed to pick_package has blocked_versions hardcoded to None, which prevents vulnerable versions from being blocked during the picking phase. Update the blocked_versions field in the PickPackageOptions initialization to pass the appropriate PackageVersionGuard information instead of None, ensuring that version rejection is applied before the spec is serialized. This will ensure that rejected/vulnerable versions are not picked and serialized into the manifest, preventing the issue where later guarded install processes cannot repick valid versions that satisfy the original user-specified range.
🤖 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.
Inline comments:
In `@pacquet/crates/resolving-npm-resolver/src/npm_resolver/tests.rs`:
- Around line 235-251: The test function
package_version_guard_repopulates_latest_tag claims to validate latest-tag
repopulation based on its name and intent, but the current assertion only
verifies the repicked version through
result.name_ver.as_ref().expect("name_ver").suffix.to_string(). Add an explicit
assertion on the result.latest field to actually verify that the latest tag was
repopulated, ensuring the test validates the behavior it claims to test and will
fail if latest-tag rewriting regresses.
In `@pacquet/crates/resolving-npm-resolver/src/pick_package_from_meta.rs`:
- Around line 415-418: The documentation comment for the dist-tag repopulation
function incorrectly states that removed tag targets are rewritten "in the same
family," but the actual implementation at line 472 only applies the major-family
guard to non-latest tags while allowing the latest tag to move across major
versions. Update the doc comment to accurately reflect this behavior by
clarifying that tags whose target was removed are rewritten to the best
remaining version, with the same-family constraint applying only to non-latest
tags, while latest tags can move across major versions to find the best
remaining version.
In `@pnpr/crates/pnpr/src/resolver.rs`:
- Around line 283-292: The code performs an expensive O(packages) OSV scan on
every in-memory resolution-cache hit via osv_violations_for_lockfile even though
the runtime.osv_index is immutable and cached lockfiles are only stored after
passing OSV checks. Remove the unnecessary OSV re-scan in this warm cache path
by eliminating the osv_clean check and its associated
osv_violations_for_lockfile call, since a cache hit already implies the lockfile
is OSV-clean. Simply return the ndjson_single_frame with done_frame directly
without performing the OSV verification again.
In `@pnpr/crates/pnpr/src/resolver/osv.rs`:
- Around line 313-346: The functions `semver_event_from_osv`,
`semver_range_from_osv`, and `parse_osv_version` currently use `Option` types
with `filter_map`, which silently discards invalid OSV data instead of failing.
Change these functions to return `Result` types that propagate errors instead of
silently filtering invalid events. Specifically, update `parse_osv_version` to
return a Result, then modify `semver_event_from_osv` to return a Result and
propagate parse errors from the introduced, fixed, last_affected, and limit
fields. Update `semver_range_from_osv` to return a Result and replace the
`filter_map` with a Result-aware operation that fails if any event is invalid.
Finally, propagate the Result type through the calling functions in
`ingest_record_bytes` so that malformed OSV data returns an appropriate error
instead of silently being ignored.
- Around line 228-232: The entry.read_to_end() call in the OSV zip entry reading
block does not enforce an upper bound on the actual amount of data read, only
initial capacity allocation. Add a maximum size limit (such as 1MB or
appropriate for your use case) and enforce it before or during the read
operation. Either check the entry's reported size against your limit and reject
oversized entries before attempting to read them, or wrap the reader with take()
to limit the bytes read to the maximum allowed. This prevents memory and CPU
exhaustion from maliciously crafted or oversized entries in attacker-controlled
OSV zip files.
---
Outside diff comments:
In `@pacquet/crates/package-manager/src/add.rs`:
- Around line 411-428: The PickPackageOptions struct passed to pick_package has
blocked_versions hardcoded to None, which prevents vulnerable versions from
being blocked during the picking phase. Update the blocked_versions field in the
PickPackageOptions initialization to pass the appropriate PackageVersionGuard
information instead of None, ensuring that version rejection is applied before
the spec is serialized. This will ensure that rejected/vulnerable versions are
not picked and serialized into the manifest, preventing the issue where later
guarded install processes cannot repick valid versions that satisfy the original
user-specified range.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 390e8d78-72e8-4995-aa85-208904e34823
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock,!Cargo.lock
📒 Files selected for processing (21)
pacquet/crates/package-manager/src/add.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/package-manager/src/resolution_observer.rspacquet/crates/resolving-npm-resolver/src/named_registry_resolver.rspacquet/crates/resolving-npm-resolver/src/npm_resolver.rspacquet/crates/resolving-npm-resolver/src/npm_resolver/tests.rspacquet/crates/resolving-npm-resolver/src/pick_package.rspacquet/crates/resolving-npm-resolver/src/pick_package/tests.rspacquet/crates/resolving-npm-resolver/src/pick_package_from_meta.rspacquet/crates/resolving-resolver-base/src/lib.rspacquet/crates/resolving-resolver-base/src/resolve.rspnpr/crates/pnpr/Cargo.tomlpnpr/crates/pnpr/src/config.rspnpr/crates/pnpr/src/config/tests.rspnpr/crates/pnpr/src/lib.rspnpr/crates/pnpr/src/main.rspnpr/crates/pnpr/src/resolver.rspnpr/crates/pnpr/src/resolver/osv.rspnpr/crates/pnpr/src/resolver/osv/tests.rspnpr/crates/pnpr/src/resolver/tests.rspnpr/crates/pnpr/src/server.rs
|
Code review by qodo was updated up to the latest commit e27a587 |
Review triage (CodeRabbit + Qodo)Pushed four follow-up commits and replied on each inline thread. Summary: Fixed
Declined (rationale on each thread)
Acknowledged follow-up (out of scope here)
Written by an agent (Claude Code, claude-opus-4-8). |
|
Code review by qodo was updated up to the latest commit c5ff937 |
|
Code review by qodo was updated up to the latest commit c5ff937 |
|
Code review by qodo was updated up to the latest commit 8c9615d |
|
Code review by qodo was updated up to the latest commit 8c9615d |
|
Code review by qodo was updated up to the latest commit ef2c499 |
|
Code review by qodo was updated up to the latest commit 293ef9a |
`load_from_directory` checked `entry_path.is_file()` on the path, then opened and read it — a concurrent swap to a FIFO/socket in that window could make `read_to_end` block startup. Verify the opened handle's metadata is a regular file before reading. Written by an agent (Claude Code, claude-opus-4-8).
Each guard rejection re-runs the picker over the packument, so an unbounded run is O(versions²). Cap rejections at 1000 — far beyond any realistic run of consecutive blocked versions — so a hostile packument can't turn the guard loop into a resolver-time CPU/memory spike; hitting the cap surfaces as "no acceptable version". Written by an agent (Claude Code, claude-opus-4-8).
|
Code review by qodo was updated up to the latest commit 0fbc966 |
|
Code review by qodo was updated up to the latest commit 0fbc966 |
Two untrusted strings from the OSV database were unbounded: - zip entry names were cloned into errors/fingerprint with no length check, so a crafted zip could force large startup allocations. Reject entry names over 4 KiB. - advisory ids (up to the per-record byte cap) were stored verbatim and fed into guard/violation reason strings, amplifying memory and NDJSON payloads. Truncate stored ids to 256 bytes (the count cap already bounds how many appear in a reason). Written by an agent (Claude Code, claude-opus-4-8).
|
Code review by qodo was updated up to the latest commit c6be244 |
|
Code review by qodo was updated up to the latest commit c6be244 |
`load_from_directory` checked `is_file()` on the path and then opened it; a concurrent swap of the path to a FIFO after the check made the open itself (O_RDONLY) block until a writer appeared, hanging pnpr before it binds its listen socket. Open with `O_NONBLOCK` on unix so the open can't block, then keep the existing regular-file check on the handle before reading. Written by an agent (Claude Code, claude-opus-4-8).
|
Code review by qodo was updated up to the latest commit 902b75d |
|
Code review by qodo was updated up to the latest commit 902b75d |
…er repick-cap error - Reject OSV `affected` entries whose `versions`/`ranges`/`events` counts exceed generous limits, so a crafted record can't expand into a huge persistent set in the index despite the per-record byte cap. - When the guard re-pick safety cap is hit, return a dedicated `GuardRepickLimitError` instead of `AllVersionsBlockedError` — the cap is a cutoff, not proof that every matching version is blocked, so the message now says so. Written by an agent (Claude Code, claude-opus-4-8).
|
Code review by qodo was updated up to the latest commit 5e733b4 |
…ross-check `tarball_url_version` only recognized a lowercase `.tgz` suffix, so under `trustLockfile` a tampered lockfile could point the tarball at a vulnerable artifact named `.TGZ` or `.tar.gz` and skip the URL-version OSV cross-check. Strip `.tgz`/`.tar.gz` case-insensitively. Written by an agent (Claude Code, claude-opus-4-8).
|
Code review by qodo was updated up to the latest commit 8f20d3e |
|
Code review by qodo was updated up to the latest commit 8f20d3e |
Summary
Adds optional pnpr OSV protection backed by a local npm OSV dump instead of live API calls in the resolver hot path.
osv.enabled/osv.pathconfig and--osv/--osv-dbCLI flagsall.zipor extracted JSON directory at startup and fails closed when enabled but the database is missing, not a regular file, or contains no npm advisoriesgitHostedflagtry_router/try_router_with_authconstructors so embedders can surface an invalid OSV database as a recoverable error instead of a panicNo TypeScript CLI port is included: pnpr has no pnpm-CLI counterpart, and the pacquet-side resolver guard is the infrastructure pnpr uses, so both sides live here.
Squash Commit Body
Checklist
pacquet/port, or the description notes what still needs porting.Written by an agent (Codex, GPT-5); description updated by an agent (Claude Code, claude-opus-4-8).
Summary by CodeRabbit
--osvto enable scanning and--osv-dbto specify the OSV database path.