1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Deserialize)]
7pub struct NewTaskResponse {
8 pub success: bool,
10 pub taskid: Option<String>,
12 pub message: Option<String>,
14}
15
16#[derive(Debug, Clone, Deserialize)]
18pub struct BasicResponse {
19 pub success: bool,
21 pub message: Option<String>,
23}
24
25#[derive(Debug, Clone, Deserialize)]
27pub struct StatusResponse {
28 pub success: bool,
30 pub status: Option<String>,
32 pub returncode: Option<i32>,
34}
35
36#[derive(Debug, Clone, Deserialize, Serialize)]
38pub struct SqlmapDataChunk {
39 pub r#type: i32,
41 pub value: serde_json::Value,
43}
44
45#[derive(Debug, Clone, Deserialize)]
47pub struct DataResponse {
48 pub success: bool,
50 pub data: Option<Vec<SqlmapDataChunk>>,
52 pub error: Option<Vec<String>>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct SqlmapFinding {
59 pub parameter: String,
61 pub vulnerability_type: String,
63 pub payload: String,
65 pub details: serde_json::Value,
67}
68
69impl DataResponse {
70 pub fn findings(&self) -> Vec<SqlmapFinding> {
75 let Some(ref chunks) = self.data else { return vec![] };
76 let mut findings = Vec::new();
77
78 for chunk in chunks {
79 if chunk.r#type == 1 {
81 if let Some(arr) = chunk.value.as_array() {
82 for item in arr {
83 if let Some(obj) = item.as_object() {
84 let parameter = obj.get("parameter")
85 .and_then(|v| v.as_str())
86 .unwrap_or("unknown")
87 .to_string();
88 let vulnerability_type = obj.get("type")
89 .and_then(|v| v.as_str())
90 .unwrap_or("unknown")
91 .to_string();
92 let payload = obj.get("payload")
93 .and_then(|v| v.as_str())
94 .unwrap_or("")
95 .to_string();
96 findings.push(SqlmapFinding {
97 parameter,
98 vulnerability_type,
99 payload,
100 details: item.clone(),
101 });
102 }
103 }
104 }
105 }
106 }
107
108 findings
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Default)]
114pub struct SqlmapOptions {
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub url: Option<String>,
118
119 #[serde(rename = "testParameter", skip_serializing_if = "Option::is_none")]
121 pub test_parameter: Option<String>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub dbms: Option<String>,
126
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub cookie: Option<String>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub tech: Option<String>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
138 pub verbose: Option<i32>,
139
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub threads: Option<i32>,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub batch: Option<bool>,
147
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub headers: Option<String>,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub risk: Option<i32>,
155
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub level: Option<i32>,
159
160 #[serde(skip_serializing_if = "Option::is_none")]
162 pub proxy: Option<String>,
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn empty_data_response_gives_no_findings() {
171 let resp = DataResponse { success: true, data: None, error: None };
172 assert!(resp.findings().is_empty());
173 }
174
175 #[test]
176 fn type_0_chunks_ignored() {
177 let resp = DataResponse {
178 success: true,
179 data: Some(vec![SqlmapDataChunk { r#type: 0, value: serde_json::json!("log message") }]),
180 error: None,
181 };
182 assert!(resp.findings().is_empty());
183 }
184
185 #[test]
186 fn type_1_chunk_parsed_as_finding() {
187 let resp = DataResponse {
188 success: true,
189 data: Some(vec![SqlmapDataChunk {
190 r#type: 1,
191 value: serde_json::json!([{
192 "parameter": "id",
193 "type": "boolean-based blind",
194 "payload": "id=1 AND 1=1"
195 }]),
196 }]),
197 error: None,
198 };
199 let findings = resp.findings();
200 assert_eq!(findings.len(), 1);
201 assert_eq!(findings[0].parameter, "id");
202 assert_eq!(findings[0].vulnerability_type, "boolean-based blind");
203 }
204
205 #[test]
206 fn options_serialization() {
207 let opts = SqlmapOptions {
208 url: Some("http://test.com?id=1".into()),
209 level: Some(3),
210 risk: Some(2),
211 batch: Some(true),
212 ..Default::default()
213 };
214 let json = serde_json::to_string(&opts).unwrap();
215 assert!(json.contains("http://test.com"));
216 assert!(json.contains("\"level\":3"));
217 assert!(!json.contains("dbms"));
219 }
220}