refactor: modularize codebase and add 404 unit tests
Some checks failed
CI / Format (push) Failing after 4s
CI / Format (pull_request) Failing after 4s
CI / Clippy (pull_request) Failing after 1m41s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Clippy (push) Failing after 1m46s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Some checks failed
CI / Format (push) Failing after 4s
CI / Format (pull_request) Failing after 4s
CI / Clippy (pull_request) Failing after 1m41s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Clippy (push) Failing after 1m46s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Split large files into focused modules across all crates while maintaining API compatibility via re-exports. Add comprehensive unit tests covering core models, pipeline parsers, LLM triage, DAST security tools, graph algorithms, and MCP parameter validation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
use compliance_core::error::CoreError;
|
||||
use compliance_core::traits::dast_agent::{DastAgent, DastContext, DiscoveredEndpoint, EndpointParameter};
|
||||
use compliance_core::traits::dast_agent::{
|
||||
DastAgent, DastContext, DiscoveredEndpoint, EndpointParameter,
|
||||
};
|
||||
use compliance_core::traits::pentest_tool::{PentestTool, PentestToolContext, PentestToolResult};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -21,16 +23,38 @@ impl XssTool {
|
||||
let mut endpoints = Vec::new();
|
||||
if let Some(arr) = input.get("endpoints").and_then(|v| v.as_array()) {
|
||||
for ep in arr {
|
||||
let url = ep.get("url").and_then(|v| v.as_str()).unwrap_or_default().to_string();
|
||||
let method = ep.get("method").and_then(|v| v.as_str()).unwrap_or("GET").to_string();
|
||||
let url = ep
|
||||
.get("url")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
let method = ep
|
||||
.get("method")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("GET")
|
||||
.to_string();
|
||||
let mut parameters = Vec::new();
|
||||
if let Some(params) = ep.get("parameters").and_then(|v| v.as_array()) {
|
||||
for p in params {
|
||||
parameters.push(EndpointParameter {
|
||||
name: p.get("name").and_then(|v| v.as_str()).unwrap_or_default().to_string(),
|
||||
location: p.get("location").and_then(|v| v.as_str()).unwrap_or("query").to_string(),
|
||||
param_type: p.get("param_type").and_then(|v| v.as_str()).map(String::from),
|
||||
example_value: p.get("example_value").and_then(|v| v.as_str()).map(String::from),
|
||||
name: p
|
||||
.get("name")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
location: p
|
||||
.get("location")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("query")
|
||||
.to_string(),
|
||||
param_type: p
|
||||
.get("param_type")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from),
|
||||
example_value: p
|
||||
.get("example_value")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -38,8 +62,14 @@ impl XssTool {
|
||||
url,
|
||||
method,
|
||||
parameters,
|
||||
content_type: ep.get("content_type").and_then(|v| v.as_str()).map(String::from),
|
||||
requires_auth: ep.get("requires_auth").and_then(|v| v.as_bool()).unwrap_or(false),
|
||||
content_type: ep
|
||||
.get("content_type")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from),
|
||||
requires_auth: ep
|
||||
.get("requires_auth")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -47,6 +77,91 @@ impl XssTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn parse_endpoints_basic() {
|
||||
let input = json!({
|
||||
"endpoints": [
|
||||
{
|
||||
"url": "https://example.com/search",
|
||||
"method": "GET",
|
||||
"parameters": [
|
||||
{ "name": "q", "location": "query" }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
let endpoints = XssTool::parse_endpoints(&input);
|
||||
assert_eq!(endpoints.len(), 1);
|
||||
assert_eq!(endpoints[0].url, "https://example.com/search");
|
||||
assert_eq!(endpoints[0].method, "GET");
|
||||
assert_eq!(endpoints[0].parameters.len(), 1);
|
||||
assert_eq!(endpoints[0].parameters[0].name, "q");
|
||||
assert_eq!(endpoints[0].parameters[0].location, "query");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_endpoints_empty() {
|
||||
let input = json!({ "endpoints": [] });
|
||||
assert!(XssTool::parse_endpoints(&input).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_endpoints_missing_key() {
|
||||
let input = json!({});
|
||||
assert!(XssTool::parse_endpoints(&input).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_endpoints_defaults() {
|
||||
let input = json!({
|
||||
"endpoints": [
|
||||
{ "url": "https://example.com/api", "parameters": [] }
|
||||
]
|
||||
});
|
||||
let endpoints = XssTool::parse_endpoints(&input);
|
||||
assert_eq!(endpoints[0].method, "GET"); // default
|
||||
assert!(!endpoints[0].requires_auth); // default false
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_endpoints_full_params() {
|
||||
let input = json!({
|
||||
"endpoints": [{
|
||||
"url": "https://example.com",
|
||||
"method": "POST",
|
||||
"content_type": "application/json",
|
||||
"requires_auth": true,
|
||||
"parameters": [{
|
||||
"name": "body",
|
||||
"location": "body",
|
||||
"param_type": "string",
|
||||
"example_value": "test"
|
||||
}]
|
||||
}]
|
||||
});
|
||||
let endpoints = XssTool::parse_endpoints(&input);
|
||||
assert_eq!(endpoints[0].method, "POST");
|
||||
assert_eq!(
|
||||
endpoints[0].content_type.as_deref(),
|
||||
Some("application/json")
|
||||
);
|
||||
assert!(endpoints[0].requires_auth);
|
||||
assert_eq!(
|
||||
endpoints[0].parameters[0].param_type.as_deref(),
|
||||
Some("string")
|
||||
);
|
||||
assert_eq!(
|
||||
endpoints[0].parameters[0].example_value.as_deref(),
|
||||
Some("test")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl PentestTool for XssTool {
|
||||
fn name(&self) -> &str {
|
||||
"xss_scanner"
|
||||
@@ -100,35 +215,37 @@ impl PentestTool for XssTool {
|
||||
&'a self,
|
||||
input: serde_json::Value,
|
||||
context: &'a PentestToolContext,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<PentestToolResult, CoreError>> + Send + 'a>> {
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = Result<PentestToolResult, CoreError>> + Send + 'a>,
|
||||
> {
|
||||
Box::pin(async move {
|
||||
let endpoints = Self::parse_endpoints(&input);
|
||||
if endpoints.is_empty() {
|
||||
return Ok(PentestToolResult {
|
||||
summary: "No endpoints provided to test.".to_string(),
|
||||
findings: Vec::new(),
|
||||
data: json!({}),
|
||||
});
|
||||
}
|
||||
let endpoints = Self::parse_endpoints(&input);
|
||||
if endpoints.is_empty() {
|
||||
return Ok(PentestToolResult {
|
||||
summary: "No endpoints provided to test.".to_string(),
|
||||
findings: Vec::new(),
|
||||
data: json!({}),
|
||||
});
|
||||
}
|
||||
|
||||
let dast_context = DastContext {
|
||||
endpoints,
|
||||
technologies: Vec::new(),
|
||||
sast_hints: Vec::new(),
|
||||
};
|
||||
let dast_context = DastContext {
|
||||
endpoints,
|
||||
technologies: Vec::new(),
|
||||
sast_hints: Vec::new(),
|
||||
};
|
||||
|
||||
let findings = self.agent.run(&context.target, &dast_context).await?;
|
||||
let count = findings.len();
|
||||
let findings = self.agent.run(&context.target, &dast_context).await?;
|
||||
let count = findings.len();
|
||||
|
||||
Ok(PentestToolResult {
|
||||
summary: if count > 0 {
|
||||
format!("Found {count} XSS vulnerabilities.")
|
||||
} else {
|
||||
"No XSS vulnerabilities detected.".to_string()
|
||||
},
|
||||
findings,
|
||||
data: json!({ "endpoints_tested": dast_context.endpoints.len() }),
|
||||
})
|
||||
Ok(PentestToolResult {
|
||||
summary: if count > 0 {
|
||||
format!("Found {count} XSS vulnerabilities.")
|
||||
} else {
|
||||
"No XSS vulnerabilities detected.".to_string()
|
||||
},
|
||||
findings,
|
||||
data: json!({ "endpoints_tested": dast_context.endpoints.len() }),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user