Thanks to visit codestin.com
Credit goes to docs.rs

rustsec/
fixer.rs

1//! Automatically attempt to fix vulnerable dependencies
2//!
3//! This module is **experimental**, and its behavior may change in the future.
4
5use crate::vulnerability::Vulnerability;
6use cargo_lock::{Lockfile, Package};
7use std::path::{Path, PathBuf};
8use std::process::Command;
9
10/// Auto-fixer for vulnerable dependencies
11#[cfg_attr(docsrs, doc(cfg(feature = "fix")))]
12pub struct Fixer {
13    lockfile: Lockfile,
14    manifest_path: Option<PathBuf>,
15    path_to_cargo: Option<PathBuf>,
16}
17
18impl Fixer {
19    /// Create a new [`Fixer`] for the given `Cargo.lock` file
20    ///
21    /// `path_to_cargo` defaults to `cargo`, resolved in your `$PATH`.
22    ///
23    /// If the path to `Cargo.toml` is not specified, the `cargo update` command
24    /// will be run in the directory with the `Cargo.lock` file.
25    /// Leaving it blank will fix the entire workspace.
26    pub fn new(
27        cargo_lock: Lockfile,
28        cargo_toml: Option<PathBuf>,
29        path_to_cargo: Option<PathBuf>,
30    ) -> Self {
31        Self {
32            lockfile: cargo_lock,
33            manifest_path: cargo_toml,
34            path_to_cargo,
35        }
36    }
37
38    /// Returns a command that calls `cargo update` with the right arguments
39    /// to attempt to fix this vulnerability.
40    ///
41    /// Note that the success of the command does not mean
42    /// the vulnerability was actually fixed!
43    /// It may remain if no semver-compatible fix was available.
44    pub fn get_fix_command(&self, vulnerability: &Vulnerability, dry_run: bool) -> Command {
45        let cargo_path: &Path = self.path_to_cargo.as_deref().unwrap_or(Path::new("cargo"));
46        let pkg_name = &vulnerability.package.name;
47        let mut command = Command::new(cargo_path);
48        command.arg("update");
49        if let Some(path) = self.manifest_path.as_ref() {
50            command.arg("--manifest-path").arg(path);
51        }
52        if dry_run {
53            command.arg("--dry-run");
54        }
55        // there can be more than one version of a given package in the lockfile, so we need to iterate over all of them
56        for pkg in self.lockfile.packages.iter().filter(|pkg| {
57            &pkg.name == pkg_name && vulnerability.versions.is_vulnerable(&pkg.version)
58        }) {
59            let pkgid = pkgid(pkg);
60            command.arg(&pkgid);
61        }
62
63        command
64    }
65}
66
67/// Returns a Cargo unique identifier for a package.
68/// See `cargo help pkgid` for more info.
69///
70/// We need to pass these to `cargo update` because otherwise
71/// the package specification will be ambiguous, and it will refuse to do anything.
72fn pkgid(pkg: &Package) -> String {
73    match pkg.source.as_ref() {
74        Some(source) => format!("{}#{}@{}", source, pkg.name, pkg.version),
75        None => format!("{}@{}", pkg.name, pkg.version),
76    }
77}