Skip to content

fix(severity): one grade per advisory, inherited from GHSA aliases when unrated; one tier for unknown - #986

Merged
runyourempire merged 5 commits into
mainfrom
fix/severity-parity
Oct 18, 2026
Merged

runyourempire merged 5 commits into
mainfrom
fix/severity-parity

Conversation

@runyourempire

@runyourempire runyourempire commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

What

This PR gives each advisory one severity in every engine and on every surface. It covers the app half of the parity rule agreed with recovery-lead, which the @4da/mcp-server 6.2.5 integration also adopts.

  1. An unrated record takes the highest grade among all its GHSA aliases. "Unrated" means no curated label and no scorable CVSS. Only the local advisory mirror is read; there is no network call.
    • Live example: GO-2024-3333 → high, via GHSA-w32m-9786-jp63.
    • The grade is never shown as the record's own. MatchedAdvisory.severity_from names the alias, and every surface prints it as "high (from GHSA-…)": the Upgrade Plan explanation and citations, the Preemption explanation, the Brief (deterministic and prompt), and stack-change.
  2. A cluster (alias group) takes the worst own grade of its records. Inheritance applies only when no record has an own grade.
    • Before, cluster_severity_tier took the highest CVSS band whenever any record had a score, and ignored labels.
    • Live, crates.io nix read medium on the plan, Preemption and Brief, but high on Knowledge Gaps.
    • knowledge_decay, the plan, alerts, the Brief and the scorer's security lane now all grade through one function (osv::severity::row_grade / cluster_severity).
  3. Per record, the curated label comes first, then the CVSS band.
    • The stored label already used this order, but the graders read CVSS first.
    • Across the 3,923 pinned conformance records, the two orders disagree on 123, all GHSA. 113 of those also carry a CVSS v4 vector that neither engine computes, and GitHub's label follows that v4 score. Example: GHSA-23hp-3jrh-7fpw has v3 7.5 and label CRITICAL, with v4 VA:H/SA:H.
    • So label-first is the more accurate order, and the MCP is being aligned to it.
  4. One rule for a still-unknown cluster, the same for plan and alerts (osv::severity::cluster_urgency). Before, the plan said Medium and alerts guessed from keywords in the first record's summary (infer_urgency_from_summary, now deleted).
    • Nothing grades it, even through aliases: Medium. It is a version-confirmed advisory, with no evidence to call it High or to dismiss it to Watch.
    • Maintenance notices only (no fix, no CVSS, "unmaintained"/"deprecated"): Watch, which is how the plan already folded them.
    • A MAL- record: High. This is unchanged from the old heuristic's id check, which is the source's own statement.
    • The AD-046 scope rule still applies afterwards.

Review round (2026-10-19)

This is stacked on #992 (fourda_depengine::is_malware plus the cwe_ids column), so it must land after #992.

  • One grader for all five surfaces. The plan, Preemption, the Brief, Knowledge Gaps and Signal all go through osv::severity::cluster_grade, over matched advisories and raw mirror rows. The mirror lookup reads the whole alias group, so nix is high, not medium. A five-surface test pins the nix and GO-2024-3333 shapes.
  • Fixes: a multi-byte-safe alias prefix, and one maintenance-notice predicate.
  • Maintenance notices read "unmaintained: zvariant (RUSTSEC-2026-0370), no exploitable vulnerability". They are never counted or worded as vulnerabilities.
  • Installed malware (a MAL- id or alias, or CWE-506) is Critical and exempt from the AD-046 scope discount. The crossenv GHSA-c2m4 record is pinned as sv-021.

Tier changes on a read-only copy of the live database

