1pub mod calibration;
64pub mod cmdi;
66pub mod ldap;
68pub mod path;
70pub mod response_oracle;
72pub mod signal_body_marker;
74mod ascii_scan;
75
76pub mod signal_connection;
78pub mod signal_h2_goaway;
80pub mod signal_headers;
82pub mod signal_response_time;
84pub mod signal_status_code;
86pub mod sql;
88pub mod ssrf;
90pub mod ssti;
92pub mod traits;
94pub mod xss;
96
97use traits::PayloadOracle;
98use wafrift_grammar::grammar::PayloadType;
99
100pub struct SqlOracle {
105 pub dialect: sql::DatabaseDialect,
107}
108
109impl SqlOracle {
110 #[must_use]
112 pub fn new(dialect: sql::DatabaseDialect) -> Self {
113 Self { dialect }
114 }
115
116 #[must_use]
118 pub fn generic() -> Self {
119 Self::new(sql::DatabaseDialect::Generic)
120 }
121}
122
123impl PayloadOracle for SqlOracle {
124 fn is_semantically_valid(&self, _original: &str, transformed: &str) -> bool {
125 sql::is_valid_expression_injection(transformed, self.dialect)
126 }
127
128 fn name(&self) -> &'static str {
129 "SQL"
130 }
131}
132
133#[must_use]
143pub fn oracle_for(payload_type: PayloadType) -> Option<Box<dyn PayloadOracle>> {
144 match payload_type {
145 PayloadType::Sql => Some(Box::new(SqlOracle::generic())),
146 PayloadType::Xss => Some(Box::new(xss::XssOracle)),
147 PayloadType::TemplateInjection => Some(Box::new(ssti::SstiOracle)),
148 PayloadType::CommandInjection => Some(Box::new(cmdi::CmdiOracle)),
149 PayloadType::PathTraversal => Some(Box::new(path::PathOracle)),
150 PayloadType::Ldap => Some(Box::new(ldap::LdapOracle)),
151 PayloadType::Ssrf => Some(Box::new(ssrf::SsrfOracle)),
152 _ => None,
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn sql_oracle_adapter_valid() {
164 let oracle = SqlOracle::generic();
165 assert!(oracle.is_semantically_valid("1 OR 1=1 --", "1 OR 1=1 --",));
166 }
167
168 #[test]
169 fn sql_oracle_adapter_invalid() {
170 let oracle = SqlOracle::generic();
171 assert!(!oracle.is_semantically_valid("1 OR 1=1 --", "1 O R 1=1 --",));
172 }
173
174 #[test]
175 fn oracle_for_sql() {
176 let oracle = oracle_for(PayloadType::Sql);
177 assert!(oracle.is_some());
178 assert_eq!(oracle.as_ref().map(|o| o.name()), Some("SQL"));
179 }
180
181 #[test]
182 fn oracle_for_xss() {
183 let oracle = oracle_for(PayloadType::Xss);
184 assert!(oracle.is_some());
185 assert_eq!(oracle.as_ref().map(|o| o.name()), Some("XSS"));
186 }
187
188 #[test]
189 fn oracle_for_ssti() {
190 let oracle = oracle_for(PayloadType::TemplateInjection);
191 assert!(oracle.is_some());
192 assert_eq!(oracle.as_ref().map(|o| o.name()), Some("SSTI"));
193 }
194
195 #[test]
196 fn oracle_for_cmdi() {
197 let oracle = oracle_for(PayloadType::CommandInjection);
198 assert!(oracle.is_some());
199 assert_eq!(oracle.as_ref().map(|o| o.name()), Some("CMDI"));
200 }
201
202 #[test]
203 fn oracle_for_path() {
204 let oracle = oracle_for(PayloadType::PathTraversal);
205 assert!(oracle.is_some());
206 assert_eq!(oracle.as_ref().map(|o| o.name()), Some("PathTraversal"));
207 }
208
209 #[test]
210 fn oracle_for_unknown_is_none() {
211 let oracle = oracle_for(PayloadType::Unknown);
212 assert!(oracle.is_none());
213 }
214
215 #[test]
216 fn oracle_for_ldap() {
217 let oracle = oracle_for(PayloadType::Ldap);
218 assert!(oracle.is_some());
219 assert_eq!(oracle.as_ref().map(|o| o.name()), Some("LDAP"));
220 }
221
222 #[test]
223 fn oracle_for_ssrf() {
224 let oracle = oracle_for(PayloadType::Ssrf);
225 assert!(oracle.is_some());
226 assert_eq!(oracle.as_ref().map(|o| o.name()), Some("SSRF"));
227 }
228
229 #[test]
230 fn ldap_oracle_validates_filter_structure() {
231 let oracle = ldap::LdapOracle;
232 assert!(oracle.is_semantically_valid("(uid=admin)", "(uid=admin)"));
234 assert!(
236 oracle.is_semantically_valid("(|(uid=admin)(uid=root))", "(|(uid=admin)(uid=root))",)
237 );
238 }
239
240 #[test]
241 fn ldap_oracle_rejects_invalid() {
242 let oracle = ldap::LdapOracle;
243 assert!(!oracle.is_semantically_valid("(uid=admin)", "uid=admin"));
245 assert!(!oracle.is_semantically_valid("(uid=admin)", ""));
247 }
248
249 #[test]
250 fn ssrf_oracle_validates_url_structure() {
251 let oracle = ssrf::SsrfOracle;
252 assert!(oracle.is_semantically_valid("http://127.0.0.1/admin", "http://127.0.0.1/admin",));
254 assert!(
256 oracle.is_semantically_valid("http://169.254.169.254/", "http://169.254.169.254/",)
257 );
258 }
259
260 #[test]
261 fn ssrf_oracle_rejects_invalid() {
262 let oracle = ssrf::SsrfOracle;
263 assert!(!oracle.is_semantically_valid("http://127.0.0.1/", "127.0.0.1"));
265 assert!(!oracle.is_semantically_valid("http://127.0.0.1/", "http://example.com/"));
267 }
268}
269
270pub mod oob;