rustsec/
vulnerability.rs

1//! Vulnerabilities represent the intersection of the [`Advisory`] database
2//! and a particular `Cargo.lock` file.
3
4use crate::{
5    advisory::{self, Advisory, affected::FunctionPath},
6    package::Package,
7};
8use serde::{Deserialize, Serialize};
9
10/// A vulnerable package and the associated advisory
11#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
12pub struct Vulnerability {
13    /// Security advisory for which the package is vulnerable
14    pub advisory: advisory::Metadata,
15
16    /// Versions impacted by this vulnerability
17    pub versions: advisory::Versions,
18
19    /// More specific information about what this advisory affects (if available)
20    pub affected: Option<advisory::Affected>,
21
22    /// Vulnerable package
23    pub package: Package,
24}
25
26impl Vulnerability {
27    /// Create `Vulnerability` about a given [`Advisory`] and [`Package`]
28    pub fn new(advisory: &Advisory, package: &Package) -> Self {
29        Self {
30            advisory: advisory.metadata.clone(),
31            versions: advisory.versions.clone(),
32            affected: advisory.affected.clone(),
33            package: package.clone(),
34        }
35    }
36
37    /// Get the set of functions affected by this vulnerability (if available)
38    pub fn affected_functions(&self) -> Option<Vec<FunctionPath>> {
39        self.affected.as_ref().and_then(|affected| {
40            if affected.functions.is_empty() {
41                None
42            } else {
43                let mut result = vec![];
44                for (path, versions) in &affected.functions {
45                    if versions
46                        .iter()
47                        .any(|req| req.matches(&self.package.version.clone()))
48                    {
49                        result.push(path.clone());
50                    }
51                }
52                Some(result)
53            }
54        })
55    }
56}