Package Plan Alerts Knowledge Gaps / Signal Interrupts
npm next 16.3.6 Medium → High Medium → High medium → high now interrupts
npm uuid 3.4.0 High → Medium High → Medium high → medium stops interrupting
npm nanoid 3.3.13 Watch → Medium Watch → Medium medium → high no
npm tar 7.5.2 Medium Medium high → critical no
npm fflate, elliptic, @ai-sdk/provider-utils Medium → Watch Medium → Watch lower no
anyhow, h2, lru, scc (crates.io) Medium Watch → Medium none no
proc-macro-error, zbus_names, zvariant, zvariant_derive Watch fold Watch none no; wording becomes "unmaintained: …"
  • The npm moves come from label-first: GitHub's label beats the CVSS v3 band.
  • The crates.io alert moves come from dropping the summary-keyword guess.
  • No "rce"/"auth bypass" cluster moves on this machine.
  • No malware matches yet: the snapshot predates cwe_ids.

Parity status: the MCP half is NOT merged yet

This is the app half. The @4da/mcp-server half (label-first per record, worst own grade per cluster, alias inheritance, the unknown tier, installed MAL- Critical, and severity in the TS vector runner) exists only on unmerged MCP branches today. Until it merges and the MCP corpus pin moves to a 4DA commit that contains severity.jsonl, the two engines can still disagree on unrated advisories. That is a hard gate before any MCP publish.

Scoring version

PIPELINE_VERSION goes 42 → 43 and is registered in SCOPED_EPOCHS for source_type IN ('cve','osv').

  • The security-lane tier now reads labels first, and that tier sets the saved signal_priority and the critical-alert flag. The v39 precedent treats that kind of change as needing a bump.
  • The inherited alias grade itself never reaches scoring, because scoring reads mirror rows, not matched advisories.

Shared vectors

src-tauri/tests/conformance/vectors/severity.jsonl has 19 lines. They are documented in the vectors README, run by the Rust gate through the real code path, and mirrored in the MCP runner. They cover:

  • RUSTSEC-2020-0014's 8 aliases;
  • GO-2024-3333;
  • an alias missing from the mirror, all-unknown, and an alias that is held but ungraded;
  • a tie;
  • own-beats-inherited;
  • the CVE-2026-4277 GHSA/PYSEC pair;
  • MAL-, a maintenance notice, and the nix group;
  • three real records where the label and the CVSS disagree;
  • the unknown tier.

Live effect, on a read-only copy of the operator database

  • 159 matched advisories. 26 are unrated, 10 have a GHSA alias, and 2 gain a grade (glib RUSTSEC-2024-0429 → medium, rand RUSTSEC-2026-0097 → low). Neither changes its package's urgency, because both groups already had their own graded record.
  • The visible change is the unknown rule: 17 packages change on the plan or alerts.
    • anyhow 1.0.102 (unsoundness, fix 1.0.103): alert Watch → Medium, now matching the plan. The same holds for h2, lru and scc.
    • instant, paste, fxhash, the unic-* crates and other maintenance notices: Watch on both surfaces, by one rule.

Not here

  • ConfidenceProvenance gets no new variant. It is a closed list describing how the confidence number was computed, and extending it is a canonical-type change that needs an ADR (doctrine rule 1). Provenance goes in the explanation and the citation note instead.
  • It overlaps fix/h1-parent-release (fix(upgrade-plan): name the parent release that fixes a nested advisory (audit H1) #981) in upgrade_plan.rs, osv/mod.rs, vectors_tests.rs and the vectors README, so whichever lands second rebases.

Checks

  • cargo clippy --locked -- -D warnings: clean.
  • depengine: fmt, clippy (native and wasm32), tests, and the wasm build (0 imports; output equals native).
  • cargo test --lib across osv, evidence, preemption, brief_facts, scoring, knowledge_decay, briefing, stack_change and morning: 1,847 passed, including the vector gate.
  • check-file-sizes: no errors.

🤖 Generated with Claude Code

https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH

runyourempire added a commit that referenced this pull request Oct 18, 2026
… malware Critical; notices never worded as vulnerabilities (#986 review)

B1. One mirror-row grader. osv::severity::cluster_grade runs over a
SeverityRecord (MatchedAdvisory or StoredAdvisory): the most severe own
grade in the alias group, else the highest GHSA alias grade from
AliasGrades. The matcher's surfaces (Upgrade Plan, Preemption, Brief,
stack_change) and the mirror-row surfaces now use the same code:
- Knowledge Gaps: advisory_tier_for groups the affecting rows by alias
  (rows_grade); AliasGrades is loaded once per GapScan.
- Signal: matcher_verdict grades its checked / exposed rows as one group;
  mirror_severity_tier reads every row of the item's alias group for the
  package, not get_osv_advisory_by_id's single GHSA-first row (GHSA-76w9
  gave medium for the nix group whose GHSA-wgrg is high).
