aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/advisory
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/advisory')
-rw-r--r--crates/shirabe/src/advisory/audit_config.rs8
-rw-r--r--crates/shirabe/src/advisory/auditor.rs145
-rw-r--r--crates/shirabe/src/advisory/ignored_security_advisory.rs15
-rw-r--r--crates/shirabe/src/advisory/partial_security_advisory.rs17
-rw-r--r--crates/shirabe/src/advisory/security_advisory.rs7
5 files changed, 76 insertions, 116 deletions
diff --git a/crates/shirabe/src/advisory/audit_config.rs b/crates/shirabe/src/advisory/audit_config.rs
index f75d499..bdf3e8d 100644
--- a/crates/shirabe/src/advisory/audit_config.rs
+++ b/crates/shirabe/src/advisory/audit_config.rs
@@ -6,7 +6,7 @@ use shirabe_php_shim::{InvalidArgumentException, PhpMixed};
use crate::advisory::auditor::Auditor;
use crate::config::Config;
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct AuditConfig {
pub audit: bool,
pub audit_format: String,
@@ -123,7 +123,11 @@ impl AuditConfig {
Ok((for_audit, for_block))
}
- pub fn from_config(config: &Config, audit: bool, audit_format: &str) -> anyhow::Result<Self> {
+ pub fn from_config(
+ config: &mut Config,
+ audit: bool,
+ audit_format: &str,
+ ) -> anyhow::Result<Self> {
let audit_config_raw = config.get("audit");
let audit_config = audit_config_raw.as_array();
diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs
index e1b40c2..68bedb0 100644
--- a/crates/shirabe/src/advisory/auditor.rs
+++ b/crates/shirabe/src/advisory/auditor.rs
@@ -70,7 +70,7 @@ impl Auditor {
/// @throws InvalidArgumentException If no packages are passed in
pub fn audit(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
repo_set: &RepositorySet,
packages: Vec<Box<dyn PackageInterface>>,
format: &str,
@@ -170,17 +170,13 @@ impl Auditor {
),
);
- io.write(
- PhpMixed::String(JsonFile::encode(
- &PhpMixed::Array(json.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
- shirabe_php_shim::JSON_UNESCAPED_SLASHES
- | shirabe_php_shim::JSON_PRETTY_PRINT
- | shirabe_php_shim::JSON_UNESCAPED_UNICODE,
- JsonFile::INDENT_DEFAULT,
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write(&JsonFile::encode_with_indent(
+ &PhpMixed::Array(json.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
+ shirabe_php_shim::JSON_UNESCAPED_SLASHES
+ | shirabe_php_shim::JSON_PRETTY_PRINT
+ | shirabe_php_shim::JSON_UNESCAPED_UNICODE,
+ JsonFile::INDENT_DEFAULT,
+ ));
return Ok(audit_bitmask);
}
@@ -214,57 +210,31 @@ impl Auditor {
};
let pkg_plurality = if pkg_count == 1 { "" } else { "s" };
let punctuation = if format == "summary" { "." } else { ":" };
- io.write_error(
- PhpMixed::String(sprintf(
- &message,
- &[
- PhpMixed::Int(total_advisory_count),
- PhpMixed::String(plurality.to_string()),
- PhpMixed::Int(pkg_count),
- PhpMixed::String(pkg_plurality.to_string()),
- PhpMixed::String(punctuation.to_string()),
- ],
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&sprintf(
+ &message,
+ &[
+ PhpMixed::Int(total_advisory_count),
+ PhpMixed::String(plurality.to_string()),
+ PhpMixed::Int(pkg_count),
+ PhpMixed::String(pkg_plurality.to_string()),
+ PhpMixed::String(punctuation.to_string()),
+ ],
+ ));
self.output_advisories(io, advisories_to_output, format)?;
}
}
if format == Self::FORMAT_SUMMARY {
- io.write_error(
- PhpMixed::String(
- "Run \"composer audit\" for a full list of advisories.".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error("Run \"composer audit\" for a full list of advisories.");
}
} else {
- io.write_error(
- PhpMixed::String(
- "<info>No security vulnerability advisories found.</info>".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error("<info>No security vulnerability advisories found.</info>");
}
if !unreachable_repos.is_empty() {
- io.write_error(
- PhpMixed::String(
- "<warning>The following repositories were unreachable:</warning>".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error("<warning>The following repositories were unreachable:</warning>");
for repo in &unreachable_repos {
- io.write_error(
- PhpMixed::String(format!(" - {}", repo)),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&format!(" - {}", repo));
}
}
@@ -458,7 +428,7 @@ impl Auditor {
/// @param self::FORMAT_* $format The format that will be used to output audit results.
fn output_advisories(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
advisories: &IndexMap<String, Vec<PartialSecurityAdvisory>>,
format: &str,
) -> Result<()> {
@@ -534,8 +504,8 @@ impl Auditor {
}
let _ = row;
io.get_table()
- .set_horizontal()
- .set_headers(headers)
+ .set_horizontal(true)
+ .set_headers(headers.into_iter().map(|h| h.into()).collect())
.add_row(ConsoleIO::sanitize(PhpMixed::Null, false))
.set_column_width(1, 80)
.set_column_max_width(1, 80)
@@ -547,7 +517,7 @@ impl Auditor {
/// @param array<string, array<SecurityAdvisory>> $advisories
fn output_advisories_plain(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
advisories: &IndexMap<String, Vec<PartialSecurityAdvisory>>,
) {
let mut error: Vec<String> = vec![];
@@ -596,41 +566,30 @@ impl Auditor {
first_advisory = false;
}
}
- io.write_error(
- PhpMixed::List(
- error
- .into_iter()
- .map(|s| Box::new(PhpMixed::String(s)))
- .collect(),
- ),
- true,
- io_interface::NORMAL,
- );
+ for line in &error {
+ io.write_error(line);
+ }
}
/// @param array<CompletePackageInterface> $packages
/// @param self::FORMAT_PLAIN|self::FORMAT_TABLE $format
fn output_abandoned_packages(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
packages: &[Box<dyn CompletePackageInterface>],
format: &str,
) -> Result<()> {
- io.write_error(
- PhpMixed::String(sprintf(
- "<error>Found %d abandoned package%s:</error>",
- &[
- PhpMixed::Int(packages.len() as i64),
- PhpMixed::String(if packages.len() > 1 {
- "s".to_string()
- } else {
- String::new()
- }),
- ],
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&sprintf(
+ "<error>Found %d abandoned package%s:</error>",
+ &[
+ PhpMixed::Int(packages.len() as i64),
+ PhpMixed::String(if packages.len() > 1 {
+ "s".to_string()
+ } else {
+ String::new()
+ }),
+ ],
+ ));
if format == Self::FORMAT_PLAIN {
for pkg in packages {
@@ -639,17 +598,13 @@ impl Auditor {
} else {
"No replacement was suggested".to_string()
};
- io.write_error(
- PhpMixed::String(sprintf(
- "%s is abandoned. %s.",
- &[
- PhpMixed::String(self.get_package_name_with_link_for_complete(pkg)),
- PhpMixed::String(replacement),
- ],
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&sprintf(
+ "%s is abandoned. %s.",
+ &[
+ PhpMixed::String(self.get_package_name_with_link_for_complete(pkg)),
+ PhpMixed::String(replacement),
+ ],
+ ));
}
return Ok(());
@@ -672,8 +627,8 @@ impl Auditor {
.unwrap()
.get_table()
.set_headers(vec![
- "Abandoned Package".to_string(),
- "Suggested Replacement".to_string(),
+ "Abandoned Package".to_string().into(),
+ "Suggested Replacement".to_string().into(),
])
.set_column_width(1, 80)
.set_column_max_width(1, 80);
diff --git a/crates/shirabe/src/advisory/ignored_security_advisory.rs b/crates/shirabe/src/advisory/ignored_security_advisory.rs
index 7ed3a4c..03e7d78 100644
--- a/crates/shirabe/src/advisory/ignored_security_advisory.rs
+++ b/crates/shirabe/src/advisory/ignored_security_advisory.rs
@@ -6,9 +6,12 @@ use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
-#[derive(Debug)]
+#[derive(Debug, serde::Serialize)]
+#[serde(rename_all = "camelCase")]
pub struct IgnoredSecurityAdvisory {
+ #[serde(flatten)]
inner: SecurityAdvisory,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub ignore_reason: Option<String>,
}
@@ -41,14 +44,4 @@ impl IgnoredSecurityAdvisory {
ignore_reason,
}
}
-
- pub fn json_serialize(&self) -> PhpMixed {
- let mut data = self.inner.json_serialize();
- if self.ignore_reason.is_none() {
- if let PhpMixed::Array(ref mut map) = data {
- map.remove("ignoreReason");
- }
- }
- data
- }
}
diff --git a/crates/shirabe/src/advisory/partial_security_advisory.rs b/crates/shirabe/src/advisory/partial_security_advisory.rs
index e7aa96e..a47ec02 100644
--- a/crates/shirabe/src/advisory/partial_security_advisory.rs
+++ b/crates/shirabe/src/advisory/partial_security_advisory.rs
@@ -1,6 +1,7 @@
//! ref: composer/src/Composer/Advisory/PartialSecurityAdvisory.php
use crate::advisory::security_advisory::SecurityAdvisory;
+use crate::repository::advisory_provider_interface::PartialOrSecurityAdvisory;
use anyhow::Result;
use chrono::{DateTime, TimeZone, Utc};
use indexmap::IndexMap;
@@ -31,7 +32,7 @@ impl PartialSecurityAdvisory {
package_name: &str,
data: &IndexMap<String, PhpMixed>,
parser: &VersionParser,
- ) -> Result<Box<dyn std::any::Any>> {
+ ) -> Result<PartialOrSecurityAdvisory> {
let affected_versions_str = data["affectedVersions"].as_string().unwrap_or("");
let constraint: Box<dyn ConstraintInterface> =
@@ -40,9 +41,12 @@ impl PartialSecurityAdvisory {
Err(_) => {
let affected_version =
Preg::replace(r"(^[>=<^~]*[\d.]+).*", "$1", affected_versions_str);
- match parser.parse_constraints(&affected_version) {
+ match parser.parse_constraints(affected_version.as_deref().unwrap_or("")) {
Ok(c) => c,
- Err(_) => Box::new(Constraint::new("==", "0.0.0-invalid-version")),
+ Err(_) => Box::new(Constraint::new(
+ "==".to_string(),
+ "0.0.0-invalid-version".to_string(),
+ )),
}
}
};
@@ -63,7 +67,8 @@ impl PartialSecurityAdvisory {
data["advisoryId"].as_string().unwrap_or("").to_string(),
constraint,
data["title"].as_string().unwrap_or("").to_string(),
- data["sources"].clone(),
+ // TODO(phase-b): parse PhpMixed sources array into Vec<IndexMap<String, String>>
+ todo!(),
reported_at,
data.get("cve")
.and_then(|v| v.as_string())
@@ -75,10 +80,10 @@ impl PartialSecurityAdvisory {
.and_then(|v| v.as_string())
.map(|s| s.to_string()),
);
- return Ok(Box::new(advisory));
+ return Ok(PartialOrSecurityAdvisory::Full(advisory));
}
- Ok(Box::new(Self {
+ Ok(PartialOrSecurityAdvisory::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 049169d..1b1ff64 100644
--- a/crates/shirabe/src/advisory/security_advisory.rs
+++ b/crates/shirabe/src/advisory/security_advisory.rs
@@ -44,12 +44,15 @@ impl SecurityAdvisory {
}
}
+ pub fn affected_versions(&self) -> &dyn ConstraintInterface {
+ &*self.inner.affected_versions
+ }
+
pub fn to_ignored_advisory(&self, ignore_reason: Option<String>) -> IgnoredSecurityAdvisory {
IgnoredSecurityAdvisory::new(
self.inner.package_name.clone(),
self.inner.advisory_id.clone(),
- // TODO: Phase B - handle shared ownership of affected_versions
- self.inner.affected_versions.clone(),
+ self.inner.affected_versions.clone_box(),
self.title.clone(),
self.sources.clone(),
self.reported_at,