opendev_tools_impl/
path_utils.rs1use std::path::Path;
10
11pub use opendev_tools_core::path::{
12 expand_home, normalize_path, resolve_dir_path, resolve_file_path, strip_curdir,
13};
14
15pub fn validate_path_access(resolved: &Path, working_dir: &Path) -> Result<(), String> {
26 let normalized = normalize_path(resolved);
28
29 if normalized.starts_with(working_dir) {
31 return Ok(());
32 }
33
34 if let (Ok(canon_path), Ok(canon_wd)) = (normalized.canonicalize(), working_dir.canonicalize())
36 && canon_path.starts_with(&canon_wd)
37 {
38 return Ok(());
39 }
40
41 if let Some(home) = dirs::home_dir() {
43 let allowed_prefixes = [home.join(".opendev"), home.join(".config").join("opendev")];
44 for prefix in &allowed_prefixes {
45 if normalized.starts_with(prefix) {
46 return Ok(());
47 }
48 }
49 }
50
51 if normalized.starts_with("/tmp") || normalized.starts_with("/var/tmp") {
53 return Ok(());
54 }
55
56 Err(format!(
57 "Access denied: path '{}' is outside the project directory '{}'",
58 resolved.display(),
59 working_dir.display()
60 ))
61}
62
63pub fn is_sensitive_file(path: &Path) -> Option<&'static str> {
68 let name = path
69 .file_name()
70 .and_then(|n| n.to_str())
71 .unwrap_or("")
72 .to_lowercase();
73
74 if name == ".env"
77 || (name.starts_with(".env.") && !name.ends_with(".example") && !name.ends_with(".sample"))
78 {
79 return Some("environment file (may contain secrets)");
80 }
81
82 if name.ends_with(".pem")
84 || name.ends_with(".key")
85 || name == "id_rsa"
86 || name == "id_ed25519"
87 || name == "id_ecdsa"
88 {
89 return Some("private key file");
90 }
91
92 let credential_names = [
94 "credentials",
95 "credentials.json",
96 "credentials.yaml",
97 "credentials.yml",
98 "service-account.json",
99 ".npmrc",
100 ".pypirc",
101 ".netrc",
102 ".htpasswd",
103 ];
104 if credential_names.contains(&name.as_str()) {
105 return Some("credentials file");
106 }
107
108 if name.contains("secret")
110 && (name.ends_with(".json") || name.ends_with(".yaml") || name.ends_with(".yml"))
111 {
112 return Some("secrets file");
113 }
114
115 None
116}
117
118#[cfg(test)]
119#[path = "path_utils_tests.rs"]
120mod tests;