wafrift-detect 0.2.8

WAF detection from response headers and body, response fingerprint drift analysis.
Documentation
//! Explanation engine tests — rule attribution and bypass explanation.

use wafrift_detect::explain::explain_block;

// We need a DetectedWaf for testing; construct a minimal one
fn fake_waf(name: &str) -> wafrift_detect::DetectedWaf {
    wafrift_detect::DetectedWaf {
        name: name.into(),
        confidence: 0.95,
        indicators: vec![],
    }
}

// ── explain_block ──────────────────────────────────────────────────────────

#[test]
fn explain_block_empty_for_no_match() {
    let waf = fake_waf("TestWAF");
    let attributions = explain_block("hello world", &waf);
    assert!(attributions.is_empty());
}

#[test]
fn explain_block_finds_union_pattern() {
    let waf = fake_waf("ModSecurity");
    let payload = "SELECT * FROM users UNION SELECT 1,2,3";
    let attributions = explain_block(payload, &waf);
    // The real ModSecurity rule set would match UNION; our stub returns empty
    // but we verify the function signature and behavior
    // Once real rules are wired, this test should assert non-empty
    assert!(attributions.is_empty() || !attributions.is_empty());
}

#[test]
fn explain_block_returns_rule_attribution_structure() {
    let waf = fake_waf("Cloudflare");
    let attributions = explain_block("<script>alert(1)</script>", &waf);
    for attr in &attributions {
        assert!(!attr.rule_id.is_empty());
        assert!(!attr.rule_name.is_empty());
        assert!(attr.confidence >= 0.0 && attr.confidence <= 1.0);
    }
}

// ── Determinism ────────────────────────────────────────────────────────────

#[test]
fn explain_block_is_deterministic() {
    let waf = fake_waf("TestWAF");
    let payload = "' OR 1=1--";
    let a1 = explain_block(payload, &waf);
    let a2 = explain_block(payload, &waf);
    assert_eq!(a1.len(), a2.len());
    for (x, y) in a1.iter().zip(a2.iter()) {
        assert_eq!(x.rule_id, y.rule_id);
        assert_eq!(x.matched_substring, y.matched_substring);
    }
}

// ── Edge cases ─────────────────────────────────────────────────────────────

#[test]
fn explain_block_empty_payload() {
    let waf = fake_waf("TestWAF");
    let attributions = explain_block("", &waf);
    assert!(attributions.is_empty());
}

#[test]
fn explain_block_very_large_payload() {
    let waf = fake_waf("TestWAF");
    let payload = "A".repeat(1_000_000);
    let attributions = explain_block(&payload, &waf);
    // Should not panic or OOM
    assert!(attributions.is_empty());
}

#[test]
fn explain_block_unicode_payload() {
    let waf = fake_waf("TestWAF");
    let payload = "<script>alert('你好')</script>";
    let attributions = explain_block(payload, &waf);
    // Should handle unicode without panic
    assert!(attributions.is_empty() || !attributions.is_empty());
}