sentinel_modsec/transformations/
mod.rs

1//! Transformation functions for ModSecurity.
2
3mod decode;
4mod encode;
5mod normalize;
6mod pipeline;
7
8pub use decode::*;
9pub use encode::*;
10pub use normalize::*;
11pub use pipeline::TransformationPipeline;
12
13use crate::error::{Error, Result};
14use std::borrow::Cow;
15use std::sync::Arc;
16
17/// Trait for transformations.
18pub trait Transformation: Send + Sync {
19    /// Apply the transformation.
20    fn transform<'a>(&self, input: &'a str) -> Cow<'a, str>;
21
22    /// Get the transformation name.
23    fn name(&self) -> &'static str;
24}
25
26/// Create a transformation from a name.
27pub fn create_transformation(name: &str) -> Result<Arc<dyn Transformation>> {
28    match name.to_lowercase().as_str() {
29        // Decoding
30        "urldecode" => Ok(Arc::new(UrlDecode)),
31        "urldecodeuni" => Ok(Arc::new(UrlDecodeUni)),
32        "base64decode" => Ok(Arc::new(Base64Decode)),
33        "base64decodeext" => Ok(Arc::new(Base64DecodeExt)),
34        "hexdecode" => Ok(Arc::new(HexDecode)),
35        "htmlentitydecode" => Ok(Arc::new(HtmlEntityDecode)),
36        "jsdecode" => Ok(Arc::new(JsDecode)),
37        "cssdecode" => Ok(Arc::new(CssDecode)),
38
39        // Encoding
40        "base64encode" => Ok(Arc::new(Base64Encode)),
41        "hexencode" => Ok(Arc::new(HexEncode)),
42        "urlencode" => Ok(Arc::new(UrlEncode)),
43
44        // Normalization
45        "lowercase" => Ok(Arc::new(Lowercase)),
46        "uppercase" => Ok(Arc::new(Uppercase)),
47        "compresswhitespace" => Ok(Arc::new(CompressWhitespace)),
48        "removewhitespace" => Ok(Arc::new(RemoveWhitespace)),
49        "removenulls" => Ok(Arc::new(RemoveNulls)),
50        "replacenulls" => Ok(Arc::new(ReplaceNulls)),
51        "trim" => Ok(Arc::new(Trim)),
52        "trimleft" => Ok(Arc::new(TrimLeft)),
53        "trimright" => Ok(Arc::new(TrimRight)),
54        "normalizepath" => Ok(Arc::new(NormalizePath)),
55        "normalizepathwin" => Ok(Arc::new(NormalizePathWin)),
56        "removecomments" => Ok(Arc::new(RemoveComments)),
57        "replacecomments" => Ok(Arc::new(ReplaceComments)),
58        "cmdline" => Ok(Arc::new(CmdLine)),
59        "removecommentschar" => Ok(Arc::new(RemoveCommentsChar)),
60
61        // Hashing
62        "md5" => Ok(Arc::new(Md5)),
63        "sha1" => Ok(Arc::new(Sha1)),
64
65        // Special
66        "length" => Ok(Arc::new(Length)),
67        "none" => Ok(Arc::new(None_)),
68
69        // Additional CRS transformations
70        "sqlhexdecode" => Ok(Arc::new(SqlHexDecode)),
71        "utf8tounicode" => Ok(Arc::new(Utf8ToUnicode)),
72        "escapeseqdecode" => Ok(Arc::new(EscapeSeqDecode)),
73        "parityeven7bit" | "parityodd7bit" | "parityzero7bit" => {
74            Ok(Arc::new(None_)) // Placeholder - pass through
75        }
76        "sha256" => Ok(Arc::new(Sha256)),
77
78        _ => Err(Error::UnknownTransformation { name: name.to_string() }),
79    }
80}
81
82/// None transformation (clears the transformation chain).
83pub struct None_;
84
85impl Transformation for None_ {
86    fn transform<'a>(&self, input: &'a str) -> Cow<'a, str> {
87        Cow::Borrowed(input)
88    }
89
90    fn name(&self) -> &'static str {
91        "none"
92    }
93}
94
95/// Length transformation (returns the length of the input).
96pub struct Length;
97
98impl Transformation for Length {
99    fn transform<'a>(&self, input: &'a str) -> Cow<'a, str> {
100        Cow::Owned(input.len().to_string())
101    }
102
103    fn name(&self) -> &'static str {
104        "length"
105    }
106}