use zentinel_modsec::ModSecurity;
fn blocked(rules: &str, uri: &str, method: &str) -> bool {
let msc = ModSecurity::from_string(rules).expect("rules should load");
let mut tx = msc.new_transaction();
tx.process_uri(uri, method, "HTTP/1.1").unwrap();
tx.add_request_header("Host", "example.com").unwrap();
tx.process_request_headers().unwrap();
tx.process_request_body().unwrap();
tx.has_intervention()
}
const CHAIN: &str = "SecRuleEngine On\n\
SecRule REQUEST_URI \"@contains admin\" \"id:1,phase:1,deny,chain\"\n\
SecRule REQUEST_METHOD \"@streq GET\"";
#[test]
fn chain_blocks_when_every_link_matches() {
assert!(blocked(CHAIN, "/admin", "GET"));
}
#[test]
fn chain_does_not_block_when_a_later_link_fails() {
assert!(!blocked(CHAIN, "/admin", "POST"));
}
#[test]
fn chain_does_not_block_when_the_starter_fails() {
assert!(!blocked(CHAIN, "/public", "GET"));
}
#[test]
fn continuation_is_not_evaluated_as_a_standalone_rule() {
assert!(!blocked(CHAIN, "/public", "GET"));
}
#[test]
fn three_link_chain_requires_all_three() {
let rules = "SecRuleEngine On\n\
SecRule REQUEST_URI \"@contains admin\" \"id:1,phase:1,deny,chain\"\n\
SecRule REQUEST_METHOD \"@streq GET\" \"chain\"\n\
SecRule REQUEST_HEADERS:Host \"@streq example.com\"";
assert!(blocked(rules, "/admin", "GET"));
assert!(!blocked(rules, "/admin", "POST"));
assert!(!blocked(rules, "/public", "GET"));
}
#[test]
fn setvar_on_the_starter_only_applies_when_the_chain_completes() {
let rules = "SecRuleEngine On\n\
SecRule REQUEST_URI \"@contains admin\" \"id:1,phase:1,pass,nolog,setvar:tx.score=5,chain\"\n\
SecRule REQUEST_METHOD \"@streq GET\"\n\
SecRule TX:score \"@ge 5\" \"id:2,phase:1,deny\"";
assert!(blocked(rules, "/admin", "GET"), "complete chain should set the score");
assert!(
!blocked(rules, "/admin", "POST"),
"partial chain match must not apply the starter's setvar"
);
}
#[test]
fn rules_after_a_failed_chain_still_run() {
let rules = "SecRuleEngine On\n\
SecRule REQUEST_URI \"@contains admin\" \"id:1,phase:1,deny,chain\"\n\
SecRule REQUEST_METHOD \"@streq POST\"\n\
SecRule REQUEST_URI \"@contains admin\" \"id:2,phase:1,deny\"";
assert!(blocked(rules, "/admin", "GET"));
}