use crate::attacks::{self, AttackTest};
use crate::{AttackResult, GatewayConfig};
use std::time::Instant;
async fn send_evaluate(
client: &reqwest::Client,
config: &GatewayConfig,
payload: &serde_json::Value,
timeout_secs: u64,
) -> Result<EvaluateResponse, String> {
let url = format!("{}{}", config.base_url, config.evaluate_path);
let mut req = client
.post(&url)
.header("Content-Type", "application/json")
.timeout(std::time::Duration::from_secs(timeout_secs))
.json(payload);
if let Some(ref auth) = config.auth_header {
req = req.header("Authorization", format!("Bearer {auth}"));
}
let resp = req
.send()
.await
.map_err(|e| format!("Request failed: {e}"))?;
let status = resp.status().as_u16();
let body = resp
.text()
.await
.map_err(|e| format!("Failed to read response body: {e}"))?;
let json: serde_json::Value =
serde_json::from_str(&body).unwrap_or(serde_json::json!({"raw": body}));
Ok(EvaluateResponse { status, body: json })
}
struct EvaluateResponse {
status: u16,
body: serde_json::Value,
}
pub async fn run_all(config: &GatewayConfig, timeout_secs: u64) -> Vec<AttackResult> {
let all_tests = attacks::all_tests();
let client = match reqwest::Client::builder()
.danger_accept_invalid_certs(false)
.build()
{
Ok(client) => client,
Err(e) => {
let details = format!("HTTP client initialization failed: {e}");
return all_tests
.iter()
.map(|test| AttackResult {
attack_id: test.id.to_string(),
name: test.name.to_string(),
class: test.class.to_string(),
passed: false,
latency_ns: 0,
details: details.clone(),
})
.collect();
}
};
let mut results = Vec::with_capacity(all_tests.len());
for test in &all_tests {
let start = Instant::now();
let passed = run_single_test(&client, config, test, timeout_secs).await;
let latency_ns = start.elapsed().as_nanos() as u64;
results.push(AttackResult {
attack_id: test.id.to_string(),
name: test.name.to_string(),
class: test.class.to_string(),
passed,
latency_ns,
details: if passed {
"Gateway correctly handled the attack".to_string()
} else {
"Gateway did not detect or block the attack".to_string()
},
});
}
results
}
async fn run_single_test(
client: &reqwest::Client,
config: &GatewayConfig,
test: &AttackTest,
timeout_secs: u64,
) -> bool {
if let Some(count) = test
.payload
.get("_test_rapid_requests")
.and_then(|v| v.as_u64())
{
let mut clean_payload = test.payload.clone();
if let Some(obj) = clean_payload.as_object_mut() {
obj.remove("_test_rapid_requests");
}
let mut any_passed = false;
for _ in 0..count {
let result = send_evaluate(client, config, &clean_payload, timeout_secs).await;
if let Ok(resp) = result {
if (test.check_fn)(&resp.body, resp.status) {
any_passed = true;
break;
}
}
}
return any_passed;
}
let result = send_evaluate(client, config, &test.payload, timeout_secs).await;
match result {
Ok(resp) => (test.check_fn)(&resp.body, resp.status),
Err(_) => false,
}
}