Skip to main content

security_rust/protocol/
xxe.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3use regex::Regex;
4use std::sync::LazyLock;
5
6use crate::{AttackCategory, DetectionResult, Detector, Severity};
7
8static PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
9    vec![
10        Regex::new(r"(?i)<!ENTITY\s+").unwrap(),
11        Regex::new(r#"(?i)SYSTEM\s+["']"#).unwrap(),
12        Regex::new(r#"(?i)PUBLIC\s+["']"#).unwrap(),
13        Regex::new(r"(?i)<!ENTITY\s+%").unwrap(),
14        Regex::new(r"(?i)<!DOCTYPE\s+").unwrap(),
15    ]
16});
17
18pub struct XxeDetector;
19
20impl Detector for XxeDetector {
21    fn name(&self) -> &'static str {
22        "xxe"
23    }
24
25    fn detect(&self, input: &str) -> Option<DetectionResult> {
26        for re in PATTERNS.iter() {
27            if let Some(m) = re.find(input) {
28                return Some(DetectionResult {
29                    attack_type: "xxe".into(),
30                    category: AttackCategory::Protocol,
31                    severity: Severity::Critical,
32                    matched_pattern: m.as_str().to_string(),
33                    offset: m.start(),
34                    message: "XXE XML External Entity attack detected".into(),
35                });
36            }
37        }
38        None
39    }
40}