- The scorer reads the alias grades from a copy the Database keeps
  (Database::alias_grades: one query, dropped on every osv_advisories
  write, with a write counter so a load that raced a write is not kept),
  never per item.
A five-surface test pins the nix shape and the GO-2024-3333 shape (graded
alias stored for another package): high on the plan, Preemption, Brief,
Knowledge Gaps, the matcher verdict and the mirror lookup.

B2. fourda_depengine::severity::ghsa_aliases used a byte slice (a[..5]) on
untrusted alias text and panicked on a multi-byte boundary; it uses get(..5)
now, with a test (a 2-byte char across index 5, a 3-byte char at 4). The
other slices in this diff index Vecs by construction.

Installed malware (recovery-lead decision). A MAL- record on the installed
version is Critical in cluster_urgency, and the plan and alert path skip the
AD-046 scope rule for it: a dev-only or transitive malicious package still
runs its code on the developer's machine. sv-010 -> critical, sv-020 new.

S2 and the notice wording. One maintenance predicate
(osv::severity::maintenance_text: no fix, no CVSS, unmaintained/deprecated
words over summary + details) decides both "may take an alias grade" and
"is a notice"; the MCP-style summary-only check is gone. A notice is never
counted or worded as a vulnerability: "unmaintained: zvariant
(RUSTSEC-2026-0370), no exploitable vulnerability" on the Preemption alert
(title and explanation; mixed groups count only vulnerabilities and add the
notice sentence), the Upgrade Plan's Watch fold, the Brief (advisory_count 0,
no severity in the deterministic line or the prompt), stack_change,
Knowledge Gaps (only_maintenance_notices: not a security gap) and Signal
(SecurityVerdict.maintenance_only or the item's own text: Watch priority,
the notice as the action line and the necessity reason, no applicability).

Nits. One tier rank (fourda_depengine::severity::tier_rank, re-exported by
osv::severity); brief_facts_build and security_verdict lost their inverted
copies, and security_verdict::normalize_tier gave way to canonical_tier.
osv_get_matches always serializes severity_from (null for an own grade),
and the frontend command type names severity_label / severity_from.

PIPELINE_VERSION stays as rebased (the security-lane changes ride the same
cve/osv-scoped bump).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
runyourempire added a commit that referenced this pull request Oct 18, 2026
… malware Critical; notices never worded as vulnerabilities (#986 review)

B1. One mirror-row grader. osv::severity::cluster_grade runs over a
SeverityRecord (MatchedAdvisory or StoredAdvisory): the most severe own
grade in the alias group, else the highest GHSA alias grade from
AliasGrades. The matcher's surfaces (Upgrade Plan, Preemption, Brief,
stack_change) and the mirror-row surfaces now use the same code:
- Knowledge Gaps: advisory_tier_for groups the affecting rows by alias
  (rows_grade); AliasGrades is loaded once per GapScan.
- Signal: matcher_verdict grades its checked / exposed rows as one group;
  mirror_severity_tier reads every row of the item's alias group for the
  package, not get_osv_advisory_by_id's single GHSA-first row (GHSA-76w9
  gave medium for the nix group whose GHSA-wgrg is high).
- The scorer reads the alias grades from a copy the Database keeps
  (Database::alias_grades: one query, dropped on every osv_advisories
  write, with a write counter so a load that raced a write is not kept),
  never per item.
A five-surface test pins the nix shape and the GO-2024-3333 shape (graded
alias stored for another package): high on the plan, Preemption, Brief,
Knowledge Gaps, the matcher verdict and the mirror lookup.

