Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com> Reviewed-on: #1
278 lines
8.4 KiB
Rust
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(),
|
|
}
|
|
}
|
|
}
|