vika_cli/config/
validator.rs

1use crate::config::model::Config;
2use crate::error::{ConfigError, Result};
3use std::path::{Path, PathBuf};
4
5pub fn validate_config(config: &Config) -> Result<()> {
6    // Validate root_dir
7    let root_dir = PathBuf::from(&config.root_dir);
8    if root_dir.is_absolute() && !root_dir.exists() {
9        return Err(ConfigError::Invalid {
10            message: format!("Root directory does not exist: {}", config.root_dir),
11        }
12        .into());
13    }
14
15    // Validate schemas output path
16    let schemas_output = PathBuf::from(&config.schemas.output);
17    if schemas_output.is_absolute() {
18        validate_safe_path(&schemas_output)?;
19    }
20
21    // Validate apis output path
22    let apis_output = PathBuf::from(&config.apis.output);
23    if apis_output.is_absolute() {
24        validate_safe_path(&apis_output)?;
25    }
26
27    // Validate style
28    if config.apis.style != "fetch" {
29        return Err(ConfigError::Invalid {
30            message: format!(
31                "Unsupported API style: {}. Only 'fetch' is supported.",
32                config.apis.style
33            ),
34        }
35        .into());
36    }
37
38    Ok(())
39}
40
41fn validate_safe_path(path: &Path) -> Result<()> {
42    // Prevent writing to system directories
43    let path_str = path.to_string_lossy();
44
45    if path_str.contains("/etc/")
46        || path_str.contains("/usr/")
47        || path_str.contains("/bin/")
48        || path_str.contains("/sbin/")
49        || path_str.contains("/var/")
50        || path_str.contains("/opt/")
51        || path_str == "/"
52        || path_str == "/root"
53    {
54        return Err(ConfigError::InvalidOutputDirectory {
55            path: path_str.to_string(),
56        }
57        .into());
58    }
59
60    Ok(())
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use crate::config::model::Config;
67
68    #[test]
69    fn test_validate_config_valid() {
70        let config = Config::default();
71        assert!(validate_config(&config).is_ok());
72    }
73
74    #[test]
75    fn test_validate_config_invalid_style() {
76        let mut config = Config::default();
77        config.apis.style = "invalid".to_string();
78
79        let result = validate_config(&config);
80        assert!(result.is_err());
81        let error = result.unwrap_err();
82        assert!(error.to_string().contains("Unsupported API style"));
83    }
84
85    #[test]
86    fn test_validate_safe_path_etc() {
87        let path = PathBuf::from("/etc/test");
88        let result = validate_safe_path(&path);
89        assert!(result.is_err());
90    }
91
92    #[test]
93    fn test_validate_safe_path_usr() {
94        let path = PathBuf::from("/usr/test");
95        let result = validate_safe_path(&path);
96        assert!(result.is_err());
97    }
98
99    #[test]
100    fn test_validate_safe_path_bin() {
101        let path = PathBuf::from("/bin/test");
102        let result = validate_safe_path(&path);
103        assert!(result.is_err());
104    }
105
106    #[test]
107    fn test_validate_safe_path_root() {
108        let path = PathBuf::from("/");
109        let result = validate_safe_path(&path);
110        assert!(result.is_err());
111    }
112
113    #[test]
114    fn test_validate_safe_path_valid() {
115        let path = PathBuf::from("/home/user/project");
116        let result = validate_safe_path(&path);
117        assert!(result.is_ok());
118    }
119
120    #[test]
121    fn test_validate_config_absolute_paths() {
122        let mut config = Config::default();
123        config.schemas.output = "/home/user/schemas".to_string();
124        config.apis.output = "/home/user/apis".to_string();
125        
126        let result = validate_config(&config);
127        assert!(result.is_ok());
128    }
129
130    #[test]
131    fn test_validate_config_unsafe_schemas_path() {
132        let mut config = Config::default();
133        config.schemas.output = "/etc/schemas".to_string();
134        
135        let result = validate_config(&config);
136        assert!(result.is_err());
137    }
138
139    #[test]
140    fn test_validate_config_unsafe_apis_path() {
141        let mut config = Config::default();
142        config.apis.output = "/usr/apis".to_string();
143        
144        let result = validate_config(&config);
145        assert!(result.is_err());
146    }
147}