B2. fourda_depengine::severity::ghsa_aliases used a byte slice (a[..5]) on
untrusted alias text and panicked on a multi-byte boundary; it uses get(..5)
now, with a test (a 2-byte char across index 5, a 3-byte char at 4). The
other slices in this diff index Vecs by construction.

Installed malware (recovery-lead decision). A MAL- record on the installed
version is Critical in cluster_urgency, and the plan and alert path skip the
AD-046 scope rule for it: a dev-only or transitive malicious package still
runs its code on the developer's machine. sv-010 -> critical, sv-020 new.

S2 and the notice wording. One maintenance predicate
(osv::severity::maintenance_text: no fix, no CVSS, unmaintained/deprecated
words over summary + details) decides both "may take an alias grade" and
"is a notice"; the MCP-style summary-only check is gone. A notice is never
counted or worded as a vulnerability: "unmaintained: zvariant
(RUSTSEC-2026-0370), no exploitable vulnerability" on the Preemption alert
(title and explanation; mixed groups count only vulnerabilities and add the
notice sentence), the Upgrade Plan's Watch fold, the Brief (advisory_count 0,
no severity in the deterministic line or the prompt), stack_change,
Knowledge Gaps (only_maintenance_notices: not a security gap) and Signal
(SecurityVerdict.maintenance_only or the item's own text: Watch priority,
the notice as the action line and the necessity reason, no applicability).

Nits. One tier rank (fourda_depengine::severity::tier_rank, re-exported by
osv::severity); brief_facts_build and security_verdict lost their inverted
copies, and security_verdict::normalize_tier gave way to canonical_tier.
osv_get_matches always serializes severity_from (null for an own grade),
and the frontend command type names severity_label / severity_from.

PIPELINE_VERSION stays as rebased (the security-lane changes ride the same
cve/osv-scoped bump).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
runyourempire added a commit that referenced this pull request Oct 18, 2026
… malware Critical; notices never worded as vulnerabilities (#986 review)

B1. One mirror-row grader. osv::severity::cluster_grade runs over a
SeverityRecord (MatchedAdvisory or StoredAdvisory): the most severe own
grade in the alias group, else the highest GHSA alias grade from
AliasGrades. The matcher's surfaces (Upgrade Plan, Preemption, Brief,
stack_change) and the mirror-row surfaces now use the same code:
- Knowledge Gaps: advisory_tier_for groups the affecting rows by alias
  (rows_grade); AliasGrades is loaded once per GapScan.
- Signal: matcher_verdict grades its checked / exposed rows as one group;
  mirror_severity_tier reads every row of the item's alias group for the
  package, not get_osv_advisory_by_id's single GHSA-first row (GHSA-76w9
  gave medium for the nix group whose GHSA-wgrg is high).
- The scorer reads the alias grades from a copy the Database keeps
  (Database::alias_grades: one query, dropped on every osv_advisories
  write, with a write counter so a load that raced a write is not kept),
  never per item.
A five-surface test pins the nix shape and the GO-2024-3333 shape (graded
alias stored for another package): high on the plan, Preemption, Brief,
Knowledge Gaps, the matcher verdict and the mirror lookup.

B2. fourda_depengine::severity::ghsa_aliases used a byte slice (a[..5]) on
untrusted alias text and panicked on a multi-byte boundary; it uses get(..5)
now, with a test (a 2-byte char across index 5, a 3-byte char at 4). The
other slices in this diff index Vecs by construction.

Installed malware (recovery-lead decision). A MAL- record on the installed
version is Critical in cluster_urgency, and the plan and alert path skip the
AD-046 scope rule for it: a dev-only or transitive malicious package still
runs its code on the developer's machine. sv-010 -> critical, sv-020 new.

S2 and the notice wording. One maintenance predicate
(osv::severity::maintenance_text: no fix, no CVSS, unmaintained/deprecated
words over summary + details) decides both "may take an alias grade" and
"is a notice"; the MCP-style summary-only check is gone. A notice is never
counted or worded as a vulnerability: "unmaintained: zvariant
(RUSTSEC-2026-0370), no exploitable vulnerability" on the Preemption alert
(title and explanation; mixed groups count only vulnerabilities and add the
notice sentence), the Upgrade Plan's Watch fold, the Brief (advisory_count 0,
no severity in the deterministic line or the prompt), stack_change,
Knowledge Gaps (only_maintenance_notices: not a security gap) and Signal
(SecurityVerdict.maintenance_only or the item's own text: Watch priority,
the notice as the action line and the necessity reason, no applicability).

