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];