blob: bf10a11530a80800433399024899cc95b66be579 [file] [log] [blame]
//! The `[versions]` subsection of an advisory.
use crate::{osv, Error};
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
/// The `[versions]` subsection of an advisory: future home to information
/// about which versions are patched and/or unaffected.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(try_from = "RawVersions")]
pub struct Versions {
/// Versions which are patched and not vulnerable (expressed as semantic version requirements)
patched: Vec<VersionReq>,
/// Versions which were never affected in the first place
#[serde(default)]
unaffected: Vec<VersionReq>,
}
impl Versions {
/// Is the given version of a package vulnerable?
pub fn is_vulnerable(&self, version: &Version) -> bool {
for range in osv::ranges_for_advisory(self).iter() {
if range.affects(version) {
return true;
}
}
false
}
/// Creates a new `[versions]` entry.
/// Checks consistency of the passed version requirements.
pub fn new(patched: Vec<VersionReq>, unaffected: Vec<VersionReq>) -> Result<Self, Error> {
RawVersions {
patched,
unaffected,
}
.try_into()
}
/// Versions which are patched and not vulnerable (expressed as semantic version requirements)
pub fn patched(&self) -> &[VersionReq] {
self.patched.as_slice()
}
/// Versions which were never affected in the first place
pub fn unaffected(&self) -> &[VersionReq] {
self.unaffected.as_slice()
}
}
impl TryFrom<RawVersions> for Versions {
type Error = Error;
fn try_from(raw: RawVersions) -> Result<Self, Self::Error> {
validate_ranges(&raw)?;
Ok(Versions {
patched: raw.patched,
unaffected: raw.unaffected,
})
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
/// Raw deserialized data that didn't pass validation yet
pub(crate) struct RawVersions {
pub patched: Vec<VersionReq>,
#[serde(default)]
pub unaffected: Vec<VersionReq>,
}
fn validate_ranges(versions: &RawVersions) -> Result<(), Error> {
let _ = osv::ranges_for_unvalidated_advisory(versions)?;
Ok(())
}