sentinel_modsec/variables/
resolver.rs1use super::{RequestData, ResponseData, TxCollection};
4use crate::parser::{Selection, VariableName, VariableSpec};
5use regex::Regex;
6
7pub struct VariableResolver<'a> {
9 request: &'a RequestData,
10 response: &'a ResponseData,
11 tx: &'a TxCollection,
12 matched_var: Option<&'a str>,
13 matched_vars: &'a [(String, String)],
14 captures: &'a [String],
15}
16
17impl<'a> VariableResolver<'a> {
18 pub fn new(
20 request: &'a RequestData,
21 response: &'a ResponseData,
22 tx: &'a TxCollection,
23 matched_var: Option<&'a str>,
24 matched_vars: &'a [(String, String)],
25 captures: &'a [String],
26 ) -> Self {
27 Self {
28 request,
29 response,
30 tx,
31 matched_var,
32 matched_vars,
33 captures,
34 }
35 }
36
37 pub fn resolve(&self, spec: &VariableSpec) -> Vec<(String, String)> {
39 let values = self.resolve_variable(spec.name, &spec.selection);
40
41 if spec.exclusions.is_empty() {
43 values
44 } else {
45 values
46 .into_iter()
47 .filter(|(k, _)| !spec.exclusions.iter().any(|e| k.contains(e)))
48 .collect()
49 }
50 }
51
52 fn resolve_variable(
54 &self,
55 name: VariableName,
56 selection: &Option<Selection>,
57 ) -> Vec<(String, String)> {
58 match name {
59 VariableName::RequestUri => {
61 vec![("REQUEST_URI".to_string(), self.request.uri.clone())]
62 }
63 VariableName::RequestUriRaw => {
64 vec![("REQUEST_URI_RAW".to_string(), self.request.uri_raw.clone())]
65 }
66 VariableName::RequestMethod => {
67 vec![("REQUEST_METHOD".to_string(), self.request.method.clone())]
68 }
69 VariableName::RequestProtocol => {
70 vec![(
71 "REQUEST_PROTOCOL".to_string(),
72 self.request.protocol.clone(),
73 )]
74 }
75 VariableName::QueryString => {
76 vec![(
77 "QUERY_STRING".to_string(),
78 self.request.query_string.clone(),
79 )]
80 }
81 VariableName::RequestFilename => {
82 vec![("REQUEST_FILENAME".to_string(), self.request.path.clone())]
83 }
84 VariableName::RequestBody => {
85 vec![("REQUEST_BODY".to_string(), self.request.body_str())]
86 }
87 VariableName::RequestBodyLength => {
88 vec![(
89 "REQUEST_BODY_LENGTH".to_string(),
90 self.request.body_length().to_string(),
91 )]
92 }
93
94 VariableName::Args => self.resolve_collection_from_all_args(selection),
96 VariableName::ArgsGet => {
97 self.resolve_collection(&self.request.args_get, "ARGS_GET", selection)
98 }
99 VariableName::ArgsPost => {
100 self.resolve_collection(&self.request.args_post, "ARGS_POST", selection)
101 }
102 VariableName::RequestHeaders => {
103 self.resolve_collection(&self.request.headers, "REQUEST_HEADERS", selection)
104 }
105 VariableName::RequestCookies => {
106 self.resolve_collection(&self.request.cookies, "REQUEST_COOKIES", selection)
107 }
108
109 VariableName::ResponseStatus => {
111 vec![(
112 "RESPONSE_STATUS".to_string(),
113 self.response.status.to_string(),
114 )]
115 }
116 VariableName::ResponseBody => {
117 vec![("RESPONSE_BODY".to_string(), self.response.body_str())]
118 }
119 VariableName::ResponseContentType => {
120 vec![(
121 "RESPONSE_CONTENT_TYPE".to_string(),
122 self.response.content_type.clone(),
123 )]
124 }
125 VariableName::ResponseHeaders => {
126 self.resolve_collection(&self.response.headers, "RESPONSE_HEADERS", selection)
127 }
128
129 VariableName::Tx => self.resolve_tx_collection(selection),
131
132 VariableName::RemoteAddr => {
134 vec![("REMOTE_ADDR".to_string(), self.request.client_ip.clone())]
135 }
136 VariableName::RemotePort => {
137 vec![(
138 "REMOTE_PORT".to_string(),
139 self.request.client_port.to_string(),
140 )]
141 }
142 VariableName::ServerName => {
143 vec![("SERVER_NAME".to_string(), self.request.server_name.clone())]
144 }
145 VariableName::ServerPort => {
146 vec![(
147 "SERVER_PORT".to_string(),
148 self.request.server_port.to_string(),
149 )]
150 }
151
152 VariableName::MatchedVar => {
154 if let Some(v) = self.matched_var {
155 vec![("MATCHED_VAR".to_string(), v.to_string())]
156 } else {
157 vec![]
158 }
159 }
160 VariableName::MatchedVars => self
161 .matched_vars
162 .iter()
163 .map(|(k, v)| (format!("MATCHED_VARS:{}", k), v.clone()))
164 .collect(),
165
166 _ => vec![],
168 }
169 }
170
171 fn resolve_collection(
173 &self,
174 collection: &super::collection::HashMapCollection,
175 prefix: &str,
176 selection: &Option<Selection>,
177 ) -> Vec<(String, String)> {
178 use super::collection::Collection;
179
180 match selection {
181 Some(Selection::Key(key)) => {
182 if let Some(values) = collection.get(key) {
183 values
184 .into_iter()
185 .map(|v| (format!("{}:{}", prefix, key), v.to_string()))
186 .collect()
187 } else {
188 vec![]
189 }
190 }
191 Some(Selection::Regex(pattern)) => {
192 if let Ok(re) = Regex::new(pattern) {
193 collection
194 .get_regex(&re)
195 .into_iter()
196 .map(|(k, v)| (format!("{}:{}", prefix, k), v.to_string()))
197 .collect()
198 } else {
199 vec![]
200 }
201 }
202 None => collection
203 .all()
204 .into_iter()
205 .map(|(k, v)| (format!("{}:{}", prefix, k), v.to_string()))
206 .collect(),
207 }
208 }
209
210 fn resolve_collection_from_all_args(&self, selection: &Option<Selection>) -> Vec<(String, String)> {
212 let mut result = self.resolve_collection(&self.request.args_get, "ARGS", selection);
213 result.extend(self.resolve_collection(&self.request.args_post, "ARGS", selection));
214 result
215 }
216
217 fn resolve_tx_collection(&self, selection: &Option<Selection>) -> Vec<(String, String)> {
219 use super::collection::Collection;
220
221 match selection {
222 Some(Selection::Key(key)) => {
223 if let Some(values) = self.tx.get(key) {
224 values
225 .into_iter()
226 .map(|v| (format!("TX:{}", key), v.to_string()))
227 .collect()
228 } else {
229 vec![]
230 }
231 }
232 Some(Selection::Regex(pattern)) => {
233 if let Ok(re) = Regex::new(pattern) {
234 self.tx
235 .get_regex(&re)
236 .into_iter()
237 .map(|(k, v)| (format!("TX:{}", k), v.to_string()))
238 .collect()
239 } else {
240 vec![]
241 }
242 }
243 None => self
244 .tx
245 .all()
246 .into_iter()
247 .map(|(k, v)| (format!("TX:{}", k), v.to_string()))
248 .collect(),
249 }
250 }
251}