security_rust/injection/
ssi_injection.rs1use crate::{AttackCategory, DetectionResult, Detector, Severity, regex_detect};
4use regex::Regex;
5use std::sync::LazyLock;
6
7static PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
8 vec![
9 Regex::new(r"<!--#exec cmd=").unwrap(),
10 Regex::new(r"<!--#include file=").unwrap(),
11 Regex::new(r"<!--#echo var=").unwrap(),
12 Regex::new(r"<!--#fsize").unwrap(),
13 Regex::new(r"<!--#flastmod").unwrap(),
14 Regex::new(r"<!--#config").unwrap(),
15 Regex::new(r"<!--#printenv").unwrap(),
16 ]
17});
18
19pub struct SsiInjectionDetector;
20
21impl Detector for SsiInjectionDetector {
22 fn name(&self) -> &'static str {
23 "ssi_injection"
24 }
25
26 fn detect(&self, input: &str) -> Option<DetectionResult> {
27 regex_detect(
28 &PATTERNS,
29 self.name(),
30 AttackCategory::Injection,
31 Severity::High,
32 "SSI Server-Side Include injection detected",
33 input,
34 )
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 fn det() -> SsiInjectionDetector {
43 SsiInjectionDetector
44 }
45
46 fn assert_hit(input: &str) {
47 crate::test_helpers::assert_detected(
48 &det(),
49 input,
50 AttackCategory::Injection,
51 Severity::High,
52 );
53 }
54
55 #[test]
56 fn name_is_ssi_injection() {
57 assert_eq!(det().name(), "ssi_injection");
58 }
59
60 #[test]
61 fn detects_common_payloads() {
62 for input in [
63 r#"<!--#exec cmd="cat /etc/passwd"-->"#,
64 r#"<!--#include file="/etc/passwd"-->"#,
65 r#"<!--#echo var="DATE_LOCAL"-->"#,
66 r#"<!--#fsize file="index.html"-->"#,
67 r#"<!--#flastmod file="index.html"-->"#,
68 r#"<!--#config timefmt="%B"-->"#,
69 r#"<!--#printenv-->"#,
70 ] {
71 assert_hit(input);
72 }
73 }
74
75 #[test]
76 fn benign_inputs_not_detected() {
77 for input in [
78 "Hello, this is a normal text input. Nothing suspicious here.",
79 "<!-- this is a plain comment -->",
80 "The page was generated at 3:00 PM",
81 "Include the file below the table",
82 ] {
83 assert!(det().detect(input).is_none(), "false positive: {input}");
84 }
85 }
86
87 #[test]
88 fn edge_cases() {
89 assert!(det().detect("").is_none());
90 assert!(det().detect(" \t\n ").is_none());
91 assert!(det().detect("你好世界 こんにちは").is_none());
92 assert!(det().detect("<!--#exec").is_none());
94 assert!(det().detect(r#"<!--#EXEC cmd="ls"-->"#).is_none());
95 assert!(det().detect("<!-- #exec cmd=\"ls\" -->").is_none());
96 }
97}