use crate::{
SourceId,
advisory::{Advisory, Severity},
collection::Collection,
package::{self, Package},
};
use platforms::target::{Arch, OS};
use semver::Version;
#[derive(Clone, Debug)]
pub struct Query {
pub(super) collection: Option<Collection>,
pub(super) package_name: Option<package::Name>,
package_version: Option<Version>,
package_source: Option<SourceId>,
severity: Option<Severity>,
target_arch: Vec<Arch>,
target_os: Vec<OS>,
year: Option<u32>,
withdrawn: Option<bool>,
informational: Option<bool>,
}
impl Query {
pub fn new() -> Self {
Self {
collection: None,
package_name: None,
package_version: None,
package_source: None,
severity: None,
target_arch: Default::default(),
target_os: Default::default(),
year: None,
withdrawn: None,
informational: None,
}
}
pub fn crate_scope() -> Self {
Self::new()
.collection(Collection::Crates)
.withdrawn(false)
.informational(false)
}
pub fn collection(mut self, collection: Collection) -> Self {
self.collection = Some(collection);
self
}
#[allow(clippy::assigning_clones)]
pub fn package(mut self, package: &Package) -> Self {
self.package_name = Some(package.name.clone());
self.package_version = Some(package.version.clone());
self.package_source = package.source.clone();
self
}
pub fn package_name(mut self, name: package::Name) -> Self {
self.package_name = Some(name);
self
}
pub fn package_version(mut self, version: Version) -> Self {
self.package_version = Some(version);
self
}
pub fn package_source(mut self, source: SourceId) -> Self {
self.package_source = Some(source);
self
}
pub fn severity(mut self, severity: Severity) -> Self {
self.severity = Some(severity);
self
}
pub fn target_arch(mut self, arch: Vec<Arch>) -> Self {
self.target_arch = arch;
self
}
pub fn target_os(mut self, os: Vec<OS>) -> Self {
self.target_os = os;
self
}
pub fn year(mut self, year: u32) -> Self {
self.year = Some(year);
self
}
pub fn withdrawn(mut self, setting: bool) -> Self {
self.withdrawn = Some(setting);
self
}
pub fn informational(mut self, setting: bool) -> Self {
self.informational = Some(setting);
self
}
pub fn matches(&self, advisory: &Advisory) -> bool {
if let Some(collection) = self.collection {
if Some(collection) != advisory.metadata.collection {
return false;
}
}
if let Some(package_name) = &self.package_name {
if package_name != &advisory.metadata.package {
return false;
}
}
if let Some(package_version) = &self.package_version {
if !advisory.versions.is_vulnerable(package_version) {
return false;
}
}
if let Some(package_source) = &self.package_source {
let advisory_source = advisory
.metadata
.source
.as_ref()
.cloned()
.unwrap_or_default();
if advisory_source.kind() != package_source.kind()
|| advisory_source.url() != package_source.url()
{
return false;
}
}
if let Some(severity_threshold) = self.severity {
if let Some(advisory_severity) = advisory.severity() {
if advisory_severity < severity_threshold {
return false;
}
}
}
if let Some(affected) = &advisory.affected {
if !affected.arch.is_empty()
&& !self.target_arch.is_empty()
&& !self
.target_arch
.iter()
.any(|target_arch| affected.arch.contains(target_arch))
{
return false;
}
if !affected.os.is_empty()
&& !self.target_os.is_empty()
&& !self
.target_os
.iter()
.any(|target_os| affected.os.contains(target_os))
{
return false;
}
}
if let Some(query_year) = self.year {
if let Some(advisory_year) = advisory.metadata.id.year() {
if query_year != advisory_year {
return false;
}
}
}
if let Some(withdrawn) = self.withdrawn {
if withdrawn != advisory.metadata.withdrawn.is_some() {
return false;
}
}
if let Some(informational) = self.informational {
if informational != advisory.metadata.informational.is_some() {
return false;
}
}
true
}
}
impl Default for Query {
fn default() -> Query {
Query::crate_scope()
}
}