Nits. One tier rank (fourda_depengine::severity::tier_rank, re-exported by
osv::severity); brief_facts_build and security_verdict lost their inverted
copies, and security_verdict::normalize_tier gave way to canonical_tier.
osv_get_matches always serializes severity_from (null for an own grade),
and the frontend command type names severity_label / severity_from.

PIPELINE_VERSION stays as rebased (the security-lane changes ride the same
cve/osv-scoped bump).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
runyourempire added a commit that referenced this pull request Oct 18, 2026
…WE ids stored

`fourda_depengine::is_malware(id, aliases, cwe_ids)`: a malicious-package
record is an OSV `MAL-` id or alias, or carries CWE-506 in
database_specific.cwe_ids. GitHub's malware advisories often have no
`MAL-` alias: crossenv GHSA-c2m4-w5hm-vqjw ("crossenv is malware") has
aliases ["CVE-2017-16074"] and cwe_ids ["CWE-506"] (live 2026-10-18), so
the parent-release predicate read it as an unfixed ordinary advisory. No
summary keyword is read (the release gate's "Malicious code in" prefix
check is replaced by the shared rule).

Phase 128 adds osv_advisories.cwe_ids (additive; schema 128, so a
127 binary refuses the database through the existing too-new guard) and
makes every ecosystem's advisory sync due, so rows stored before it are
re-upserted with their CWE ids on the next cycle instead of staying NULL.
The sync stores database_specific.cwe_ids; StoredAdvisory and
MatchedAdvisory carry them. Wired where MAL- was matched: the
parent-release Advisory (osv/parent_release.rs) and the release gate
(osv/release_advisories.rs). Severity is left to #986. No new egress.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
runyourempire added a commit that referenced this pull request Oct 18, 2026
…WE ids stored

`fourda_depengine::is_malware(id, aliases, cwe_ids)`: a malicious-package
record is an OSV `MAL-` id or alias, or carries CWE-506 in
database_specific.cwe_ids. GitHub's malware advisories often have no
`MAL-` alias: crossenv GHSA-c2m4-w5hm-vqjw ("crossenv is malware") has
aliases ["CVE-2017-16074"] and cwe_ids ["CWE-506"] (live 2026-10-18), so
the parent-release predicate read it as an unfixed ordinary advisory. No
summary keyword is read (the release gate's "Malicious code in" prefix
check is replaced by the shared rule).

Phase 128 adds osv_advisories.cwe_ids (additive; schema 128, so a
127 binary refuses the database through the existing too-new guard) and
makes every ecosystem's advisory sync due, so rows stored before it are
re-upserted with their CWE ids on the next cycle instead of staying NULL.
The sync stores database_specific.cwe_ids; StoredAdvisory and
MatchedAdvisory carry them. Wired where MAL- was matched: the
parent-release Advisory (osv/parent_release.rs) and the release gate
(osv/release_advisories.rs). Severity is left to #986. No new egress.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
runyourempire and others added 5 commits October 19, 2026 01:15
…en unrated; one tier for unknown

Severity parity with @4da/mcp-server, pinned by a new shared vector file
(src-tauri/tests/conformance/vectors/severity.jsonl, sv-001..sv-019).

