Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions make-lcov-info.bash

This file was deleted.

41 changes: 8 additions & 33 deletions src/common/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ where
}

/// Check whether a path points to a regular file and any executable flag is set
pub(crate) fn is_valid_executable(path: &PathBuf) -> bool {
pub(crate) fn is_valid_executable(path: &Path) -> bool {
if path.is_file() {
match fs::metadata(path) {
Ok(meta) => meta.mode() & 0o111 != 0,
Expand All @@ -216,12 +216,12 @@ pub(crate) fn resolve_path(command: &Path, path: &str) -> Option<PathBuf> {
// construct a possible executable absolute path candidate
.map(|path| path.join(command))
// check whether the candidate is a regular file and any executable flag is set
.find(is_valid_executable)
.find(|arg| is_valid_executable(arg))
}

#[cfg(test)]
mod tests {
use std::path::PathBuf;
use std::path::Path;

use crate::common::resolve::CurrentUser;
use crate::system::ROOT_GROUP_NAME;
Expand All @@ -234,43 +234,18 @@ mod tests {
let path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";

assert!(is_valid_executable(
&resolve_path(&PathBuf::from("yes"), path).unwrap()
&resolve_path(Path::new("yes"), path).unwrap()
));

assert!(is_valid_executable(
&resolve_path(&PathBuf::from("whoami"), path).unwrap()
&resolve_path(Path::new("whoami"), path).unwrap()
));

assert!(is_valid_executable(
&resolve_path(&PathBuf::from("env"), path).unwrap()
&resolve_path(Path::new("env"), path).unwrap()
));
assert_eq!(
resolve_path(&PathBuf::from("thisisnotonyourfs"), path),
None
);
assert_eq!(resolve_path(&PathBuf::from("thisisnotonyourfs"), "."), None);
}

#[test]
fn test_cwd_resolve_path() {
// We modify the path to contain ".", which is supposed to be ignored
let path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:.";

let cwd = std::env::current_dir().unwrap();

// we filter for executable files, so it is most likely going to pick one of the shell
// scripts in the project's root
let some_file = cwd
.read_dir()
.unwrap()
.filter_map(|entry| entry.ok())
.find_map(|entry| {
let pathbuf = PathBuf::from(entry.file_name());
is_valid_executable(&pathbuf).then_some(pathbuf)
})
.unwrap();

assert_eq!(resolve_path(&some_file, path), None);
assert_eq!(resolve_path(Path::new("thisisnotonyourfs"), path), None);
assert_eq!(resolve_path(Path::new("thisisnotonyourfs"), "."), None);
}

#[test]
Expand Down
32 changes: 32 additions & 0 deletions test-framework/sudo-compliance-tests/src/sudo/path_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,38 @@ fn can_find_command_not_visible_to_regular_user() {
.assert_success();
}

#[test]
//Cross-reference: sudoers::secure_path::if_set_searches_program_in_secure_path for
//testing that relative paths in secure_path are also not matched.
fn does_not_use_relative_paths() {
let path = "/root/my-script";
let env = Env("Defaults ignore_dot
ALL ALL=(ALL:ALL) NOPASSWD: ALL")
.user(USERNAME)
.file(path, TextFile("#!/bin/sh").chmod("100"))
.build();

let output = Command::new("sh")
.args([
"-c",
&format!("export PATH=.; cd /root; {BIN_SUDO} my-script"),
])
.output(&env);

output.assert_exit_code(1);

if sudo_test::is_original_sudo() {
assert_eq!(
output.stderr(),
"sudo: ignoring \"my-script\" found in '.'
Use \"sudo ./my-script\" if this is the \"my-script\" you wish to run."
);
} else {
//NOTE: we don't have a specialized error message for this case
assert_eq!(output.stderr(), "sudo: 'my-script': command not found");
}
}

#[test]
fn when_path_is_unset_does_not_search_in_default_path_set_for_command_execution() {
let path = "/usr/bin/my-script";
Expand Down
Loading