prevent arbitrary code execution when expanding path

This commit is contained in:
Nguyen Duc Toan 2023-10-04 00:05:07 +07:00 committed by sxyazi
parent ba3a08403b
commit 4fb835ee64
No known key found for this signature in database

View file

@ -10,11 +10,19 @@ pub fn expand_path(p: impl AsRef<Path>) -> PathBuf {
// expand the environment variable by calling the "echo" command, in linux case, this also expands the '~' path // expand the environment variable by calling the "echo" command, in linux case, this also expands the '~' path
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let expanded_path = match std::process::Command::new("cmd").args(&["/C", "echo"]).arg(p).output() { let expanded_path = match std::process::Command::new("cmd").args(&["/C", "echo"]).arg(p).output() {
Ok(output) if output.status.success() => Some(String::from_utf8_lossy(&output.stdout).trim_end().to_string()), Ok(output) if output.status.success() => Some(String::from_utf8_lossy(&output.stdout)
.trim_end()
.trim_matches('"')
.replace("\\\"", "\"")
.to_string()
),
_ => None, _ => None,
}; };
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let expanded_path = match std::process::Command::new("sh").arg("-c").arg(format!("echo {}", p.display())).output() { let expanded_path = match std::process::Command::new("sh")
.arg("-c")
.arg(format!("echo \"{}\"", p.to_string_lossy().replace("\"", "\\\"")))
.output() {
Ok(output) if output.status.success() => Some(String::from_utf8_lossy(&output.stdout).trim_end().to_string()), Ok(output) if output.status.success() => Some(String::from_utf8_lossy(&output.stdout).trim_end().to_string()),
_ => None, _ => None,
}; };
@ -160,7 +168,9 @@ pub fn optional_bool(s: &str) -> Option<bool> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{borrow::Cow, path::Path}; use std::{borrow::Cow, path::Path, env};
use crate::expand_path;
use super::path_relative_to; use super::path_relative_to;
@ -191,4 +201,19 @@ mod tests {
assert("C:\\a", "C:\\a\\b\\c", "..\\..\\"); assert("C:\\a", "C:\\a\\b\\c", "..\\..\\");
assert("C:\\a\\a\\b", "C:\\a\\b\\b", "..\\..\\a\\b"); assert("C:\\a\\a\\b", "C:\\a\\b\\b", "..\\..\\a\\b");
} }
#[test]
fn test_expand_path() {
let path_s = r#"a"b"#;
assert_eq!(expand_path(path_s), env::current_dir().unwrap().join(std::path::Path::new(path_s)));
let path_s = r#"a'b"#;
assert_eq!(expand_path(path_s), env::current_dir().unwrap().join(std::path::Path::new(path_s)));
let path_s = r#"a; x b"#;
assert_eq!(expand_path(path_s), env::current_dir().unwrap().join(std::path::Path::new(path_s)));
let path_s = r#"a && x b"#;
assert_eq!(expand_path(path_s), env::current_dir().unwrap().join(std::path::Path::new(path_s)));
}
} }