swagger/scan/passive/
additional_checks.rs

1use super::*;
2
3impl<T: OAS + Serialize> PassiveSwaggerScan<T> {
4    pub fn check_valid_responses(&self) -> Vec<Alert> {
5        let mut alerts: Vec<Alert> = vec![];
6        for (path, item) in &self.swagger.get_paths() {
7            for (m, op) in item.get_ops() {
8                let statuses = op.responses().keys().cloned().collect::<Vec<String>>();
9                for status in statuses {
10                    if let Ok(res_code) = status.parse::<u16>() {
11                        if !(100..600).contains(&res_code) {
12                            alerts.push(Alert::new(
13                                Level::Low,
14                                "Responses have an invalid or unrecognized status code",
15                                format!("swagger path:{} operation:{} status:{}", path, m, status),
16                            ));
17                        }
18                    } else if status != "default" {
19                        alerts.push(Alert::new(
20                            Level::Low,
21                            "Responses have an invalid or unrecognized status code",
22                            format!("swagger path:{} operation:{} status:{}", path, m, status),
23                        ));
24                    }
25                }
26            }
27        }
28        alerts
29    }
30    fn get_check(security: &Option<Vec<Security>>, path: &str) -> Vec<Alert> {
31        let mut alerts = vec![];
32        match security {
33            Some(x) => {
34                for i in x {
35                    let y = i.values().flatten().cloned().collect::<Vec<String>>();
36                    for item in y {
37                        if !item.starts_with("read") {
38                            alerts.push(Alert::new(
39                                Level::Medium,
40                                "Request GET has to be only read permission",
41                                format!("swagger path:{} method:{}", path, Method::GET),
42                            ));
43                        }
44                    }
45                }
46            }
47            None => (),
48        };
49        alerts
50    }
51    fn put_check(security: &Option<Vec<Security>>, path: &str) -> Vec<Alert> {
52        let mut alerts = vec![];
53        match security {
54            Some(x) => {
55                for i in x {
56                    let y = i.values().flatten().cloned().collect::<Vec<String>>();
57                    for item in y {
58                        if !item.starts_with("write") {
59                            alerts.push(Alert::new(
60                                Level::Medium,
61                                "Request PUT has to be only write permission",
62                                format!("swagger path:{} method:{}", path, Method::PUT),
63                            ));
64                        }
65                    }
66                }
67            }
68            None => (),
69        }
70        alerts
71    }
72    fn post_check(security: &Option<Vec<Security>>, path: &str) -> Vec<Alert> {
73        let mut alerts = vec![];
74        match security {
75            Some(x) => {
76                for i in x {
77                    let y = i.values().flatten().cloned().collect::<Vec<String>>();
78                    for item in y {
79                        if !item.starts_with("write:") && !item.starts_with("read:") {
80                            alerts.push(Alert::new(
81                                Level::Low,
82                                "Request POST has to be with read and write permissions",
83                                format!("swagger path:{} method:{}", path, Method::POST),
84                            ));
85                        }
86                    }
87                }
88            }
89            None => (),
90        }
91        alerts
92    }
93    pub fn check_method_permissions(&self) -> Vec<Alert> {
94        let mut alerts: Vec<Alert> = vec![];
95        for (path, item) in &self.swagger.get_paths() {
96            for (m, op) in item.get_ops() {
97                match m {
98                    Method::GET => alerts.extend(Self::get_check(&op.security, path)),
99                    Method::PUT => alerts.extend(Self::put_check(&op.security, path)),
100                    Method::POST => alerts.extend(Self::post_check(&op.security, path)),
101                    _ => (),
102                };
103            }
104        }
105        alerts
106    }
107    pub fn check_contains_operation(&self) -> Vec<Alert> {
108        let mut alerts: Vec<Alert> = vec![];
109        for (path, item) in &self.swagger.get_paths() {
110            if item.get_ops().is_empty() {
111                alerts.push(Alert::new(
112                    Level::Low,
113                    "Path has no operations",
114                    format!("swagger path:{} ", path),
115                ));
116            }
117        }
118        alerts
119    }
120
121    pub fn check_valid_encoding(&self) -> Vec<Alert> {
122        let mut alerts: Vec<Alert> = vec![];
123        for (path, item) in &self.swagger.get_paths() {
124            for (_m, op) in item.get_ops() {
125                if let Some(req_body) = &op.request_body {
126                    req_body
127                        .inner(&self.swagger_value)
128                        .content
129                        .keys()
130                        .for_each(|c_t| {
131                            if !LIST_CONTENT_TYPE.contains(&c_t.as_str()) {
132                                alerts.push(Alert::new(
133                                    Level::Low,
134                                    "Request body has an invalid content type",
135                                    format!("swagger path:{} content type:{}", path, c_t),
136                                ))
137                            }
138                        });
139                }
140            }
141        }
142        alerts
143    }
144
145    pub fn check_description(&self) -> Vec<Alert> {
146        let mut alerts: Vec<Alert> = vec![];
147        for (path, item) in &self.swagger.get_paths() {
148            for (m, op) in item.get_ops() {
149                if op.description.is_none() {
150                    alerts.push(Alert::new(
151                        Level::Low,
152                        "Operation has no description",
153                        format!("swagger path:{} operation:{}", path, m),
154                    ));
155                } else if op.description.as_ref().unwrap().is_empty() {
156                    alerts.push(Alert::new(
157                        Level::Low,
158                        "Operation has an empty description",
159                        format!("swagger path:{} operation:{}", path, m),
160                    ));
161                }
162            }
163        }
164        alerts
165    }
166
167    pub fn check_contains_response(&self) -> Vec<Alert> {
168        let mut alerts: Vec<Alert> = vec![];
169        for (path, item) in &self.swagger.get_paths() {
170            for (m, op) in item.get_ops() {
171                if op.responses.is_none() || op.responses.as_ref().unwrap().is_empty() {
172                    alerts.push(Alert::new(
173                        Level::Low,
174                        "Operation has no responses",
175                        format!("swagger path:{} operation:{}", path, m),
176                    ));
177                }
178            }
179        }
180        alerts
181    }
182}
183
184const LIST_CONTENT_TYPE: [&str; 35] = [
185    "application/java-archive",
186    "application/json",
187    "application/xml",
188    "multipart/form-data",
189    "application/EDI-X12",
190    "application/EDIFACT",
191    "application/javascript",
192    "application/octet-stream",
193    "application/ogg",
194    "application/pdf",
195    "application/pdf",
196    "application/xhtml+xml",
197    "application/x-shockwave-flash",
198    "application/json",
199    "application/ld+json",
200    "application/xml",
201    "application/zip",
202    "application/x-www-form-urlencoded",
203    "image/gif",
204    "image/jpeg",
205    "image/png",
206    "image/tiff",
207    "image/vnd.microsoft.icon",
208    "image/x-icon",
209    "image/vnd.djvu",
210    "image/svg+xml",
211    "text/css",
212    "text/csv",
213    "text/html",
214    "text/plain",
215    "text/xml",
216    "multipart/mixed",
217    "multipart/alternative",
218    "multipart/related",
219    "multipart/form-data",
220];