Unrated records. A record with no curated label and no scorable CVSS takes
the HIGHEST grade among all of its GHSA aliases readable in the local
mirror (osv_advisories, any package or ecosystem), named by the smallest
GHSA id on a tie; an unreadable alias never lowers it; none readable leaves
it unknown. Maintenance notices are never graded from an alias (the MCP's
isMaintenanceNotice). The rule is the pure fourda_depengine::severity
(twin of the MCP's gradeFromAliases); osv::severity::grade_unrated applies
it once per matching pass over the mirror rows get_matched_advisories
already reads: no network, no extra query, nothing per advisory.

Provenance. MatchedAdvisory carries severity_from (the GHSA id); an
inherited grade is always shown with it: "high (from GHSA-w32m-9786-jp63)"
in the Upgrade Plan explanation and citation notes, the Preemption alert
explanation, the Brief (SecurityFact.worst_tier_from, rendered by the
deterministic brief, the LLM prompt and the stack-change stream item).
EvidenceItem's ConfidenceProvenance is a closed enum (Heuristic /
OsvVerified / ...) naming how the confidence number was derived; adding a
variant for a severity source would extend the canonical type (doctrine
rule 1, needs an ADR), so the alias goes in the explanation and the
citation note instead.

Cluster grade. A vulnerability cluster (one alias group) takes the MOST
SEVERE OWN grade among its records; an inherited grade applies only when no
record has one. Before, cluster_severity_tier took the highest CVSS band
whenever any record was scored and ignored labels: crates.io nix
(GHSA-76w9 CVSS 6.7, GHSA-wgrg label HIGH, RUSTSEC-2021-0119) read medium on
the plan, alerts and Brief and high on Knowledge Gaps. Knowledge Gaps, the
plan, the alert path, the Brief and the scorer's security lane now grade
each record by one function, osv::severity::row_grade.

Per-record order. The curated label first, the CVSS band second (the
mirror's stored severity_label order). Every grader used to read CVSS
first. On the 3,923 pinned conformance records the two orders disagree on
123, all GHSA, 113 of them beside a CVSS v4 vector neither engine scores,
which GitHub's label follows (GHSA-23hp-3jrh-7fpw: v3 7.5, label CRITICAL).
Pinned: sv-015..sv-017.

Unknown tier. One rule for a cluster nothing grades, on the plan and the
alerts (osv::severity::cluster_urgency): Medium; Watch for maintenance
notices only (no fix, no CVSS, unmaintained/deprecated text, the plan's
informational fold); High for a malicious-package record (MAL-). The alert
path used to guess from the first record's summary words
(infer_urgency_from_summary, deleted): Watch unless it said "rce" / "xss"
etc. Accuracy first: a version-confirmed vulnerability with a fix is not
dismissed to Watch on a keyword miss, nor raised to High on a keyword hit;
Medium says what is known. The AD-046 scope rule
(scope_adjusted_urgency) still applies on top, so a dev-only unknown reads
Watch on both surfaces.

Scoring. PIPELINE_VERSION 42 -> 43, registered in SCOPED_EPOCHS on
source_type IN ('cve','osv'): the security lane's tier (advisory_tier,
mirror_severity_tier, content_severity_tier) moves to the label-first
order, which can change a persisted signal_priority / critical-alert flag
(the v39 precedent). The tier is computed only for is_security_source.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
… malware Critical; notices never worded as vulnerabilities (#986 review)

B1. One mirror-row grader. osv::severity::cluster_grade runs over a
SeverityRecord (MatchedAdvisory or StoredAdvisory): the most severe own
grade in the alias group, else the highest GHSA alias grade from
AliasGrades. The matcher's surfaces (Upgrade Plan, Preemption, Brief,
stack_change) and the mirror-row surfaces now use the same code:
- Knowledge Gaps: advisory_tier_for groups the affecting rows by alias
  (rows_grade); AliasGrades is loaded once per GapScan.
- Signal: matcher_verdict grades its checked / exposed rows as one group;
  mirror_severity_tier reads every row of the item's alias group for the
  package, not get_osv_advisory_by_id's single GHSA-first row (GHSA-76w9
  gave medium for the nix group whose GHSA-wgrg is high).
- The scorer reads the alias grades from a copy the Database keeps
  (Database::alias_grades: one query, dropped on every osv_advisories
  write, with a write counter so a load that raced a write is not kept),
  never per item.
A five-surface test pins the nix shape and the GO-2024-3333 shape (graded
alias stored for another package): high on the plan, Preemption, Brief,
Knowledge Gaps, the matcher verdict and the mirror lookup.

