Repository navigation
fix(upgrade-plan): name the parent release that fixes a nested advisory (audit H1) - #981
Merged
Merged
Conversation
runyourempire
added a commit
that referenced
this pull request
Oct 18, 2026
…w of #981) B1: the raise of a declared parent's own target (express 4.20.0 -> 4.22.3) moves into osv::raised_lines, read by the Upgrade Plan, Preemption's alert (alert_fix_target now takes the raised lines) and the Brief's facts. S2: every declared dependency a transitive parent arrives through is named (up to 3, "and N more"), from a walk to the first declared package on every path (osv::parent_route, the MCP's findRoute); a path ending at an undeclared package makes the lever unknown and names no command. S4: a declared lever's release agrees with its own row: its row is raised by what the parents it requires pin (lever_needs), and a hop lifts to it when that release still brings a fixed parent in (osv::parent_release_hop). S5: graph-mode rule vectors run the whole pipeline (matcher, raised lines, load_routes, hops, raised_row_target) from RAW npm documents: pr-007 (NodeGoat incl. serve-static/send via express), pr-008 (glob's whole deprecated 11.x keeps 11.1.0 as the target; @nestjs/cli 11.0.11). Parity: a parent's "newest release" is the highest stable release that is not withdrawn and clear of its stored advisories; newest() for own-advisory checks is the highest installable release, not the latest tag; the NoneAdmits sentences name no second release. The persisted-plan vitest shape (own row, a child needing more, a child nothing admits) names vitest 4.1.11 on every row, the alert and the Brief. Nits: the override option is back on dead ends; pre-releases are not kept as parent releases; the stored document is parsed once per build; hop, admits_move and parent_move are split. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
runyourempire
force-pushed
the
fix/h1-parent-release
branch
from
October 18, 2026 09:12
af48c7b to
f8883b4
Compare
…ry (audit H1) A vulnerable transitive copy whose parent pins it said "upgrade express (check that it admits path-to-regexp >= 0.1.13)" and stopped. The registry already knew the answer. The plan, Preemption and the Brief now name it, by the rules of @4da/mcp-server fix/planner-advice-625 (1840dab): - the smallest parent release above the installed one whose own requirement resolves to a clean copy at or above the target, clear of the parent's own advisories (osv/parent_release.rs, pure); - one release per dependency: a parent with its own vulnerable row has that row raised to the highest release the transitive rows need from it (no major crossed, no own advisory affects it), and a lower row is lifted to it when it still admits a clean copy; - the refresh after the install when the new requirement still admits the installed copy (npm 10.9.2 keeps it); - a parent that cannot be tied to a declared dependency gets its release but no command; a command goes through osv::direct_upgrade (declared operator kept); a major move is flagged; - a parent reached through a declared dependency names that dependency's release that brings a fixed parent in (adjacent lever). Data: the npm abbreviated document #976 already reads now also keeps, for the parents that pin a vulnerable copy (and their declared lever), each release's requirement on that package plus the latest tag. Same request, same cap, cache, source toggle; NETWORK.md says so. npm only. NodeGoat (express 4.16.4): the express, path-to-regexp and qs rows all name express 4.22.3 (qs forces it over 4.22.0). New shared vector kind parent_release (pr-001..pr-006), pinned to live registry and OSV data of 2026-10-18, run against the real functions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
…w of #981) B1: the raise of a declared parent's own target (express 4.20.0 -> 4.22.3) moves into osv::raised_lines, read by the Upgrade Plan, Preemption's alert (alert_fix_target now takes the raised lines) and the Brief's facts. S2: every declared dependency a transitive parent arrives through is named (up to 3, "and N more"), from a walk to the first declared package on every path (osv::parent_route, the MCP's findRoute); a path ending at an undeclared package makes the lever unknown and names no command. S4: a declared lever's release agrees with its own row: its row is raised by what the parents it requires pin (lever_needs), and a hop lifts to it when that release still brings a fixed parent in (osv::parent_release_hop). S5: graph-mode rule vectors run the whole pipeline (matcher, raised lines, load_routes, hops, raised_row_target) from RAW npm documents: pr-007 (NodeGoat incl. serve-static/send via express), pr-008 (glob's whole deprecated 11.x keeps 11.1.0 as the target; @nestjs/cli 11.0.11). Parity: a parent's "newest release" is the highest stable release that is not withdrawn and clear of its stored advisories; newest() for own-advisory checks is the highest installable release, not the latest tag; the NoneAdmits sentences name no second release. The persisted-plan vitest shape (own row, a child needing more, a child nothing admits) names vitest 4.1.11 on every row, the alert and the Brief. Nits: the override option is back on dead ends; pre-releases are not kept as parent releases; the stored document is parsed once per build; hop, admits_move and parent_move are split. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
…g (review of #981, round 3) B2: osv::parent_release::Advisories carries each advisory's id and a malware flag (MAL- id or alias) and holds THE predicate every reader shares: newest() is the highest stable release no malicious record affects; usable() rules out malware and any advisory with a published fix, while an advisory no release fixes does not block a move and is reported on the release named ("remains affected by <id> (no fix published)"). An advisory whose ranges cannot be read makes the decision undecided (no release, no command, the reason said). An installed malicious copy, parent or lever is to be removed or replaced, never moved to. parent_standing's "newest release" uses the same predicate. B1: a lever lifted to its own row keeps its own smallest release in `smallest` and the parent's admitting release in a new `floor` field: "express 4.21.0 is the smallest express release that brings in a serve-static that admits it, and express 4.22.3, the release express's own upgrade moves to, also brings in serve-static 1.16.3 (>= 1.16.1)". pr-007/pr-008 release objects carry `floor`. Also: the Signal item's fix agrees with the plan (security_verdict raises a declared package's fix like the plan row); the release-facts generation bumps after the write and the 60 s cache is tested with an injected clock and generation; the test helpers move out of release_facts.rs (641 lines); the lever walk counts the project's own requirement edge with the other parents and says "lever not determined (dependency graph too large)" when it stops at its bound. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
runyourempire
force-pushed
the
fix/h1-parent-release
branch
from
October 18, 2026 10:24
f8883b4 to
2e59ea9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Audit H1. When a vulnerable transitive dependency can only be fixed by moving its parent, the app names the exact parent release that fixes it. One release per dependency appears on every row and every surface: the Upgrade Plan, Preemption and the Brief. The rules follow
@4da/mcp-server(upgrade-planner-fixpaths.ts, fix/planner-advice-625):osv::direct_upgrade, so the declared operator is kept.Doctrine: the output stays inside
EvidenceItem(explanation text plus adependency-pathcitation).ParentStandinggainsAdmitsandNoneAdmits.Review of #981, addressed
osv::raised_lines::raised_line_targets, which all three surfaces read: the plan, Preemption'salert_fix_target(it now takes the raised lines) and the Brief's facts.osv::parent_routereplacesdirect_through.osv::parent_release_hop).mode: "graph"vectors run the full pipeline from RAW npm documents, with deprecation notices kept and nothing pre-filtered:latesttag. NoneAdmits sentences name no second release.hop,admits_moveandparent_moveare split.Review round 3, addressed
osv::parent_release::Advisories). The same predicate is used bynewest(), the parent's own-advisory check, the child copy's check and the parent's "newest release" text.MAL-id or alias) that affects a release rules it out: never clean, never a target, never "the newest". An installed malicious copy, parent or declared dependency is to be removed or replaced, never moved to.smallestand the parent's admitting release in a newfloorfield.floor.security_verdictraises a declared package's fix the same way as the plan row).release_facts.rsis down to 641 lines (the test helpers moved out);Before / after: NodeGoat (express 4.16.4, path-to-regexp 0.1.7, qs 6.5.2)
Before (path-to-regexp row):
After:
The express row, the express alert and the Brief now say 4.22.3, not 4.20.0. qs is what forces 4.22.3 over 4.22.0. This matches the reference engine run on the real NodeGoat lockfile, whose express is 4.16.4.
Data / network
There is no new request. The npm abbreviated document that #976 already fetches now also keeps:
latesttag;The cap, cache and npm source toggle are unchanged, and only names are sent. NETWORK.md is updated and
check-privacy-egresspasses. Scope is npm only; crates.io would need per-version dependency data the app does not read.Tests
cargo test --lib -- osv:: evidence:: brief_facts preemption::(plusscoring::security_verdict) on the rebased head 2e59ea9: 572 passed, 0 failed.cargo clippy --lib -D warningsand fmt are clean; file sizes are within limits.Not verified live in the running app. fourda.exe was not started. The live check: run an OSV sync, then look at the NodeGoat path-to-regexp and express rows.
Shared-file notes:
brief_facts_copies.rs: one test-helper line (lever_unknown: None);preemption.rs: thealert_fix_targetsignature (a peer has a claim on this file);brief_facts.rsis smaller than before.🤖 Generated with Claude Code
https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7