llm_config_security/
errors.rs1use thiserror::Error;
4
5pub type SecurityResult<T> = Result<T, SecurityError>;
7
8#[derive(Error, Debug)]
10pub enum SecurityError {
11 #[error("Input validation failed: {0}")]
13 ValidationError(String),
14
15 #[error("Rate limit exceeded: {0}")]
17 RateLimitExceeded(String),
18
19 #[error("Cryptographic operation failed: {0}")]
21 CryptoError(String),
22
23 #[error("Security policy violation: {0}")]
25 PolicyViolation(String),
26
27 #[error("Authentication failed: {0}")]
29 AuthenticationError(String),
30
31 #[error("Authorization failed: {0}")]
33 AuthorizationError(String),
34
35 #[error("Invalid session: {0}")]
37 InvalidSession(String),
38
39 #[error("Suspicious activity detected: {0}")]
41 SuspiciousActivity(String),
42
43 #[error("Security configuration error: {0}")]
45 ConfigError(String),
46
47 #[error("Audit log validation failed: {0}")]
49 AuditError(String),
50
51 #[error("SQL injection attempt detected")]
53 SqlInjectionAttempt,
54
55 #[error("XSS attempt detected")]
57 XssAttempt,
58
59 #[error("Path traversal attempt detected")]
61 PathTraversalAttempt,
62
63 #[error("Command injection attempt detected")]
65 CommandInjectionAttempt,
66
67 #[error("LDAP injection attempt detected")]
69 LdapInjectionAttempt,
70
71 #[error("Regular expression DoS attempt detected")]
73 RegexDosAttempt,
74
75 #[error("Request size exceeds maximum allowed: {0} bytes")]
77 RequestTooLarge(usize),
78
79 #[error("Invalid content type: {0}")]
81 InvalidContentType(String),
82
83 #[error("Password does not meet security requirements: {0}")]
85 WeakPassword(String),
86
87 #[error("Invalid or expired token")]
89 InvalidToken,
90
91 #[error("CSRF token mismatch")]
93 CsrfTokenMismatch,
94
95 #[error("Insecure protocol: {0}")]
97 InsecureProtocol(String),
98
99 #[error("Certificate validation failed: {0}")]
101 CertificateError(String),
102
103 #[error("Security error: {0}")]
105 General(String),
106}
107
108impl SecurityError {
109 pub fn severity(&self) -> Severity {
111 match self {
112 Self::SqlInjectionAttempt
113 | Self::XssAttempt
114 | Self::CommandInjectionAttempt
115 | Self::PathTraversalAttempt
116 | Self::SuspiciousActivity(_)
117 | Self::AuthenticationError(_)
118 | Self::AuthorizationError(_) => Severity::Critical,
119
120 Self::RateLimitExceeded(_)
121 | Self::PolicyViolation(_)
122 | Self::WeakPassword(_)
123 | Self::InvalidToken
124 | Self::CsrfTokenMismatch => Severity::High,
125
126 Self::ValidationError(_)
127 | Self::RequestTooLarge(_)
128 | Self::InvalidContentType(_)
129 | Self::InvalidSession(_) => Severity::Medium,
130
131 Self::ConfigError(_)
132 | Self::AuditError(_)
133 | Self::General(_) => Severity::Low,
134
135 Self::CryptoError(_)
136 | Self::CertificateError(_)
137 | Self::InsecureProtocol(_) => Severity::High,
138
139 Self::LdapInjectionAttempt | Self::RegexDosAttempt => Severity::Critical,
140 }
141 }
142
143 pub fn should_alert(&self) -> bool {
145 matches!(self.severity(), Severity::Critical | Severity::High)
146 }
147
148 pub fn public_message(&self) -> String {
150 match self {
151 Self::SqlInjectionAttempt
153 | Self::XssAttempt
154 | Self::CommandInjectionAttempt
155 | Self::PathTraversalAttempt
156 | Self::LdapInjectionAttempt
157 | Self::RegexDosAttempt
158 | Self::SuspiciousActivity(_) => {
159 "Request rejected due to security policy".to_string()
160 }
161
162 Self::RateLimitExceeded(_) => "Rate limit exceeded. Please try again later".to_string(),
163
164 Self::AuthenticationError(_) => "Authentication failed".to_string(),
165
166 Self::AuthorizationError(_) => "Access denied".to_string(),
167
168 Self::InvalidSession(_) => "Session expired. Please log in again".to_string(),
169
170 Self::WeakPassword(_) => {
171 "Password does not meet security requirements".to_string()
172 }
173
174 Self::RequestTooLarge(_) => "Request size too large".to_string(),
175
176 _ => self.to_string(),
178 }
179 }
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
184pub enum Severity {
185 Low,
186 Medium,
187 High,
188 Critical,
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_error_severity() {
197 assert_eq!(
198 SecurityError::SqlInjectionAttempt.severity(),
199 Severity::Critical
200 );
201 assert_eq!(
202 SecurityError::RateLimitExceeded("test".to_string()).severity(),
203 Severity::High
204 );
205 assert_eq!(
206 SecurityError::ValidationError("test".to_string()).severity(),
207 Severity::Medium
208 );
209 }
210
211 #[test]
212 fn test_should_alert() {
213 assert!(SecurityError::SqlInjectionAttempt.should_alert());
214 assert!(SecurityError::WeakPassword("test".to_string()).should_alert());
215 assert!(!SecurityError::ValidationError("test".to_string()).should_alert());
216 }
217
218 #[test]
219 fn test_public_message() {
220 let err = SecurityError::SqlInjectionAttempt;
221 assert_eq!(
222 err.public_message(),
223 "Request rejected due to security policy"
224 );
225
226 let err = SecurityError::ValidationError("internal details".to_string());
227 assert!(err.public_message().contains("validation"));
228 }
229}