Skip to main content

ai_agent/utils/settings/
tool_validation_config.rs

1// Source: ~/claudecode/openclaudecode/src/utils/settings/toolValidationConfig.ts
2//! Tool validation configuration for permission rule patterns.
3
4/// Tools that accept file glob patterns (e.g., *.ts, src/**)
5const FILE_PATTERN_TOOLS: &[&str] = &[
6    "Read", "Write", "Edit", "Glob", "NotebookRead", "NotebookEdit",
7];
8
9/// Tools that accept bash wildcard patterns and legacy :* prefix syntax
10const BASH_PREFIX_TOOLS: &[&str] = &["Bash"];
11
12/// Check if a tool uses file glob patterns
13pub fn is_file_pattern_tool(tool_name: &str) -> bool {
14    FILE_PATTERN_TOOLS
15        .iter()
16        .any(|t| t.eq_ignore_ascii_case(tool_name))
17}
18
19/// Check if a tool uses bash prefix patterns
20pub fn is_bash_prefix_tool(tool_name: &str) -> bool {
21    BASH_PREFIX_TOOLS
22        .iter()
23        .any(|t| t.eq_ignore_ascii_case(tool_name))
24}
25
26/// Custom validation result
27pub type CustomValidationResult = CustomValidateResult;
28
29/// Result of a custom validation check
30#[derive(Debug, Clone)]
31pub struct CustomValidateResult {
32    pub valid: bool,
33    pub error: Option<String>,
34    pub suggestion: Option<String>,
35    pub examples: Option<Vec<String>>,
36}
37
38/// Get custom validation for a specific tool
39pub fn get_custom_validation(content: &str) -> Option<CustomValidateResult> {
40    // WebSearch doesn't support wildcards or complex patterns
41    if content.starts_with("WebSearch") {
42        let check = if content.contains('*') || content.contains('?') {
43            CustomValidateResult {
44                valid: false,
45                error: Some("WebSearch does not support wildcards".into()),
46                suggestion: Some("Use exact search terms without * or ?".into()),
47                examples: Some(vec![
48                    "WebSearch(claude ai)".into(),
49                    "WebSearch(typescript tutorial)".into(),
50                ]),
51            }
52        } else {
53            CustomValidateResult {
54                valid: true,
55                error: None,
56                suggestion: None,
57                examples: None,
58            }
59        };
60        return Some(check);
61    }
62
63    // WebFetch uses domain: prefix for hostname-based permissions
64    if content.starts_with("WebFetch") {
65        if content.contains("://") || content.starts_with("http") {
66            return Some(CustomValidateResult {
67                valid: false,
68                error: Some("WebFetch permissions use domain format, not URLs".into()),
69                suggestion: Some("Use \"domain:hostname\" format".into()),
70                examples: Some(vec![
71                    "WebFetch(domain:example.com)".into(),
72                    "WebFetch(domain:github.com)".into(),
73                ]),
74            });
75        }
76        if !content.contains("domain:") {
77            return Some(CustomValidateResult {
78                valid: false,
79                error: Some("WebFetch permissions must use \"domain:\" prefix".into()),
80                suggestion: Some("Use \"domain:hostname\" format".into()),
81                examples: Some(vec![
82                    "WebFetch(domain:example.com)".into(),
83                    "WebFetch(domain:*.google.com)".into(),
84                ]),
85            });
86        }
87        return Some(CustomValidateResult {
88            valid: true,
89            error: None,
90            suggestion: None,
91            examples: None,
92        });
93    }
94
95    None
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_is_file_pattern_tool() {
104        assert!(is_file_pattern_tool("Read"));
105        assert!(is_file_pattern_tool("Write"));
106        assert!(is_file_pattern_tool("Edit"));
107        assert!(is_file_pattern_tool("Glob"));
108        assert!(!is_file_pattern_tool("Bash"));
109        assert!(!is_file_pattern_tool("WebSearch"));
110    }
111
112    #[test]
113    fn test_is_bash_prefix_tool() {
114        assert!(is_bash_prefix_tool("Bash"));
115        assert!(!is_bash_prefix_tool("Read"));
116        assert!(!is_bash_prefix_tool("WebSearch"));
117    }
118
119    #[test]
120    fn test_custom_validation_websearch() {
121        let result = get_custom_validation("WebSearch(claude ai)");
122        assert!(result.is_none() || result.unwrap().valid);
123
124        // Wildcards should fail
125        let result = get_custom_validation("WebSearch(claude*)");
126        assert!(result.is_some());
127        let result = result.unwrap();
128        assert!(!result.valid);
129    }
130
131    #[test]
132    fn test_custom_validation_webfetch() {
133        // Valid domain format
134        let result = get_custom_validation("WebFetch(domain:example.com)");
135        assert!(result.is_some());
136        assert!(result.unwrap().valid);
137
138        // URL format should fail
139        let result = get_custom_validation("WebFetch(https://example.com)");
140        assert!(result.is_some());
141        assert!(!result.unwrap().valid);
142    }
143
144    #[test]
145    fn test_no_custom_validation_for_other_tools() {
146        assert!(get_custom_validation("Bash(npm install)").is_none());
147        assert!(get_custom_validation("Read(*.ts)").is_none());
148    }
149}