1use serde::{Deserialize, Serialize};
2
3use crate::client::Client;
4use crate::error::Result;
5
6#[derive(Debug, Clone, Serialize)]
12pub struct SecurityScanUrlRequest {
13 pub url: String,
15}
16
17#[derive(Debug, Clone, Serialize, Default)]
19pub struct SecurityScanHtmlRequest {
20 pub html: String,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub visible_text: Option<String>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub url: Option<String>,
30}
31
32#[derive(Debug, Clone, Serialize)]
34pub struct SecurityReportRequest {
35 pub url: String,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub description: Option<String>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub category: Option<String>,
45}
46
47#[derive(Debug, Clone, Deserialize)]
53pub struct SecurityScanResponse {
54 pub assessment: SecurityAssessment,
56
57 #[serde(default)]
59 pub request_id: String,
60}
61
62#[derive(Debug, Clone, Deserialize, Default)]
64pub struct SecurityAssessment {
65 #[serde(default)]
67 pub url: String,
68
69 #[serde(default)]
71 pub threat_level: String,
72
73 #[serde(default)]
75 pub threat_score: f64,
76
77 #[serde(default)]
79 pub findings: Vec<SecurityFinding>,
80
81 #[serde(default)]
83 pub hidden_text_length: i32,
84
85 #[serde(default)]
87 pub visible_text_length: i32,
88
89 #[serde(default)]
91 pub hidden_ratio: f64,
92
93 #[serde(default)]
95 pub summary: String,
96}
97
98#[derive(Debug, Clone, Deserialize, Default)]
100pub struct SecurityFinding {
101 #[serde(default)]
105 pub category: String,
106
107 #[serde(default)]
109 pub pattern: String,
110
111 #[serde(default)]
113 pub content: String,
114
115 #[serde(default)]
117 pub location: String,
118
119 #[serde(default)]
121 pub threat: String,
122
123 #[serde(default)]
125 pub confidence: f64,
126
127 #[serde(default)]
129 pub description: String,
130}
131
132#[derive(Debug, Clone, Deserialize)]
134pub struct SecurityCheckResponse {
135 #[serde(default)]
137 pub url: String,
138
139 #[serde(default)]
141 pub blocked: bool,
142
143 #[serde(default)]
145 pub threat_level: Option<String>,
146
147 #[serde(default)]
149 pub threat_score: Option<f64>,
150
151 #[serde(default)]
153 pub categories: Option<Vec<String>>,
154
155 #[serde(default)]
157 pub first_seen: Option<String>,
158
159 #[serde(default)]
161 pub last_seen: Option<String>,
162
163 #[serde(default)]
165 pub report_count: Option<i32>,
166
167 #[serde(default)]
169 pub status: Option<String>,
170
171 #[serde(default)]
173 pub message: Option<String>,
174}
175
176#[derive(Debug, Clone, Deserialize)]
178pub struct SecurityBlocklistResponse {
179 #[serde(default)]
181 pub entries: Vec<SecurityBlocklistEntry>,
182
183 #[serde(default)]
185 pub count: i32,
186
187 #[serde(default)]
189 pub status: String,
190}
191
192#[derive(Debug, Clone, Deserialize, Default)]
194pub struct SecurityBlocklistEntry {
195 #[serde(default)]
197 pub id: Option<String>,
198
199 #[serde(default)]
201 pub url: String,
202
203 #[serde(default)]
205 pub status: String,
206
207 #[serde(default)]
209 pub threat_level: String,
210
211 #[serde(default)]
213 pub threat_score: f64,
214
215 #[serde(default)]
217 pub categories: Vec<String>,
218
219 #[serde(default)]
221 pub findings_count: i32,
222
223 #[serde(default)]
225 pub hidden_ratio: f64,
226
227 #[serde(default)]
229 pub first_seen: Option<String>,
230
231 #[serde(default)]
233 pub summary: String,
234}
235
236#[derive(Debug, Clone, Deserialize)]
238pub struct SecurityReportResponse {
239 #[serde(default)]
241 pub url: String,
242
243 #[serde(default)]
245 pub status: String,
246
247 #[serde(default)]
249 pub message: String,
250
251 #[serde(default)]
253 pub threat_level: Option<String>,
254}
255
256impl Client {
261 pub async fn security_scan_url(&self, url: &str) -> Result<SecurityScanResponse> {
263 let req = SecurityScanUrlRequest { url: url.to_string() };
264 let (resp, _) = self.post_json("/qai/v1/security/scan-url", &req).await?;
265 Ok(resp)
266 }
267
268 pub async fn security_scan_html(&self, req: &SecurityScanHtmlRequest) -> Result<SecurityScanResponse> {
270 let (resp, _) = self.post_json("/qai/v1/security/scan-html", req).await?;
271 Ok(resp)
272 }
273
274 pub async fn security_check(&self, url: &str) -> Result<SecurityCheckResponse> {
276 let encoded = urlencoding::encode(url);
277 let (resp, _) = self.get_json(&format!("/qai/v1/security/check?url={}", encoded)).await?;
278 Ok(resp)
279 }
280
281 pub async fn security_blocklist(&self, status: Option<&str>) -> Result<SecurityBlocklistResponse> {
283 let path = match status {
284 Some(s) => format!("/qai/v1/security/blocklist?status={}", s),
285 None => "/qai/v1/security/blocklist".into(),
286 };
287 let (resp, _) = self.get_json(&path).await?;
288 Ok(resp)
289 }
290
291 pub async fn security_report(&self, req: &SecurityReportRequest) -> Result<SecurityReportResponse> {
293 let (resp, _) = self.post_json("/qai/v1/security/report", req).await?;
294 Ok(resp)
295 }
296}