Files
compliance-scanner-agent/compliance-core/src/models/dast.rs
Sharang Parnerkar 42cabf0582
All checks were successful
CI / Format (push) Successful in 2s
CI / Clippy (push) Successful in 2m56s
CI / Security Audit (push) Successful in 1m25s
CI / Tests (push) Successful in 3m57s
feat: rag-embedding-ai-chat (#1)
Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com>
Reviewed-on: #1
2026-03-06 21:54:15 +00:00

278 lines
8.4 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use super::finding::Severity;
/// Type of DAST target application
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DastTargetType {
WebApp,
RestApi,
GraphQl,
}
impl std::fmt::Display for DastTargetType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::WebApp => write!(f, "webapp"),
Self::RestApi => write!(f, "rest_api"),
Self::GraphQl => write!(f, "graphql"),
}
}
}
/// Authentication configuration for DAST target
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DastAuthConfig {
/// Authentication method: "none", "basic", "bearer", "cookie", "form"
pub method: String,
/// Login URL for form-based auth
pub login_url: Option<String>,
/// Username or token
pub username: Option<String>,
/// Password (stored encrypted in practice)
pub password: Option<String>,
/// Bearer token
pub token: Option<String>,
/// Custom headers for auth
pub headers: Option<std::collections::HashMap<String, String>>,
}
/// A target for DAST scanning
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DastTarget {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<bson::oid::ObjectId>,
pub name: String,
pub base_url: String,
pub target_type: DastTargetType,
pub auth_config: Option<DastAuthConfig>,
/// Linked repository ID (for SAST correlation)
pub repo_id: Option<String>,
/// URL paths to exclude from scanning
pub excluded_paths: Vec<String>,
/// Maximum crawl depth
pub max_crawl_depth: u32,
/// Rate limit (requests per second)
pub rate_limit: u32,
/// Whether destructive tests (DELETE, PUT) are allowed
pub allow_destructive: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl DastTarget {
pub fn new(name: String, base_url: String, target_type: DastTargetType) -> Self {
let now = Utc::now();
Self {
id: None,
name,
base_url,
target_type,
auth_config: None,
repo_id: None,
excluded_paths: Vec::new(),
max_crawl_depth: 5,
rate_limit: 10,
allow_destructive: false,
created_at: now,
updated_at: now,
}
}
}
/// Phase of a DAST scan
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DastScanPhase {
Reconnaissance,
Crawling,
VulnerabilityAnalysis,
Exploitation,
Reporting,
Completed,
}
impl std::fmt::Display for DastScanPhase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Reconnaissance => write!(f, "reconnaissance"),
Self::Crawling => write!(f, "crawling"),
Self::VulnerabilityAnalysis => write!(f, "vulnerability_analysis"),
Self::Exploitation => write!(f, "exploitation"),
Self::Reporting => write!(f, "reporting"),
Self::Completed => write!(f, "completed"),
}
}
}
/// Status of a DAST scan run
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DastScanStatus {
Running,
Completed,
Failed,
Cancelled,
}
/// A DAST scan run
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DastScanRun {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<bson::oid::ObjectId>,
pub target_id: String,
pub status: DastScanStatus,
pub current_phase: DastScanPhase,
pub phases_completed: Vec<DastScanPhase>,
/// Number of endpoints discovered during crawling
pub endpoints_discovered: u32,
/// Number of findings
pub findings_count: u32,
/// Number of confirmed exploitable findings
pub exploitable_count: u32,
pub error_message: Option<String>,
/// Linked SAST scan run ID (if triggered as part of pipeline)
pub sast_scan_run_id: Option<String>,
pub started_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
}
impl DastScanRun {
pub fn new(target_id: String) -> Self {
Self {
id: None,
target_id,
status: DastScanStatus::Running,
current_phase: DastScanPhase::Reconnaissance,
phases_completed: Vec::new(),
endpoints_discovered: 0,
findings_count: 0,
exploitable_count: 0,
error_message: None,
sast_scan_run_id: None,
started_at: Utc::now(),
completed_at: None,
}
}
}
/// Type of DAST vulnerability
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DastVulnType {
SqlInjection,
Xss,
AuthBypass,
Ssrf,
ApiMisconfiguration,
OpenRedirect,
Idor,
InformationDisclosure,
SecurityMisconfiguration,
BrokenAuth,
Other,
}
impl std::fmt::Display for DastVulnType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SqlInjection => write!(f, "sql_injection"),
Self::Xss => write!(f, "xss"),
Self::AuthBypass => write!(f, "auth_bypass"),
Self::Ssrf => write!(f, "ssrf"),
Self::ApiMisconfiguration => write!(f, "api_misconfiguration"),
Self::OpenRedirect => write!(f, "open_redirect"),
Self::Idor => write!(f, "idor"),
Self::InformationDisclosure => write!(f, "information_disclosure"),
Self::SecurityMisconfiguration => write!(f, "security_misconfiguration"),
Self::BrokenAuth => write!(f, "broken_auth"),
Self::Other => write!(f, "other"),
}
}
}
/// Evidence collected during DAST testing
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DastEvidence {
/// HTTP request that triggered the finding
pub request_method: String,
pub request_url: String,
pub request_headers: Option<std::collections::HashMap<String, String>>,
pub request_body: Option<String>,
/// HTTP response
pub response_status: u16,
pub response_headers: Option<std::collections::HashMap<String, String>>,
/// Relevant snippet of response body
pub response_snippet: Option<String>,
/// Path to screenshot file (if captured)
pub screenshot_path: Option<String>,
/// The payload that triggered the vulnerability
pub payload: Option<String>,
/// Timing information (for timing-based attacks)
pub response_time_ms: Option<u64>,
}
/// A finding from DAST scanning
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DastFinding {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<bson::oid::ObjectId>,
pub scan_run_id: String,
pub target_id: String,
pub vuln_type: DastVulnType,
pub title: String,
pub description: String,
pub severity: Severity,
pub cwe: Option<String>,
/// The URL endpoint where the vulnerability was found
pub endpoint: String,
/// HTTP method
pub method: String,
/// Parameter that is vulnerable
pub parameter: Option<String>,
/// Whether exploitability was confirmed with a working payload
pub exploitable: bool,
/// Evidence chain
pub evidence: Vec<DastEvidence>,
/// Remediation guidance
pub remediation: Option<String>,
/// Linked SAST finding ID (if correlated)
pub linked_sast_finding_id: Option<String>,
pub created_at: DateTime<Utc>,
}
impl DastFinding {
#[allow(clippy::too_many_arguments)]
pub fn new(
scan_run_id: String,
target_id: String,
vuln_type: DastVulnType,
title: String,
description: String,
severity: Severity,
endpoint: String,
method: String,
) -> Self {
Self {
id: None,
scan_run_id,
target_id,
vuln_type,
title,
description,
severity,
cwe: None,
endpoint,
method,
parameter: None,
exploitable: false,
evidence: Vec::new(),
remediation: None,
linked_sast_finding_id: None,
created_at: Utc::now(),
}
}
}