1use crate::{enums, structs};
5use lazy_static::lazy_static;
6use log::{self, error, trace};
7use regex::{Captures, Regex};
8use serde_json::Value;
9use std::collections::HashMap;
10use std::process;
11
12lazy_static! {
15 static ref STRINGIFIED_BOOLS: Regex =
16 Regex::new(r"[:=]\s*([fF]alse|[tT]rue)\s*([,}]+|$)").unwrap();
17}
18pub fn fix_stringified_bools(fstr: &str) -> String {
19 let after = STRINGIFIED_BOOLS.replace_all(fstr, |caps: &Captures| caps[0].to_lowercase());
20 after.to_string()
21}
22
23pub fn format_value(v: &Value) -> String {
24 let formatted_value = if v.is_string() {
25 strip_ws_nl(String::from(v.as_str().unwrap()))
27 } else {
28 strip_ws_nl(v.to_string())
30 };
31 trace!("formatted_value is '{}'", formatted_value);
32 formatted_value
33}
34
35pub fn strip_ws_nl(v: String) -> String {
36 trace!("Removing spaces and newline characters from '{}'", &v);
37 v.replace("\n", "").replace(" ", "")
38}
39
40pub fn convert_list_var_to_vec(rule_val: &str) -> Vec<String> {
41 let mut value_vec: Vec<String> = vec![];
42 match serde_json::from_str(rule_val) {
43 Ok(v) => {
44 trace!("List {} is a json list", rule_val);
45 let val: Value = v;
46 match val.as_array() {
47 Some(vv) => {
48 for vvv in vv {
49 let val_string = vvv.to_string();
50 let list_val = val_string.trim_start_matches('"').trim_end_matches('"');
51 value_vec.push(String::from(list_val))
52 }
53 }
54 None => value_vec.push(val.to_string()),
55 }
56 }
57 Err(_) => {
58 trace!("List {} is not a json list", rule_val);
59 let value_string: String = rule_val
60 .trim_start_matches('[')
61 .trim_end_matches(']')
62 .replace(" ", "");
63
64 for vs in value_string.split(',') {
65 value_vec.push(String::from(vs));
66 }
67 }
68 };
69
70 trace!("Rule value_vec is {:?}", &value_vec);
71 value_vec
72}
73
74fn match_props<'a>(props: &'a Value, n: &'a dyn serde_json::value::Index) -> Result<&'a Value, ()> {
75 trace!("props are {:#?}", props);
76
77 match props.get(n) {
78 Some(v) => Ok(v),
79 None => Err(()),
80 }
81}
82
83pub fn get_resource_prop_value(props: &Value, field: &[&str]) -> Result<Value, String> {
84 trace!("Getting {:?} from {}", &field, &props);
85 let mut field_list = field.to_owned();
86 trace!("field_list is {:?}", field_list);
87 if field_list.is_empty() {
88 return Ok(props.clone());
89 }
90 let next_field = field_list.remove(0);
91 match next_field {
92 "" => return Ok(props.clone()),
93 "." => get_resource_prop_value(&props, &field_list),
94 _ => match next_field.parse::<usize>() {
95 Ok(n) => {
96 trace!(
97 "next_field is {:?} and field_list is now {:?}",
98 &n,
99 &field_list
100 );
101
102 match match_props(props, &n) {
103 Ok(v) => {
104 if !field_list.is_empty() {
105 get_resource_prop_value(&v, &field_list)
106 } else {
107 Ok(v.clone())
108 }
109 }
110 Err(_) => destringify_json(props, &n, &mut field_list),
111 }
112 }
113 Err(_) => {
114 trace!(
115 "next_field is {:?} and field_list is now {:?}",
116 &next_field,
117 &field_list
118 );
119 match match_props(props, &next_field) {
120 Ok(v) => {
121 if !field_list.is_empty() {
122 get_resource_prop_value(&v, &field_list)
123 } else {
124 Ok(v.clone())
125 }
126 }
127 Err(_) => destringify_json(props, &next_field, &mut field_list),
128 }
129 }
130 },
131 }
132}
133
134fn destringify_json<'a>(
135 props: &'a Value,
136 field: &'a dyn serde_json::value::Index,
137 field_list: &'a mut Vec<&str>,
138) -> Result<Value, String> {
139 match props.as_str() {
140 Some(p) => match serde_json::from_str::<Value>(p) {
141 Ok(s) => {
142 trace!("sub structure is {:#?}", s);
143 match match_props(&s, field) {
145 Ok(v) => {
146 trace!("next_props is {:#?}", v);
147 get_resource_prop_value(&v, &field_list)
148 }
149 Err(_) => return Err(format!("Invalid address")),
150 }
151 }
152 Err(e) => return Err(e.to_string()),
153 },
154 None => return Err(format!("Could not convert properties to string")),
155 }
156}
157
158pub fn filter_for_env_vars(vars: &HashMap<String, String>) -> HashMap<String, String> {
159 let mut filtered_map: HashMap<String, String> = HashMap::new();
160 for (k, v) in vars.iter() {
161 if !k.starts_with("ENV") {
162 filtered_map.insert(k.to_string(), v.to_string());
163 } else {
164 filtered_map.insert(k.to_string(), format!("********"));
165 }
166 }
167 filtered_map
168}
169
170pub fn deref_rule_value<'a>(
171 rule: &'a structs::Rule,
172 vars: &'a HashMap<String, String>,
173) -> Result<&'a str, String> {
174 let filtered_env_vars = filter_for_env_vars(vars);
175 trace!(
176 "Entered dereference_rule_value() with '{:#?}' and Variables '{:#?}'",
177 rule,
178 filtered_env_vars
179 );
180 match rule.rule_vtype {
181 enums::RValueType::Variable => {
182 let target_value: &str = rule.value.split('%').collect::<Vec<&str>>()[1];
183 let first_char = target_value.chars().collect::<Vec<char>>()[0];
184 let final_target = match first_char {
185 '{' => format!(
187 "ENV_{}",
188 target_value.trim_start_matches('{').trim_end_matches('}')
189 ),
190 _ => target_value.to_string(),
191 };
192 trace!(
193 "Dereferencing variable {:?} in '{:#?}'",
194 final_target,
195 filtered_env_vars
196 );
197 match &vars.get(&final_target) {
198 Some(v) => Ok(v),
199 None => {
200 error!(
201 "Undefined Variable: [{}] does not exist in {:#?}",
202 final_target, &filtered_env_vars
203 );
204 Err(format!(
205 "[{}] does not exist in {:#?}",
206 rule.value, &filtered_env_vars
207 ))
208 }
209 }
210 }
211 _ => Ok(&rule.value),
212 }
213}
214
215pub fn expand_wildcard_props(
216 props: &Value,
217 address: String,
218 accumulator: String,
219) -> Option<Vec<String>> {
220 trace!(
221 "Entering expand_wildcard_props() with props: {:#?} , address: {:#?} , accumulator: {:#?}",
222 &props,
223 &address,
224 &accumulator
225 );
226 let mut segments = address.split('*').collect::<Vec<&str>>();
227 trace!("Segments are {:#?}", &segments);
228 let segment = segments.remove(0);
229 trace!("Processing segment {:#?}", &segment);
230 if segments.len() > 0 {
231 let mut expanded_props: Vec<String> = vec![];
232 let s = segment.trim_end_matches('.').trim_start_matches('.');
233 let steps = s.split('.').collect::<Vec<&str>>();
234 match get_resource_prop_value(props, &steps) {
235 Ok(v) => match v.as_array() {
236 Some(result_array) => {
237 trace!("Value is an array");
238 for (counter, r) in result_array.iter().enumerate() {
239 trace!("Counter is {:#?}", counter);
240 let next_segment = segments.join("*");
241 trace!("next_segment is '{:#?}'", &next_segment);
242 let temp_address = format!("{}{}{}", accumulator, segment, counter);
243 trace!("temp_address is {:#?}", &temp_address);
244 match expand_wildcard_props(&r, next_segment, temp_address) {
245 Some(result) => expanded_props.append(&mut result.clone()),
246 None => return None,
247 }
248 }
249 }
250 None => match v.as_object() {
251 Some(result_object) => {
252 trace!("Value is an object");
253 for (k, v) in result_object.iter() {
254 trace!("Key is '{}'", k);
255 let next_segment = segments.join("*");
256 trace!("next_segment is {:#?}", next_segment);
257 let temp_address = format!("{}{}{}", accumulator, segment, k);
258 trace!("temp_address is {:#?}", &temp_address);
259 match expand_wildcard_props(&v, next_segment, temp_address) {
260 Some(result) => expanded_props.append(&mut result.clone()),
261 None => return None,
262 }
263 }
264 }
265 None => expanded_props.push(format!("{}{}", accumulator, segment)),
266 },
267 },
268 Err(_) => return None,
269 }
270 Some(expanded_props)
271 } else {
272 trace!("Final segment");
273 let accumulated_address = format!("{}{}", accumulator, segment);
274 trace!("Accumulated address: {}", accumulated_address);
275 Some(vec![accumulated_address])
276 }
277}
278
279pub fn parse_value_as_float(val: &Value) -> f32 {
287 trace!("Formatting {} as float", val);
288 match format_value(val).parse::<f32>() {
289 Ok(s) => s,
290 Err(_) => {
291 let msg_string = format!("Value cannot be parsed as a float: {}", val);
292 error!("{}", &msg_string);
293 process::exit(1)
294 }
295 }
296}
297pub fn parse_str_as_float(val: &str) -> f32 {
298 trace!("Formatting {} as float", val);
299 match strip_ws_nl(val.to_string()).parse::<f32>() {
300 Ok(s) => s,
301 Err(_) => {
302 let msg_string = format!("String cannot be parsed as a float: {}", val);
303 error!("{}", &msg_string);
304 process::exit(1)
305 }
306 }
307}
308mod tests {
309 #[cfg(test)]
310 use crate::util::expand_wildcard_props;
311 #[cfg(test)]
312 use std::collections::HashMap;
313
314 #[test]
315 fn test_wildcard_expansion() {
316 let iam_template: &'static str = r#"
317Resources:
318 LambdaRoleHelper:
319 Type: 'AWS::IAM::Role'
320 Properties:
321 AssumeRolePolicyDocument:
322 Version: 2012-10-17
323 Statement:
324 - Effect: Allow
325 Principal:
326 Service:
327 - ec2.amazonaws.com
328 - lambda.amazonaws.com
329 Action:
330 - 'sts:AssumeRole'
331 - Effect: Allow
332 Principal:
333 Service:
334 - lambda.amazonaws.com
335 - ec2.amazonaws.com
336 Action:
337 - 'sts:AssumeRole'
338 - Effect: Allow
339 Principal:
340 Service:
341 - lambda.amazonaws.com
342 - ec2.amazonaws.com
343 Action:
344 - 'sts:AssumeRole'
345"#;
346 let cfn_template: HashMap<String, serde_json::Value> =
347 serde_yaml::from_str(&iam_template).unwrap();
348 let mut wildcard = String::from("AssumeRolePolicyDocument.Statement.*.Effect");
349 let root = &cfn_template["Resources"]["LambdaRoleHelper"]["Properties"];
350 let mut expanded_wildcards =
351 expand_wildcard_props(&root, wildcard, String::from("")).unwrap();
352 assert_eq!(
353 expanded_wildcards,
354 vec![
355 String::from("AssumeRolePolicyDocument.Statement.0.Effect"),
356 String::from("AssumeRolePolicyDocument.Statement.1.Effect"),
357 String::from("AssumeRolePolicyDocument.Statement.2.Effect"),
358 ]
359 );
360 wildcard = String::from("AssumeRolePolicyDocument.Statement.*.Action.*");
361 expanded_wildcards = expand_wildcard_props(&root, wildcard, String::from("")).unwrap();
362 assert_eq!(
363 expanded_wildcards,
364 vec![
365 String::from("AssumeRolePolicyDocument.Statement.0.Action.0"),
366 String::from("AssumeRolePolicyDocument.Statement.1.Action.0"),
367 String::from("AssumeRolePolicyDocument.Statement.2.Action.0"),
368 ]
369 );
370 }
371}