From 51843230859ef39344c0b67daa9049ead87ec49c Mon Sep 17 00:00:00 2001 From: nsfisis Date: Tue, 2 Jun 2026 23:58:38 +0900 Subject: feat(resolver): port SecurityAdvisoryPoolFilter::filter Implement the security advisory pool filter end to end, plus the remaining actionable wirings it unblocked. - Unify the PartialSecurityAdvisory|SecurityAdvisory union as the PartialOrFullSecurityAdvisory enum and make the advisory types Clone, so advisories can be collected and stored; Pool.security_removed_versions now carries the union. This also unblocks PoolOptimizer's clone of the security-removed versions. - Thread the filter result through run_security_advisory_filter/build_pool as anyhow::Result. - Introduce typed PlatformRepositoryHandle and pass platform repos as handles through determine_requirements instead of &PlatformRepository. - Wire RuleSetGenerator's is_unacceptable_fixed_or_locked_package check and UpdateCommand's non-locked installed-packages branch. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe/src/advisory/auditor.rs | 59 +++++++++++++++++----------------- 1 file changed, 29 insertions(+), 30 deletions(-) (limited to 'crates/shirabe/src/advisory/auditor.rs') diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs index 535f6a7..18af0f3 100644 --- a/crates/shirabe/src/advisory/auditor.rs +++ b/crates/shirabe/src/advisory/auditor.rs @@ -11,6 +11,7 @@ use shirabe_php_shim::{ }; use crate::advisory::IgnoredSecurityAdvisory; +use crate::advisory::PartialOrFullSecurityAdvisory; use crate::advisory::SecurityAdvisory; use crate::io::ConsoleIO; use crate::io::IOInterface; @@ -19,7 +20,6 @@ use crate::package::CompletePackageInterfaceHandle; use crate::package::PackageInterfaceHandle; use crate::package::base_package; use crate::package::base_package::BasePackage; -use crate::repository::PartialOrSecurityAdvisory; use crate::repository::RepositorySet; use crate::util::PackageInfo; @@ -182,7 +182,7 @@ impl Auditor { let error_or_warn = if warning_only { "warning" } else { "error" }; if affected_packages_count > 0 || ignored_advisories.len() > 0 { let passes: Vec<( - &IndexMap>, + &IndexMap>, String, )> = vec![ ( @@ -243,12 +243,12 @@ impl Auditor { Ok(audit_bitmask) } - /// @param array> $advisories + /// @param array> $advisories /// @param array $ignoreList /// @return bool pub fn needs_complete_advisory_load( &self, - advisories: &IndexMap>, + advisories: &IndexMap>, ignore_list: &IndexMap>, ) -> bool { if advisories.len() == 0 { @@ -256,16 +256,20 @@ impl Auditor { } // no partial advisories present - let advisories_values: Vec<&Vec> = advisories.values().collect(); + let advisories_values: Vec<&Vec> = + advisories.values().collect(); if array_all( &advisories_values, - |pkg_advisories: &&Vec| { - array_all(pkg_advisories, |_advisory: &PartialOrSecurityAdvisory| { - // TODO(phase-b): `$advisory instanceof SecurityAdvisory` — needs an advisory - // enum or trait downcast; SecurityAdvisoriesResult currently only holds - // PartialOrSecurityAdvisory so this is hard-coded to false - false - }) + |pkg_advisories: &&Vec| { + array_all( + pkg_advisories, + |_advisory: &PartialOrFullSecurityAdvisory| { + // TODO(phase-b): `$advisory instanceof SecurityAdvisory` — needs an advisory + // enum or trait downcast; SecurityAdvisoriesResult currently only holds + // PartialOrFullSecurityAdvisory so this is hard-coded to false + false + }, + ) }, ) { return false; @@ -307,13 +311,9 @@ impl Auditor { Ok(result) } - /// @phpstan-param array> $allAdvisories - /// @param array $ignoreList List of advisory IDs, remote IDs, CVE IDs or package names that reported but not listed as vulnerabilities. - /// @param array $ignoredSeverities List of ignored severity levels - /// @phpstan-return array{advisories: array>, ignoredAdvisories: array>} pub fn process_advisories( &self, - all_advisories: IndexMap>, + all_advisories: IndexMap>, ignore_list: &IndexMap>, ignored_severities: &IndexMap>, ) -> ProcessAdvisoriesResult { @@ -324,8 +324,8 @@ impl Auditor { }; } - let mut advisories: IndexMap> = IndexMap::new(); - let mut ignored: IndexMap> = IndexMap::new(); + let mut advisories: IndexMap> = IndexMap::new(); + let mut ignored: IndexMap> = IndexMap::new(); let mut ignore_reason: Option = None; for (package, pkg_advisories) in all_advisories { @@ -347,7 +347,7 @@ impl Auditor { // TODO(phase-b): `$advisory instanceof SecurityAdvisory` — needs an advisory enum // or trait downcast; the block below is skipped while SecurityAdvisoriesResult - // only holds PartialOrSecurityAdvisory + // only holds PartialOrFullSecurityAdvisory let advisory_as_full: Option<&SecurityAdvisory> = None; if let Some(full) = advisory_as_full { if is_string(&PhpMixed::String(full.severity.clone().unwrap_or_default())) @@ -411,11 +411,10 @@ impl Auditor { } } - /// @param array> $advisories /// @return array{int, int} Count of affected packages and total count of advisories fn count_advisories( &self, - advisories: &IndexMap>, + advisories: &IndexMap>, ) -> (i64, i64) { let mut count: i64 = 0; for package_advisories in advisories.values() { @@ -430,7 +429,7 @@ impl Auditor { fn output_advisories( &self, io: &mut dyn IOInterface, - advisories: &IndexMap>, + advisories: &IndexMap>, format: &str, ) -> Result<()> { match format { @@ -469,7 +468,7 @@ impl Auditor { fn output_advisories_table( &self, io: &ConsoleIO, - advisories: &IndexMap>, + advisories: &IndexMap>, ) { for package_advisories in advisories.values() { for advisory in package_advisories { @@ -483,7 +482,7 @@ impl Auditor { "Affected versions".to_string(), "Reported at".to_string(), ]; - // TODO(phase-b): advisory typed PartialOrSecurityAdvisory; PHP accesses + // TODO(phase-b): advisory typed PartialOrFullSecurityAdvisory; PHP accesses // SecurityAdvisory fields (title, link, reportedAt, etc.) let _ = advisory; let row: Vec = vec![ @@ -519,7 +518,7 @@ impl Auditor { fn output_advisories_plain( &self, io: &mut dyn IOInterface, - advisories: &IndexMap>, + advisories: &IndexMap>, ) { let mut error: Vec = vec![]; let mut first_advisory = true; @@ -528,7 +527,7 @@ impl Auditor { if !first_advisory { error.push("--------".to_string()); } - // TODO(phase-b): advisory typed PartialOrSecurityAdvisory; PHP accesses + // TODO(phase-b): advisory typed PartialOrFullSecurityAdvisory; PHP accesses // SecurityAdvisory fields let _ = advisory; error.push(format!("Package: {}", /* advisory.packageName */ "")); @@ -687,7 +686,7 @@ impl Auditor { } fn get_advisory_id(&self, advisory: &SecurityAdvisory) -> String { - // TODO(phase-b): advisory.advisory_id lives on inner PartialOrSecurityAdvisory + // TODO(phase-b): advisory.advisory_id lives on inner PartialOrFullSecurityAdvisory let advisory_id: &str = ""; let _ = advisory; if str_starts_with(advisory_id, "PKSA-") { @@ -747,6 +746,6 @@ impl Auditor { #[derive(Debug)] pub struct ProcessAdvisoriesResult { - pub advisories: IndexMap>, - pub ignored_advisories: IndexMap>, + pub advisories: IndexMap>, + pub ignored_advisories: IndexMap>, } -- cgit v1.3.1