1use super::*;
2use strum::IntoEnumIterator;
3
4mod additional_checks;
5mod flow;
6mod http_client;
7mod logs;
8mod response_checks;
9mod utils;
10
11pub use http_client::Authorization;
12use http_client::*;
13pub use logs::*;
14use serde_json::json;
15use std::{collections::HashSet, iter};
16
17type CheckRetVal = (Vec<(ResponseData, AttackResponse)>, AttackLog);
18
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20pub enum ActiveScanType {
21 Full,
22 Partial(Vec<ActiveChecks>),
23 NonInvasive,
24 OnlyTests,
25}
26
27type PayloadMap = HashMap<Vec<String>, Schema>;
28
29#[derive(Debug, Clone, Serialize, Default, PartialEq)]
30pub struct Payload {
31 pub payload: Value,
32 pub map: PayloadMap,
33}
34#[derive(Debug, Clone, Serialize, Default, PartialEq)]
35pub struct Path {
36 pub path_item: PathItem,
37 pub path: String,
38}
39
40#[derive(Debug, Clone, Serialize, Default, PartialEq)]
41struct OASMap {
42 pub path: Path,
43 pub payload: Payload,
44}
45
46#[derive(Default, Debug)]
47pub struct ResponseData {
48 location: String,
49 alert_text: String,
50 serverity: Level,
51}
52
53#[derive(Debug, Clone, Serialize, Default, PartialEq)]
54pub struct ActiveScan<T>
55where
56 T: Serialize,
57{
58 oas: T,
59 oas_value: Value,
60 verbosity: u8,
61 pub checks: Vec<ActiveChecks>,
62 payloads: Vec<OASMap>,
63 logs: AttackLog,
64}
65
66impl<T: OAS + Serialize + for<'de> Deserialize<'de>> ActiveScan<T> {
67 pub fn new(oas_value: Value) -> Result<Self, &'static str> {
68 let oas = match serde_json::from_value::<T>(oas_value.clone()) {
69 Ok(oas) => oas,
70 Err(e) => {
71 println!("{:?}", e);
72 return Err("Failed at deserializing swagger value to a swagger struct, please check the swagger definition");
73 }
74 };
75 let payloads = Self::payloads_generator(&oas, &oas_value);
76 Ok(ActiveScan {
77 oas,
78 oas_value,
79 checks: vec![],
80 verbosity: 0,
81 payloads,
82 logs: AttackLog::default(),
83 })
84 }
85 pub async fn run(&mut self, tp: ActiveScanType, auth: &Authorization) {
86 match tp {
87 ActiveScanType::Full => {
88 for check in ActiveChecks::iter() {
89 self.checks.push(self.run_check(check, auth).await);
90 }
91 }
92 ActiveScanType::NonInvasive => {
93 for check in ActiveChecks::iter() {
94 self.checks.push(self.run_check(check, auth).await);
95 }
96 }
97 ActiveScanType::OnlyTests => {
98 for check in ActiveChecks::iter() {
99 self.checks.push(self.run_check(check, auth).await);
100 }
101 }
102 ActiveScanType::Partial(checks) => {
103 for check in checks {
104 self.checks.push(self.run_check(check, auth).await);
105 }
106 }
107 };
108 }
109 pub fn print(&self, verbosity: u8) {
110 match verbosity {
111 0 => {
113 print_active_alerts_verbose(self.checks.clone());
114 }
115 1 => {
116 print_active_alerts(self.checks.clone());
117 }
118 _ => (),
119 }
120 }
121 pub fn print_to_file_string(&self) -> String {
122 String::new()
123 }
124
125 fn payloads_generator(oas: &T, oas_value: &Value) -> Vec<OASMap> {
126 let mut payloads = vec![];
127 for (path, path_item) in oas.get_paths() {
128 payloads.push(OASMap {
129 path: (Path {
130 path_item: path_item.clone(),
131 path,
132 }),
133 payload: Self::build_payload(oas_value, &path_item),
134 });
135 }
136 payloads
137 }
138
139 pub fn build_payload(oas_value: &Value, path_item: &PathItem) -> Payload {
140 let mut payload = json!({});
141 let mut map: PayloadMap = HashMap::new();
142 for (_, op) in path_item.get_ops() {
143 if let Some(req) = &op.request_body {
144 for (_, med_t) in req.inner(oas_value).content {
145 let mut path = Vec::<String>::new();
146 if let Some(s_ref) = &med_t.schema {
147 let mut visited_schemes = HashSet::new();
148 path.push(Self::get_name_s_ref(s_ref, oas_value, &None));
149 payload = Self::unwind_schema(
150 oas_value,
151 s_ref,
152 &mut map,
153 &mut path,
154 &mut visited_schemes,
155 );
156 }
157 }
158 }
159 }
160 Payload { payload, map }
161 }
162
163 pub fn unwind_schema(
164 oas_value: &Value,
165 reference: &SchemaRef,
166 map: &mut HashMap<Vec<String>, Schema>,
167 path: &mut Vec<String>,
168 visited_schemes: &mut HashSet<String>,
169 ) -> Value {
170 let mut payload = json!({});
171 let reference = reference.inner(oas_value);
172 if let Some(example) = reference.example {
173 payload = example;
174 } else if let Some(prop_map) = reference.properties {
175 for (name, schema) in prop_map {
176 path.push(name.clone());
177 payload[&name] = match schema {
178 SchemaRef::Ref(ref r) => {
179 if visited_schemes.contains(&r.param_ref) {
180 panic!("Circular reference detected");
181 }
182 visited_schemes.insert(r.param_ref.clone());
183 let ret =
184 Self::unwind_schema(oas_value, &schema, map, path, visited_schemes);
185 visited_schemes.remove(&r.param_ref);
186 ret
187 }
188 SchemaRef::Schema(schema) => {
189 map.insert(path.clone(), *schema.clone());
190 path.pop();
191 if let Some(example) = schema.example {
192 example
193 } else {
194 Self::gen_default_value(schema)
195 }
196 }
197 };
198 }
199 } else if let Some(item_ref) = reference.items {
200 payload = json!([match *item_ref {
202 SchemaRef::Ref(ref r) => {
203 if visited_schemes.contains(&r.param_ref) {
204 panic!("Circular reference detected");
205 }
206 visited_schemes.insert(r.param_ref.clone());
207 let ret = Self::unwind_schema(oas_value, &item_ref, map, path, visited_schemes);
208 visited_schemes.remove(&r.param_ref);
209 ret
210 }
211 SchemaRef::Schema(schema) => {
212 map.insert(path.clone(), *schema.clone());
213 path.pop();
214 if let Some(example) = schema.example {
215 example
216 } else {
217 Self::gen_default_value(schema)
218 }
219 }
220 }]);
221 }
222 payload
223 }
224
225 pub fn gen_default_value(schema: Box<Schema>) -> Value {
226 let ret: Value = if let Some(data_type) = schema.schema_type {
227 match data_type.as_str() {
228 "string" => {
229 if let Some(num) = schema.min_length {
230 json!(iter::repeat(['B', 'L', 'S', 'T'])
231 .flatten()
232 .take(num.try_into().unwrap())
233 .collect::<String>())
234 } else {
235 json!("BLST")
236 }
237 }
238 "integer" => {
239 if let Some(num) = schema.minimum {
240 json!(num)
241 } else {
242 json!(5usize)
243 }
244 }
245 "boolean" => {
246 json!(false)
247 }
248 _ => {
249 json!({})
250 }
251 }
252 } else {
253 json!({})
254 };
255 ret
256 }
257
258 pub fn get_name_s_ref(s_ref: &SchemaRef, value: &Value, name: &Option<String>) -> String {
259 let schema = s_ref.inner(value);
260 if let Some(ref t) = schema.title {
261 t.to_string()
262 } else if let SchemaRef::Ref(r) = s_ref {
263 r.param_ref.split('/').last().unwrap().to_string()
264 } else if let Some(n) = name {
265 n.to_string()
266 } else {
267 String::new()
268 }
269 }
270}
271
272impl ActiveChecks {
273 pub fn parse_check_list(list: Vec<String>, exclude: bool) -> Vec<ActiveChecks> {
274 let mut checks = Vec::new();
275 for check in list.iter() {
276 let check = Self::from_string(check);
277 if let Some(c) = check {
278 checks.push(c);
279 }
280 }
281 if exclude {
282 let mut ex_checks: Vec<_> = Self::iter().collect();
283 ex_checks.retain(|x| !checks.contains(x));
284 return ex_checks;
285 }
286 checks
287 }
288}