vtcode_core/tools/validation/
paths.rs1use anyhow::{Result, anyhow};
2use vtcode_commons::paths::validate_path_safety as common_validate_path_safety;
3
4pub fn validate_path_safety(path: &str) -> Result<()> {
7 common_validate_path_safety(path)
8}
9
10pub fn validate_non_root_listing_path(path: Option<&str>) -> Result<()> {
16 let raw = path.unwrap_or_default();
17 let normalized = raw.trim_start_matches("./").trim_start_matches('/');
18 if normalized.is_empty() && raw != "." && raw != "./" {
19 return Err(anyhow!(
20 "Error: directory-listing path is empty. Please specify a subdirectory like 'src/', 'vtcode-core/src/', or 'tests/'."
21 ));
22 }
23
24 Ok(())
25}
26
27#[cfg(test)]
28mod tests {
29 use super::{validate_non_root_listing_path, validate_path_safety};
30
31 #[test]
32 fn allows_macos_temp_paths_under_var_folders() {
33 validate_path_safety("/var/folders/ab/cd/tmp123/file.txt").unwrap();
34 }
35
36 #[test]
37 fn still_blocks_sensitive_var_paths() {
38 assert!(validate_path_safety("/var/db/shadow").is_err());
39 assert!(validate_path_safety("/var").is_err());
40 }
41
42 #[test]
43 fn allows_non_critical_prefix_matches() {
44 validate_path_safety("/varnish/cache/file").unwrap();
45 }
46
47 #[test]
48 fn allows_var_tmp_paths() {
49 validate_path_safety("/var/tmp/vtcode/run.log").unwrap();
50 }
51
52 #[test]
53 fn blocks_empty_or_missing_paths() {
54 for candidate in [None, Some(""), Some("/")] {
55 assert!(
56 validate_non_root_listing_path(candidate).is_err(),
57 "should block {:?}",
58 candidate
59 );
60 }
61 }
62
63 #[test]
64 fn allows_root_listing_path() {
65 for candidate in [Some("."), Some("./")] {
66 validate_non_root_listing_path(candidate)
67 .unwrap_or_else(|_| panic!("should allow {:?}", candidate));
68 }
69 }
70
71 #[test]
72 fn allows_subdirectory_listing_requests() {
73 for candidate in [Some("src"), Some("./src"), Some("/workspace/src")] {
74 validate_non_root_listing_path(candidate).unwrap();
75 }
76 }
77}