CVE-2021-36376 (Rust, published 2021-07-13T13:15Z)
Description: value: dandavison delta before 0.8.3 on Windows resolves an executable's pathname as a relative path from the current directory.
Severity: MEDIUM (CVSS v3 base score: 7.8)
Weakness: CWE-427 \u2014 Uncontrolled Search Path Element
The product uses a fixed or controlled search path to find resources, but one or more locations in that path can be under the control of unintended actors.
Repository: https://github.com/dandavison/delta
Files changed:
Cargo.lock
Cargo.toml
src/bat_utils/less.rs
src/bat_utils/output.rs
src/main.rs
Commit message: Do not resolve executables as relative path from current directory (#658)

* Refactor: child pager process creation

* Protect calls to Command::new behind grep_cli::resolve_binary

* Move less env-var setting into less-specific branch

Vulnerable code:
    let cmd = Command::new("less").arg("--version").output().ok()?;
    parse_less_version(&cmd.stdout)
        match pagerflags.split_first() {
                let mut process = if is_less {
                    let mut p = Command::new(&pager_path);
                    if args.is_empty() || replace_arguments_to_less {
                        p.args(vec!["--RAW-CONTROL-CHARS"]);

                        // Passing '--no-init' fixes a bug with '--quit-if-one-screen' in older
                        // versions of 'less'. Unfortunately, it also breaks mouse-wheel support.
                        //
                        // See: http://www.greenwoodsoftware.com/less/news.530.html
                        //
                        // For newer versions (530 or 558 on Windows), we omit '--no-init' as it
                        // is not needed anymore.
                        match retrieve_less_version() {
                            None => {
                                p.arg("--no-init");
                            }
                            Some(version)
                                if (version < 530 || (cfg!(windows) && version < 558)) =>
                            {
                                p.arg("--no-init");
                            }
                            _ => {}
                        }

                        if quit_if_one_screen {
                            p.arg("--quit-if-one-screen");
                        }
                    } else {
                        p.args(args);
                    }
                    p.env("LESSCHARSET", "UTF-8");
                    p
                    if pager_path.file_stem() == Some(&OsString::from("delta")) {
                        eprintln!(
                            "\
It looks like you have set delta as the value of $PAGER. \
This would result in a non-terminating recursion. \
delta is not an appropriate value for $PAGER \
(but it is an appropriate value for $GIT_PAGER)."
                        );
                        std::process::exit(1);
                    }
                    let mut p = Command::new(&pager_path);
                    p.args(args);
                    p
                if is_less && config.navigate {
                    if let Ok(hist_file) =
                        navigate::copy_less_hist_file_and_append_navigate_regexp(config)
                    {
                        process.env("LESSHISTFILE", hist_file);
                        if config.show_themes {
                            process.arg("+n");
                        }
                    }
                Ok(process
                    .env("LESSANSIENDCHARS", "mK")
                    .stdin(Stdio::piped())
                    .spawn()
                    .map(OutputType::Pager)
                    .unwrap_or_else(|_| OutputType::stdout()))
            None => Ok(OutputType::stdout()),
        }
    let mut diff_process = process::Command::new(PathBuf::from(diff_command))

Fixed code:
[[package]]
name = "bstr"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
dependencies = [
 "lazy_static",
 "memchr",
 "regex-automata",
]

 "grep-cli",
[[package]]
name = "globset"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd"
dependencies = [
 "aho-corasick",
 "bstr",
 "fnv",
 "log",
 "regex",
]

[[package]]
name = "grep-cli"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dd110c34bb4460d0de5062413b773e385cbf8a85a63fc535590110a09e79e8a"
dependencies = [
 "atty",
 "bstr",
 "globset",
 "lazy_static",
 "log",
 "regex",
 "same-file",
 "termcolor",
 "winapi-util",
]

[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"

[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
 "winapi-util",
]

grep-cli = "0.1.6"
    if let Ok(less_path) = grep_cli::resolve_binary("less") {
        let cmd = Command::new(less_path).arg("--version").output().ok()?;
        parse_less_version(&cmd.stdout)
    } else {
        None
    }
        Ok(match pagerflags.split_first() {
                let process = if is_less {
                    _make_process_from_less_path(
                        pager_path,
                        args,
                        replace_arguments_to_less,
                        quit_if_one_screen,
                        config,
                    )
                    _make_process_from_pager_path(pager_path, args)
                if let Some(mut process) = process {
                    process
                        .stdin(Stdio::piped())
                        .spawn()
                        .map(OutputType::Pager)
                        .unwrap_or_else(|_| OutputType::stdout())
                } else {
                    OutputType::stdout()
            None => OutputType::stdout(),
        })
fn _make_process_from_less_path(
    less_path: PathBuf,
    args: &[String],
    replace_arguments_to_less: bool,
    quit_if_one_screen: bool,
    config: &config::Config,
) -> Option<Command> {
    if let Ok(less_path) = grep_cli::resolve_binary(less_path) {
        let mut p = Command::new(&less_path);
        if args.is_empty() || replace_arguments_to_less {
            p.args(vec!["--RAW-CONTROL-CHARS"]);

            // Passing '--no-init' fixes a bug with '--quit-if-one-screen' in older
            // versions of 'less'. Unfortunately, it also breaks mouse-wheel support.
            //
            // See: http://www.greenwoodsoftware.com/less/news.530.html
            //
            // For newer versions (530 or 558 on Windows), we omit '--no-init' as it
            // is not needed anymore.
            match retrieve_less_version() {
                None => {
                    p.arg("--no-init");
                }
                Some(version) if (version < 530 || (cfg!(windows) && version < 558)) => {
                    p.arg("--no-init");
                }
                _ => {}
            }

            if quit_if_one_screen {
                p.arg("--quit-if-one-screen");
            }
        } else {
            p.args(args);
        }
        p.env("LESSCHARSET", "UTF-8");
        p.env("LESSANSIENDCHARS", "mK");
        if config.navigate {
            if let Ok(hist_file) = navigate::copy_less_hist_file_and_append_navigate_regexp(config)
            {
                p.env("LESSHISTFILE", hist_file);
                if config.show_themes {
                    p.arg("+n");
                }
            }
        }
        Some(p)
    } else {
        None
    }
}

fn _make_process_from_pager_path(pager_path: PathBuf, args: &[String]) -> Option<Command> {
    if pager_path.file_stem() == Some(&OsString::from("delta")) {
        eprintln!(
            "\
It looks like you have set delta as the value of $PAGER. \
This would result in a non-terminating recursion. \
delta is not an appropriate value for $PAGER \
(but it is an appropriate value for $GIT_PAGER)."
        );
        std::process::exit(1);
    }
    if let Ok(pager_path) = grep_cli::resolve_binary(pager_path) {
        let mut p = Command::new(&pager_path);
        p.args(args);
        Some(p)
    } else {
        None
    }
}

    let diff_command_path = match grep_cli::resolve_binary(PathBuf::from(diff_command)) {
        Ok(path) => path,
        Err(_) => return config.error_exit_code,
    };
    let mut diff_process = process::Command::new(diff_command_path)