Problem
When the same advisory is present in multiple audit passes, mergeAdvisories spreads the later record over the earlier one. A later moderate/low record can overwrite an earlier high/critical severity, causing isHighRisk to stop treating the merged advisory as blocking. Advisories without a stable identifier can also collapse under the same "undefined" key.
This is a P1 security-policy bug because merge order can downgrade a real blocker.
Evidence
- Current trunk: 0590a47.
- scripts/dependency-audit-policy.mts mergeAuditJsons combines results from multiple audit passes by advisoryKey.
- mergeAdvisories returns { ...left, ...right, ... }, so right.severity overwrites left.severity without severity ordering.
- isHighRisk later checks only the merged severity.
- advisoryKey falls back to String(advisory.id); missing identifiers therefore share the same fallback key.
Desired outcome
Merging audit evidence must be monotonic: adding another audit result can preserve or increase risk, never lower it, and records without a stable identity must not be conflated.
Proposed implementation path
- Normalize severities through an explicit ordered enum/rank.
- Select the maximum observed severity while merging the remaining evidence sets deterministically.
- Require a stable advisory identity (GHSA, CVE, or validated audit id); fail closed or keep records distinct when it is missing.
- Make merged output independent of input ordering and retain provenance useful for diagnostics.
Acceptance criteria
- The same high/low advisory merges to high in both input orders.
- Critical/high cannot be overwritten by moderate/low/unknown values.
- Two identifier-less advisories do not collapse into one record.
- Invalid severity or identity evidence produces a stable diagnostic rather than silent normalization.
- Existing valid audit fixtures remain deterministic.
Validation
- Add table-driven tests for every severity ordering in both merge directions.
- Add fixtures for GHSA, CVE-only, numeric-id, and missing-id records.
- Run the dependency-audit policy suite and repository security gate.
Scope boundaries
- Do not change which normalized severities are considered blocking.
- Do not merge unrelated advisories solely because package names or descriptions match.
- Keep allowlist review semantics unchanged.
Problem
When the same advisory is present in multiple audit passes, mergeAdvisories spreads the later record over the earlier one. A later moderate/low record can overwrite an earlier high/critical severity, causing isHighRisk to stop treating the merged advisory as blocking. Advisories without a stable identifier can also collapse under the same "undefined" key.
This is a P1 security-policy bug because merge order can downgrade a real blocker.
Evidence
Desired outcome
Merging audit evidence must be monotonic: adding another audit result can preserve or increase risk, never lower it, and records without a stable identity must not be conflated.
Proposed implementation path
Acceptance criteria
Validation
Scope boundaries