security_rust/protocol/
cors.rs1use regex::Regex;
4use std::sync::LazyLock;
5
6use crate::{regex_detect, AttackCategory, DetectionResult, Detector, Severity};
7
8static PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
9 vec![
10 Regex::new(r"(?i)Origin:\s*null").unwrap(),
11 Regex::new(r"(?i)Access-Control-Allow-Origin:\s*\*").unwrap(),
12 Regex::new(r"(?i)Access-Control-Allow-Credentials:\s*true").unwrap(),
13 ]
14});
15
16pub struct CorsDetector;
17
18impl Detector for CorsDetector {
19 fn name(&self) -> &'static str {
20 "cors"
21 }
22
23 fn detect(&self, input: &str) -> Option<DetectionResult> {
24 regex_detect(&PATTERNS, self.name(), AttackCategory::Protocol, Severity::Medium, "CORS bypass attempt detected", input)
25 }
26}
27
28#[cfg(test)]
29mod tests {
30 use super::*;
31
32 fn assert_detected(input: &str) {
33 crate::test_helpers::assert_detected(
34 &CorsDetector,
35 input,
36 AttackCategory::Protocol,
37 Severity::Medium,
38 );
39 }
40
41 fn assert_clean(input: &str) {
42 crate::test_helpers::assert_clean(&CorsDetector, input);
43 }
44
45 #[test]
46 fn name_is_cors() {
47 assert_eq!(CorsDetector.name(), "cors");
48 }
49
50 #[test]
51 fn detects_null_origin() {
52 assert_detected("Origin: null");
53 assert_detected("Origin:null");
54 }
55
56 #[test]
57 fn detects_wildcard_allow_origin() {
58 assert_detected("Access-Control-Allow-Origin: *");
59 assert_detected("Access-Control-Allow-Origin:*");
60 }
61
62 #[test]
63 fn detects_allow_credentials() {
64 assert_detected("Access-Control-Allow-Credentials: true");
65 }
66
67 #[test]
68 fn detects_mixed_case() {
69 assert_detected("origin: NULL");
70 assert_detected("access-control-allow-origin: *");
71 }
72
73 #[test]
74 fn rejects_benign_headers() {
75 assert_clean("Origin: http://example.com");
76 assert_clean("Access-Control-Allow-Origin: https://example.com");
77 assert_clean("Access-Control-Allow-Credentials: false");
78 assert_clean("Origin: *");
79 }
80
81 #[test]
82 fn rejects_space_before_colon() {
83 assert_clean("Access-Control-Allow-Origin : *");
84 }
85
86 #[test]
87 fn rejects_empty_and_whitespace() {
88 assert_clean("");
89 assert_clean(" ");
90 }
91
92 #[test]
93 fn rejects_unicode_text() {
94 assert_clean("跨域配置说明,无攻击");
95 }
96}