Skip to main content

quantum_sdk/
security.rs

1use serde::{Deserialize, Serialize};
2
3use crate::client::Client;
4use crate::error::Result;
5
6// ---------------------------------------------------------------------------
7// Scan requests
8// ---------------------------------------------------------------------------
9
10/// Request body for scanning a URL for prompt injection.
11#[derive(Debug, Clone, Serialize)]
12pub struct SecurityScanUrlRequest {
13    /// URL to scan.
14    pub url: String,
15}
16
17/// Request body for scanning raw HTML content.
18#[derive(Debug, Clone, Serialize, Default)]
19pub struct SecurityScanHtmlRequest {
20    /// Raw HTML to scan.
21    pub html: String,
22
23    /// Rendered visible text (for structural analysis).
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub visible_text: Option<String>,
26
27    /// Source URL (for context).
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub url: Option<String>,
30}
31
32/// Request body for reporting a suspicious URL.
33#[derive(Debug, Clone, Serialize)]
34pub struct SecurityReportRequest {
35    /// URL to report.
36    pub url: String,
37
38    /// Description of the suspected threat.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub description: Option<String>,
41
42    /// Category of the suspected threat.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub category: Option<String>,
45}
46
47// ---------------------------------------------------------------------------
48// Response types
49// ---------------------------------------------------------------------------
50
51/// Response from a security scan.
52#[derive(Debug, Clone, Deserialize)]
53pub struct SecurityScanResponse {
54    /// Full threat assessment.
55    pub assessment: SecurityAssessment,
56
57    /// Request identifier.
58    #[serde(default)]
59    pub request_id: String,
60}
61
62/// Threat assessment for a scanned page.
63#[derive(Debug, Clone, Deserialize, Default)]
64pub struct SecurityAssessment {
65    /// Source URL.
66    #[serde(default)]
67    pub url: String,
68
69    /// Overall threat level: "none", "low", "medium", "high", "critical".
70    #[serde(default)]
71    pub threat_level: String,
72
73    /// Numeric threat score (0.0 - 100.0).
74    #[serde(default)]
75    pub threat_score: f64,
76
77    /// Individual findings.
78    #[serde(default)]
79    pub findings: Vec<SecurityFinding>,
80
81    /// Length of hidden text content detected.
82    #[serde(default)]
83    pub hidden_text_length: i32,
84
85    /// Length of visible text content.
86    #[serde(default)]
87    pub visible_text_length: i32,
88
89    /// Ratio of hidden to total content.
90    #[serde(default)]
91    pub hidden_ratio: f64,
92
93    /// Human-readable summary.
94    #[serde(default)]
95    pub summary: String,
96}
97
98/// A single detected injection pattern.
99#[derive(Debug, Clone, Deserialize, Default)]
100pub struct SecurityFinding {
101    /// Category: "instruction_override", "role_impersonation", "data_exfiltration",
102    /// "hidden_text", "hidden_comment", "model_targeting", "encoded_payload",
103    /// "structural_anomaly", "meta_injection", "safety_override".
104    #[serde(default)]
105    pub category: String,
106
107    /// Pattern that matched.
108    #[serde(default)]
109    pub pattern: String,
110
111    /// Offending content (truncated).
112    #[serde(default)]
113    pub content: String,
114
115    /// Location in the page.
116    #[serde(default)]
117    pub location: String,
118
119    /// Threat level for this finding.
120    #[serde(default)]
121    pub threat: String,
122
123    /// Detection confidence (0.0 - 1.0).
124    #[serde(default)]
125    pub confidence: f64,
126
127    /// Human-readable description.
128    #[serde(default)]
129    pub description: String,
130}
131
132/// Response from checking a URL against the registry.
133#[derive(Debug, Clone, Deserialize)]
134pub struct SecurityCheckResponse {
135    /// URL that was checked.
136    #[serde(default)]
137    pub url: String,
138
139    /// Whether the URL is blocked.
140    #[serde(default)]
141    pub blocked: bool,
142
143    /// Threat level (if blocked).
144    #[serde(default)]
145    pub threat_level: Option<String>,
146
147    /// Threat score (if blocked).
148    #[serde(default)]
149    pub threat_score: Option<f64>,
150
151    /// Detection categories (if blocked).
152    #[serde(default)]
153    pub categories: Option<Vec<String>>,
154
155    /// First seen timestamp.
156    #[serde(default)]
157    pub first_seen: Option<String>,
158
159    /// Last seen timestamp.
160    #[serde(default)]
161    pub last_seen: Option<String>,
162
163    /// Number of reports.
164    #[serde(default)]
165    pub report_count: Option<i32>,
166
167    /// Registry status: "confirmed", "suspected".
168    #[serde(default)]
169    pub status: Option<String>,
170
171    /// Human-readable message.
172    #[serde(default)]
173    pub message: Option<String>,
174}
175
176/// Response from the blocklist feed.
177#[derive(Debug, Clone, Deserialize)]
178pub struct SecurityBlocklistResponse {
179    /// Blocklist entries.
180    #[serde(default)]
181    pub entries: Vec<SecurityBlocklistEntry>,
182
183    /// Total count.
184    #[serde(default)]
185    pub count: i32,
186
187    /// Filter status used.
188    #[serde(default)]
189    pub status: String,
190}
191
192/// A single blocklist entry.
193#[derive(Debug, Clone, Deserialize, Default)]
194pub struct SecurityBlocklistEntry {
195    /// Entry identifier.
196    #[serde(default)]
197    pub id: Option<String>,
198
199    /// Blocked URL.
200    #[serde(default)]
201    pub url: String,
202
203    /// Registry status.
204    #[serde(default)]
205    pub status: String,
206
207    /// Threat level.
208    #[serde(default)]
209    pub threat_level: String,
210
211    /// Threat score.
212    #[serde(default)]
213    pub threat_score: f64,
214
215    /// Detection categories.
216    #[serde(default)]
217    pub categories: Vec<String>,
218
219    /// Number of findings.
220    #[serde(default)]
221    pub findings_count: i32,
222
223    /// Hidden content ratio.
224    #[serde(default)]
225    pub hidden_ratio: f64,
226
227    /// First seen timestamp.
228    #[serde(default)]
229    pub first_seen: Option<String>,
230
231    /// Summary.
232    #[serde(default)]
233    pub summary: String,
234}
235
236/// Response from reporting a URL.
237#[derive(Debug, Clone, Deserialize)]
238pub struct SecurityReportResponse {
239    /// URL that was reported.
240    #[serde(default)]
241    pub url: String,
242
243    /// Report status: "existing" or "suspected".
244    #[serde(default)]
245    pub status: String,
246
247    /// Message.
248    #[serde(default)]
249    pub message: String,
250
251    /// Threat level (if already in registry).
252    #[serde(default)]
253    pub threat_level: Option<String>,
254}
255
256// ---------------------------------------------------------------------------
257// Client methods
258// ---------------------------------------------------------------------------
259
260impl Client {
261    /// Scan a URL for prompt injection attacks.
262    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    /// Scan raw HTML content for prompt injection.
269    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    /// Check a URL against the injection registry.
275    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    /// Get the injection blocklist feed.
282    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    /// Report a suspicious URL.
292    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}