mysqladm/server/
input_sanitization.rs1use crate::core::{
2 common::UnixUser,
3 protocol::request_validation::{NameValidationError, OwnerValidationError},
4};
5
6const MAX_NAME_LENGTH: usize = 64;
7
8pub fn validate_name(name: &str) -> Result<(), NameValidationError> {
9 if name.is_empty() {
10 Err(NameValidationError::EmptyString)
11 } else if name.len() > MAX_NAME_LENGTH {
12 Err(NameValidationError::TooLong)
13 } else if !name
14 .chars()
15 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
16 {
17 Err(NameValidationError::InvalidCharacters)
18 } else {
19 Ok(())
20 }
21}
22
23pub fn validate_ownership_by_unix_user(
24 name: &str,
25 user: &UnixUser,
26) -> Result<(), OwnerValidationError> {
27 let prefixes = std::iter::once(user.username.to_owned())
28 .chain(user.groups.iter().cloned())
29 .collect::<Vec<String>>();
30
31 validate_ownership_by_prefixes(name, &prefixes)
32}
33
34pub fn validate_ownership_by_prefixes(
39 name: &str,
40 prefixes: &[String],
41) -> Result<(), OwnerValidationError> {
42 if name.is_empty() {
43 return Err(OwnerValidationError::StringEmpty);
44 }
45
46 if prefixes
47 .iter()
48 .filter(|p| name.starts_with(&(p.to_string() + "_")))
49 .collect::<Vec<_>>()
50 .is_empty()
51 {
52 return Err(OwnerValidationError::NoMatch);
53 };
54
55 Ok(())
56}
57
58#[inline]
59pub fn quote_literal(s: &str) -> String {
60 format!("'{}'", s.replace('\'', r"\'"))
61}
62
63#[inline]
64pub fn quote_identifier(s: &str) -> String {
65 format!("`{}`", s.replace('`', r"\`"))
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 #[test]
72 fn test_quote_literal() {
73 let payload = "' OR 1=1 --";
74 assert_eq!(quote_literal(payload), r#"'\' OR 1=1 --'"#);
75 }
76
77 #[test]
78 fn test_quote_identifier() {
79 let payload = "` OR 1=1 --";
80 assert_eq!(quote_identifier(payload), r#"`\` OR 1=1 --`"#);
81 }
82
83 #[test]
84 fn test_validate_name() {
85 assert_eq!(validate_name(""), Err(NameValidationError::EmptyString));
86 assert_eq!(validate_name("abcdefghijklmnopqrstuvwxyz"), Ok(()));
87 assert_eq!(validate_name("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), Ok(()));
88 assert_eq!(validate_name("0123456789_-"), Ok(()));
89
90 for c in "\n\t\r !@#$%^&*()+=[]{}|;:,.<>?/".chars() {
91 assert_eq!(
92 validate_name(&c.to_string()),
93 Err(NameValidationError::InvalidCharacters)
94 );
95 }
96
97 assert_eq!(validate_name(&"a".repeat(MAX_NAME_LENGTH)), Ok(()));
98
99 assert_eq!(
100 validate_name(&"a".repeat(MAX_NAME_LENGTH + 1)),
101 Err(NameValidationError::TooLong)
102 );
103 }
104
105 #[test]
106 fn test_validate_owner_by_prefixes() {
107 let prefixes = vec!["user".to_string(), "group".to_string()];
108
109 assert_eq!(
110 validate_ownership_by_prefixes("", &prefixes),
111 Err(OwnerValidationError::StringEmpty)
112 );
113
114 assert_eq!(
115 validate_ownership_by_prefixes("user_testdb", &prefixes),
116 Ok(())
117 );
118 assert_eq!(
119 validate_ownership_by_prefixes("group_testdb", &prefixes),
120 Ok(())
121 );
122 assert_eq!(
123 validate_ownership_by_prefixes("group_test_db", &prefixes),
124 Ok(())
125 );
126 assert_eq!(
127 validate_ownership_by_prefixes("group_test-db", &prefixes),
128 Ok(())
129 );
130
131 assert_eq!(
132 validate_ownership_by_prefixes("nonexistent_testdb", &prefixes),
133 Err(OwnerValidationError::NoMatch)
134 );
135 }
136}