Repository navigation
fix: check announced releases against advisories (hold, no Run toward malware); workspace members' deps are direct - #983
Merged
Conversation
…aming a command toward it A newly published malicious (OSV MAL- / GitHub malware) or advisory-affected minor inside a declared range was announced in the stack-change stream with a ready-to-run `npm update`, the command that installs it; the Brief's upgrade facts had the same blind spot (release_grade never consults advisories). osv/release_advisories.rs checks the release itself with the predicate behind every "this version is clear" (check_version_affected over the package's stored advisories, conservative on unreadable windows), never a registry- withdrawn release (#976), and an all-clear only from a mirror that is ready (osv::readiness) and synced after the release was published; otherwise the status is unknown. Stack-change stream: affected -> no command, title/explanation name the advisories (malware: "do not install", urgency High, says a refresh would pull it in when the range admits it), osv citations; when every affecting advisory already hits the installed copy the item is left to the advisory lane; withdrawn / unknown -> no command, said so. Brief: affected or withdrawn releases are not presented as upgrades. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
The JavaScript direct set read only the lockfile directory's own
package.json, so a dependency only an npm/yarn/pnpm workspace member declared
(runtime or dev) was stored transitive and the parent walk (parent_hint stops
at the first direct copy) found no lever for anything under it.
direct_names now adds every member's dependencies + devDependencies, members
found from the root `workspaces` (array or yarn's {packages: [...]}) and
pnpm-workspace.yaml `packages:` (literal !negations honoured), via the new
ace::npm_local::workspace_member_dirs. Member packages themselves (local
code) and protocol specs are excluded. Rows stay on the workspace's lockfile
project (no declared-in column); single-root projects are unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
…ies, never drop malware (#983 review) B1: a clean release younger than RELEASE_HOLD_DAYS (3, pinned to pnpm-workspace.yaml minimumReleaseAge 4320 and >= the trust horizon) is Held: no command, "published N days ago; 4DA names no command for a release younger than 3 days, so a compromised publish can be caught and pulled first". An undated release is Unknown ("the registry gave no publish date"), never Clear. A bare date ages from 23:59:59 (S6). B2: "covered by the advisory lane" is judged on the ACTIVE sites' copies (every active copy confirmed-affected by every hit) and never drops a malicious release. S3: an affected dev-only minor is announced; `admitted` is read for dev pins from the project's own devDependencies (commands stay runtime-only). S4: workspace members' manifests are read for the refresh warning. S7: any undecodable advisory row makes the read unknown (no silent skip). S8: an unreadable mirror has its own reason. S9: release_item split into explanation / citations / actions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
…ackages (#983 review) `!**/test/**`-style negations now exclude every member directory they match (an excluded package is not a member, so its declarations are not direct), and pnpm-workspace.yaml `packages: ['a/*', ...]` is read. `dir/**` stays one level deep, documented. 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/release-advisory-gate
branch
from
October 18, 2026 10:16
781de6c to
98510ae
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.
Summary
Two defects, fixed in two commits, plus two commits that address the review.
A (HIGH, security): a release is checked against advisories before it is announced or offered a command
Until now
scoring::release_gradenever asked whether the announced release was safe to move to, and the advisory lanes only check the copies a project already runs. So a newly published malicious minor (OSVMAL-, or a GitHub malware advisory) inside a project's declared range showed up in the stack-change stream with a ready-to-runnpm update <pkg>, which is the command that installs it. The Brief's upgrade facts had the same blind spot.New module
osv/release_advisories.rs(ReleaseChecker) checks the announced release itself, in this order:parent_hint'sis_clear): every stored, non-withdrawn advisory for the package must leave the version outside its window. A window the matcher cannot read counts as affected. An affecting advisory is reported even when the mirror is stale.release_facts: yanked, or npm-deprecated with a later clean release.osv::readiness::classify: no successful sync inside the 72 h trust horizon, failed, or still syncing). Also unknown when the advisory rows cannot be read: any row error makes the whole read unknown, never a silent skip. Also unknown when the registry gave no publish date.RELEASE_HOLD_DAYS = 3. A bare date counts from its last second, not from midnight.pnpm-workspace.yamlminimumReleaseAge: 4320(a test pins it) and the MCP server'sWAIT_DAYS.run_command.Package names are matched the way the matcher does it (
package_key, plus crates.io treating-and_as the same name).What the stack-change stream does with each verdict:
run_command. The title becomespkg 4.2.0: malicious release, do not install(oraffected by <ids>). The explanation names the advisory ids. Where the declared range admits the release, it also says that a lockfile refresh or an install without the lockfile would pull it in. The item getsosvcitations and areview_security"Read the advisory" action. Malware is urgency High.devDependencies.release_itemis split intorelease_explanation,release_citationsandrelease_actions.The Brief (
brief_facts_build.rs, a 6-line hunk): an upgrade fact whose release is affected or withdrawn is dropped.UpgradeFactinbrief_facts.rs, which is at 997 of 1000 lines and shared with three open PRs.The npm
0.0.N-securityplaceholder handling is unchanged: it is still a prerelease that is never announced (test added), and the matcher's holding rule is untouched.B (MEDIUM): workspace members' declared dependencies are direct
direct_namesfor JavaScript read only the lockfile directory's ownpackage.json. A dependency that only an npm, yarn or pnpm workspace member declared was stored as transitive, so the parent walk (parent_hint, which stops at the first direct copy) found no lever for anything under it.The direct set now also includes the members'
dependenciesanddevDependencies. Members are found byace::npm_local::workspace_member_dirsfrom:workspaces(array, or yarn's{packages: [...]});pnpm-workspace.yamlpackages:, in block or flow style (packages: ['a/*']).Negations are honoured with real glob matching:
!**/test/**excludespackages/test, because an excluded package is not a member.dir/**still expands one level deep, which is documented in the code. The members' own package names are excluded (they are local code), as are protocol specs such asworkspace:. Single-root projects are unchanged.Attribution:
direct_upgrade::direct_fixreads the rootpackage.json, does not find a member-only declaration, and names no command. The plan keeps "bump it where it is declared". Yarn members still decline by design.Coordination
osv/mod.rs: one line (pub(crate) mod release_advisories;), away from fix(upgrade-plan): name the parent release that fixes a nested advisory (audit H1) #981's lines.brief_facts_build.rs(feat(brief): a declared copy's bump names the command that keeps how it is declared (audit E8, C3) #982): one 6-line hunk inbuild_upgrade_facts.brief_facts.rs,parent_hint.rs,upgrade_plan.rs,pipeline_v2.rs,osv/types.rsandosv/identity.rsare not touched.Follow-ups (not in this PR)
pipeline_v2"New minor: X a -> b" feed row still announces a release without the check. It carries no command, and the file is fix(advisories): unversioned matches are never grounded; uncovered ecosystems say so (audit A2/E4) #974's.release_changelog::release_targetgrades releases (and fetches the new version's archive) without the check.release_grade::concerned_pinspicks the largest gap before the scratch/dormant filter. When a scratch project is a major behind and an active project only a minor behind,active_pinsis empty and the release says nothing for the active project, malware included. This was already the case before this PR; the fix is to filter liveness before picking the top gap.Tests
All run after rebasing onto
origin/main(which includes #974 and #982):cargo test --lib -- scoring:: evidence:: brief_facts ace_commands osv:: ace::npm_local briefing: 1703 passed, 0 failed (30 ignored).osv/release_advisories_tests.rs(16 tests):MAL-ids,MAL-aliases, GHSA "Malicious code in" summaries;minimumReleaseAge(4320 minutes) and to the trust horizon;evidence/stack_change_tests.rs:npm update;^2.0.0), withMAL-listing 2.0.5 and 2.1.0, still produces the warning;ace_commands/dependencies_tests.rs: npm array workspaces, yarn object form, pnpm block style with a negation, pnpm flow style plus wildcard negations, a single root unchanged, and an end-to-end walk where a member's dependency is stored as a direct row and its child stays transitive.cargo fmt --check: clean.cargo clippy --lib -- -D warnings: clean.check-file-sizes: warnings only, no errors.🤖 Generated with Claude Code
https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7