diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-02 23:58:38 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-02 23:58:54 +0900 |
| commit | 51843230859ef39344c0b67daa9049ead87ec49c (patch) | |
| tree | f657969816da51b7f8656012e756498680ffcc23 /crates/shirabe/src/advisory | |
| parent | 20dbcf11b86cb03c451ba1d5cd9efe17b68fa66d (diff) | |
| download | php-shirabe-51843230859ef39344c0b67daa9049ead87ec49c.tar.gz php-shirabe-51843230859ef39344c0b67daa9049ead87ec49c.tar.zst php-shirabe-51843230859ef39344c0b67daa9049ead87ec49c.zip | |
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) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/advisory')
| -rw-r--r-- | crates/shirabe/src/advisory/auditor.rs | 59 | ||||
| -rw-r--r-- | crates/shirabe/src/advisory/mod.rs | 2 | ||||
| -rw-r--r-- | crates/shirabe/src/advisory/partial_or_full_security_advisory.rs | 25 | ||||
| -rw-r--r-- | crates/shirabe/src/advisory/partial_security_advisory.rs | 10 | ||||
| -rw-r--r-- | crates/shirabe/src/advisory/security_advisory.rs | 2 |
5 files changed, 62 insertions, 36 deletions
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<String, Vec<PartialOrSecurityAdvisory>>, + &IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, String, )> = vec![ ( @@ -243,12 +243,12 @@ impl Auditor { Ok(audit_bitmask) } - /// @param array<string, array<SecurityAdvisory|PartialOrSecurityAdvisory>> $advisories + /// @param array<string, array<SecurityAdvisory|PartialOrFullSecurityAdvisory>> $advisories /// @param array<string, string|null> $ignoreList /// @return bool pub fn needs_complete_advisory_load( &self, - advisories: &IndexMap<String, Vec<PartialOrSecurityAdvisory>>, + advisories: &IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, ignore_list: &IndexMap<String, Option<String>>, ) -> bool { if advisories.len() == 0 { @@ -256,16 +256,20 @@ impl Auditor { } // no partial advisories present - let advisories_values: Vec<&Vec<PartialOrSecurityAdvisory>> = advisories.values().collect(); + let advisories_values: Vec<&Vec<PartialOrFullSecurityAdvisory>> = + advisories.values().collect(); if array_all( &advisories_values, - |pkg_advisories: &&Vec<PartialOrSecurityAdvisory>| { - 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<PartialOrFullSecurityAdvisory>| { + 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<string, array<PartialOrSecurityAdvisory|SecurityAdvisory>> $allAdvisories - /// @param array<string, string|null> $ignoreList List of advisory IDs, remote IDs, CVE IDs or package names that reported but not listed as vulnerabilities. - /// @param array<string, string|null> $ignoredSeverities List of ignored severity levels - /// @phpstan-return array{advisories: array<string, array<PartialOrSecurityAdvisory|SecurityAdvisory>>, ignoredAdvisories: array<string, array<PartialOrSecurityAdvisory|SecurityAdvisory>>} pub fn process_advisories( &self, - all_advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>>, + all_advisories: IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, ignore_list: &IndexMap<String, Option<String>>, ignored_severities: &IndexMap<String, Option<String>>, ) -> ProcessAdvisoriesResult { @@ -324,8 +324,8 @@ impl Auditor { }; } - let mut advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>> = IndexMap::new(); - let mut ignored: IndexMap<String, Vec<PartialOrSecurityAdvisory>> = IndexMap::new(); + let mut advisories: IndexMap<String, Vec<PartialOrFullSecurityAdvisory>> = IndexMap::new(); + let mut ignored: IndexMap<String, Vec<PartialOrFullSecurityAdvisory>> = IndexMap::new(); let mut ignore_reason: Option<String> = 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<string, array<PartialOrSecurityAdvisory>> $advisories /// @return array{int, int} Count of affected packages and total count of advisories fn count_advisories( &self, - advisories: &IndexMap<String, Vec<PartialOrSecurityAdvisory>>, + advisories: &IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, ) -> (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<String, Vec<PartialOrSecurityAdvisory>>, + advisories: &IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, format: &str, ) -> Result<()> { match format { @@ -469,7 +468,7 @@ impl Auditor { fn output_advisories_table( &self, io: &ConsoleIO, - advisories: &IndexMap<String, Vec<PartialOrSecurityAdvisory>>, + advisories: &IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, ) { 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<String> = vec![ @@ -519,7 +518,7 @@ impl Auditor { fn output_advisories_plain( &self, io: &mut dyn IOInterface, - advisories: &IndexMap<String, Vec<PartialOrSecurityAdvisory>>, + advisories: &IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, ) { let mut error: Vec<String> = 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<String, Vec<PartialOrSecurityAdvisory>>, - pub ignored_advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>>, + pub advisories: IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, + pub ignored_advisories: IndexMap<String, Vec<PartialOrFullSecurityAdvisory>>, } diff --git a/crates/shirabe/src/advisory/mod.rs b/crates/shirabe/src/advisory/mod.rs index bcfbe93..df80735 100644 --- a/crates/shirabe/src/advisory/mod.rs +++ b/crates/shirabe/src/advisory/mod.rs @@ -1,11 +1,13 @@ pub mod audit_config; pub mod auditor; pub mod ignored_security_advisory; +pub mod partial_or_full_security_advisory; pub mod partial_security_advisory; pub mod security_advisory; pub use audit_config::*; pub use auditor::*; pub use ignored_security_advisory::*; +pub use partial_or_full_security_advisory::*; pub use partial_security_advisory::*; pub use security_advisory::*; diff --git a/crates/shirabe/src/advisory/partial_or_full_security_advisory.rs b/crates/shirabe/src/advisory/partial_or_full_security_advisory.rs new file mode 100644 index 0000000..bb1df78 --- /dev/null +++ b/crates/shirabe/src/advisory/partial_or_full_security_advisory.rs @@ -0,0 +1,25 @@ +use crate::advisory::PartialSecurityAdvisory; +use crate::advisory::SecurityAdvisory; +use shirabe_semver::constraint::AnyConstraint; + +#[derive(Debug, Clone)] +pub enum PartialOrFullSecurityAdvisory { + Partial(PartialSecurityAdvisory), + Full(SecurityAdvisory), +} + +impl PartialOrFullSecurityAdvisory { + pub fn advisory_id(&self) -> &str { + match self { + PartialOrFullSecurityAdvisory::Partial(p) => &p.advisory_id, + PartialOrFullSecurityAdvisory::Full(s) => s.advisory_id(), + } + } + + pub fn affected_versions(&self) -> &AnyConstraint { + match self { + PartialOrFullSecurityAdvisory::Partial(p) => &p.affected_versions, + PartialOrFullSecurityAdvisory::Full(s) => s.affected_versions(), + } + } +} diff --git a/crates/shirabe/src/advisory/partial_security_advisory.rs b/crates/shirabe/src/advisory/partial_security_advisory.rs index fe3c99e..1e92dec 100644 --- a/crates/shirabe/src/advisory/partial_security_advisory.rs +++ b/crates/shirabe/src/advisory/partial_security_advisory.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Advisory/PartialSecurityAdvisory.php +use crate::advisory::PartialOrFullSecurityAdvisory; use crate::advisory::SecurityAdvisory; -use crate::repository::PartialOrSecurityAdvisory; use anyhow::Result; use chrono::{DateTime, TimeZone, Utc}; use indexmap::IndexMap; @@ -18,7 +18,7 @@ fn serialize_constraint<S: serde::Serializer>( serializer.serialize_str(&c.get_pretty_string()) } -#[derive(Debug, serde::Serialize)] +#[derive(Debug, Clone, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct PartialSecurityAdvisory { pub advisory_id: String, @@ -32,7 +32,7 @@ impl PartialSecurityAdvisory { package_name: &str, data: &IndexMap<String, PhpMixed>, parser: &VersionParser, - ) -> Result<PartialOrSecurityAdvisory> { + ) -> Result<PartialOrFullSecurityAdvisory> { let affected_versions_str = data["affectedVersions"].as_string().unwrap_or(""); let constraint: AnyConstraint = match parser.parse_constraints(affected_versions_str) { @@ -81,10 +81,10 @@ impl PartialSecurityAdvisory { .and_then(|v| v.as_string()) .map(|s| s.to_string()), ); - return Ok(PartialOrSecurityAdvisory::Full(advisory)); + return Ok(PartialOrFullSecurityAdvisory::Full(advisory)); } - Ok(PartialOrSecurityAdvisory::Partial(Self { + Ok(PartialOrFullSecurityAdvisory::Partial(Self { advisory_id: data["advisoryId"].as_string().unwrap_or("").to_string(), package_name: package_name.to_string(), affected_versions: constraint, diff --git a/crates/shirabe/src/advisory/security_advisory.rs b/crates/shirabe/src/advisory/security_advisory.rs index edb161c..c766e0f 100644 --- a/crates/shirabe/src/advisory/security_advisory.rs +++ b/crates/shirabe/src/advisory/security_advisory.rs @@ -7,7 +7,7 @@ use shirabe_semver::constraint::AnyConstraint; use crate::advisory::IgnoredSecurityAdvisory; use crate::advisory::PartialSecurityAdvisory; -#[derive(Debug, serde::Serialize)] +#[derive(Debug, Clone, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct SecurityAdvisory { #[serde(flatten)] |
