Skip to main content

tryaudex_core/
validate.rs

1use crate::error::{AvError, Result};
2
3/// Validate that a session ID is safe for use in file paths and CLI arguments.
4///
5/// Session IDs are generated as UUIDs internally, but external callers
6/// (server API, MCP, CLI prefix lookups) can supply arbitrary strings.
7/// This rejects path traversal (`../`), shell metacharacters, and
8/// anything that isn't alphanumeric or a hyphen.
9pub fn session_id(id: &str) -> Result<()> {
10    if id.is_empty() || id.len() > 64 {
11        return Err(AvError::SessionNotFound { id: id.to_string() });
12    }
13    if !id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-') {
14        return Err(AvError::SessionNotFound { id: id.to_string() });
15    }
16    Ok(())
17}
18
19/// Validate that a path component (e.g., Vault mount or role) is safe.
20///
21/// Rejects `/`, `\`, `..`, and empty strings to prevent path traversal
22/// in URL paths or file system operations.
23pub fn path_component(s: &str, label: &str) -> Result<()> {
24    if s.is_empty() || s.contains('/') || s.contains('\\') || s.contains("..") {
25        return Err(AvError::InvalidPolicy(format!(
26            "Invalid {}: contains path traversal characters",
27            label
28        )));
29    }
30    Ok(())
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn valid_uuid_session_id() {
39        assert!(session_id("a1b2c3d4-e5f6-7890-abcd-ef1234567890").is_ok());
40    }
41
42    #[test]
43    fn valid_short_prefix() {
44        assert!(session_id("a1b2c3d4").is_ok());
45    }
46
47    #[test]
48    fn rejects_path_traversal() {
49        assert!(session_id("../../etc/passwd").is_err());
50    }
51
52    #[test]
53    fn rejects_slashes() {
54        assert!(session_id("foo/bar").is_err());
55    }
56
57    #[test]
58    fn rejects_empty() {
59        assert!(session_id("").is_err());
60    }
61
62    #[test]
63    fn rejects_shell_metacharacters() {
64        assert!(session_id("$(whoami)").is_err());
65        assert!(session_id("id;rm -rf /").is_err());
66    }
67
68    #[test]
69    fn path_component_valid() {
70        assert!(path_component("my-mount", "mount").is_ok());
71        assert!(path_component("my_role", "role").is_ok());
72    }
73
74    #[test]
75    fn path_component_rejects_traversal() {
76        assert!(path_component("../../sys/seal", "role").is_err());
77        assert!(path_component("foo/bar", "mount").is_err());
78        assert!(path_component("", "mount").is_err());
79    }
80}