B2. fourda_depengine::severity::ghsa_aliases used a byte slice (a[..5]) on
untrusted alias text and panicked on a multi-byte boundary; it uses get(..5)
now, with a test (a 2-byte char across index 5, a 3-byte char at 4). The
other slices in this diff index Vecs by construction.

Installed malware (recovery-lead decision). A MAL- record on the installed
version is Critical in cluster_urgency, and the plan and alert path skip the
AD-046 scope rule for it: a dev-only or transitive malicious package still
runs its code on the developer's machine. sv-010 -> critical, sv-020 new.

S2 and the notice wording. One maintenance predicate
(osv::severity::maintenance_text: no fix, no CVSS, unmaintained/deprecated
words over summary + details) decides both "may take an alias grade" and
"is a notice"; the MCP-style summary-only check is gone. A notice is never
counted or worded as a vulnerability: "unmaintained: zvariant
(RUSTSEC-2026-0370), no exploitable vulnerability" on the Preemption alert
(title and explanation; mixed groups count only vulnerabilities and add the
notice sentence), the Upgrade Plan's Watch fold, the Brief (advisory_count 0,
no severity in the deterministic line or the prompt), stack_change,
Knowledge Gaps (only_maintenance_notices: not a security gap) and Signal
(SecurityVerdict.maintenance_only or the item's own text: Watch priority,
the notice as the action line and the necessity reason, no applicability).

Nits. One tier rank (fourda_depengine::severity::tier_rank, re-exported by
osv::severity); brief_facts_build and security_verdict lost their inverted
copies, and security_verdict::normalize_tier gave way to canonical_tier.
osv_get_matches always serializes severity_from (null for an own grade),
and the frontend command type names severity_label / severity_from.

PIPELINE_VERSION stays as rebased (the security-lane changes ride the same
cve/osv-scoped bump).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
…until fix/malware-cwe)

contains_malware now reads a MAL- id OR a MAL- alias (a GHSA malware
advisory listing its OpenSSF record). The shared fourda_depengine malware
predicate, with database_specific.cwe_ids CWE-506 (GHSA-c2m4-w5hm-vqjw,
crossenv, has no MAL alias), lands with fix/malware-cwe and replaces it;
the crossenv vector waits for that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
…depengine::is_malware); crossenv vector on

contains_malware now calls fourda_depengine::is_malware over each
match's (advisory_id, aliases, cwe_ids): a MAL- id or alias, or CWE-506 in
database_specific.cwe_ids. The local MAL-only predicate is deleted. Stacked
on fix/malware-cwe (#992), which stores cwe_ids; this lands after it.

sv-021: crossenv GHSA-c2m4-w5hm-vqjw (live OSV 2026-10-18: aliases
[CVE-2017-16074], cwe_ids [CWE-506], own label HIGH) on the installed
1.0.1 is Critical, its severity its own high. The vector checker reads
database_specific.cwe_ids as osv::sync stores it. Knowledge Gaps reads the
cwe_ids column with the rows it grades.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
…edAdvisory::is_malware); sv-022 title-only malware

contains_malware calls MatchedAdvisory::is_malware, the merged
fourda_depengine::is_malware(id, aliases, cwe_ids, summary): a MAL- id or
alias, CWE-506, or GitHub's "Malicious Package in" / "Malicious code in"
title. #992's unrated-malware test now pins Critical on the alert path
(most_urgent_tier) instead of the deleted summary heuristic.

sv-022: GHSA-28f8-hqmc-7ph8 "Malicious Package in ember-power-timepicker"
(live OSV 2026-10-19: no aliases, cwe_ids [], own label CRITICAL, versions
[1.0.8]) installed is Critical; a unit test pins the title arm beside a low
own grade. Restores a closing brace the rebase dropped in
briefing_deterministic_tests.rs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KSXWNneouUjTiVV3Rc3FH
@runyourempire
runyourempire added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 90904f9 Oct 18, 2026
24 checks passed
@runyourempire
runyourempire deleted the fix/severity-parity branch October 18, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant