pub mod calibration;
pub mod cmdi;
pub mod ldap;
pub mod path;
pub mod response_oracle;
pub mod signal_body_marker;
pub mod signal_connection;
pub mod signal_h2_goaway;
pub mod signal_headers;
pub mod signal_response_time;
pub mod signal_status_code;
pub mod sql;
pub mod ssrf;
pub mod ssti;
pub mod traits;
pub mod xss;
use traits::PayloadOracle;
use wafrift_grammar::grammar::PayloadType;
pub struct SqlOracle {
pub dialect: sql::DatabaseDialect,
}
impl SqlOracle {
#[must_use]
pub fn new(dialect: sql::DatabaseDialect) -> Self {
Self { dialect }
}
#[must_use]
pub fn generic() -> Self {
Self::new(sql::DatabaseDialect::Generic)
}
}
impl PayloadOracle for SqlOracle {
fn is_semantically_valid(&self, _original: &str, transformed: &str) -> bool {
sql::is_valid_expression_injection(transformed, self.dialect)
}
fn name(&self) -> &'static str {
"SQL"
}
}
#[must_use]
pub fn oracle_for(payload_type: PayloadType) -> Option<Box<dyn PayloadOracle>> {
match payload_type {
PayloadType::Sql => Some(Box::new(SqlOracle::generic())),
PayloadType::Xss => Some(Box::new(xss::XssOracle)),
PayloadType::TemplateInjection => Some(Box::new(ssti::SstiOracle)),
PayloadType::CommandInjection => Some(Box::new(cmdi::CmdiOracle)),
PayloadType::PathTraversal => Some(Box::new(path::PathOracle)),
PayloadType::Ldap => Some(Box::new(ldap::LdapOracle)),
PayloadType::Ssrf => Some(Box::new(ssrf::SsrfOracle)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sql_oracle_adapter_valid() {
let oracle = SqlOracle::generic();
assert!(oracle.is_semantically_valid("1 OR 1=1 --", "1 OR 1=1 --",));
}
#[test]
fn sql_oracle_adapter_invalid() {
let oracle = SqlOracle::generic();
assert!(!oracle.is_semantically_valid("1 OR 1=1 --", "1 O R 1=1 --",));
}
#[test]
fn oracle_for_sql() {
let oracle = oracle_for(PayloadType::Sql);
assert!(oracle.is_some());
assert_eq!(oracle.as_ref().map(|o| o.name()), Some("SQL"));
}
#[test]
fn oracle_for_xss() {
let oracle = oracle_for(PayloadType::Xss);
assert!(oracle.is_some());
assert_eq!(oracle.as_ref().map(|o| o.name()), Some("XSS"));
}
#[test]
fn oracle_for_ssti() {
let oracle = oracle_for(PayloadType::TemplateInjection);
assert!(oracle.is_some());
assert_eq!(oracle.as_ref().map(|o| o.name()), Some("SSTI"));
}
#[test]
fn oracle_for_cmdi() {
let oracle = oracle_for(PayloadType::CommandInjection);
assert!(oracle.is_some());
assert_eq!(oracle.as_ref().map(|o| o.name()), Some("CMDI"));
}
#[test]
fn oracle_for_path() {
let oracle = oracle_for(PayloadType::PathTraversal);
assert!(oracle.is_some());
assert_eq!(oracle.as_ref().map(|o| o.name()), Some("PathTraversal"));
}
#[test]
fn oracle_for_unknown_is_none() {
let oracle = oracle_for(PayloadType::Unknown);
assert!(oracle.is_none());
}
#[test]
fn oracle_for_ldap() {
let oracle = oracle_for(PayloadType::Ldap);
assert!(oracle.is_some());
assert_eq!(oracle.as_ref().map(|o| o.name()), Some("LDAP"));
}
#[test]
fn oracle_for_ssrf() {
let oracle = oracle_for(PayloadType::Ssrf);
assert!(oracle.is_some());
assert_eq!(oracle.as_ref().map(|o| o.name()), Some("SSRF"));
}
#[test]
fn ldap_oracle_validates_filter_structure() {
let oracle = ldap::LdapOracle;
assert!(oracle.is_semantically_valid("(uid=admin)", "(uid=admin)"));
assert!(
oracle.is_semantically_valid("(|(uid=admin)(uid=root))", "(|(uid=admin)(uid=root))",)
);
}
#[test]
fn ldap_oracle_rejects_invalid() {
let oracle = ldap::LdapOracle;
assert!(!oracle.is_semantically_valid("(uid=admin)", "uid=admin"));
assert!(!oracle.is_semantically_valid("(uid=admin)", ""));
}
#[test]
fn ssrf_oracle_validates_url_structure() {
let oracle = ssrf::SsrfOracle;
assert!(oracle.is_semantically_valid("http://127.0.0.1/admin", "http://127.0.0.1/admin",));
assert!(
oracle.is_semantically_valid("http://169.254.169.254/", "http://169.254.169.254/",)
);
}
#[test]
fn ssrf_oracle_rejects_invalid() {
let oracle = ssrf::SsrfOracle;
assert!(!oracle.is_semantically_valid("http://127.0.0.1/", "127.0.0.1"));
assert!(!oracle.is_semantically_valid("http://127.0.0.1/", "http://example.com/"));
}
}