feat: findings refinement, new scanners, and deployment tooling (#6)
Some checks failed
CI / Format (push) Successful in 3s
CI / Clippy (push) Successful in 4m3s
CI / Security Audit (push) Successful in 1m38s
CI / Tests (push) Successful in 4m44s
CI / Detect Changes (push) Successful in 2s
CI / Deploy Agent (push) Successful in 2s
CI / Deploy Dashboard (push) Successful in 2s
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Failing after 2s

This commit was merged in pull request #6.
This commit is contained in:
2026-03-09 12:53:12 +00:00
parent 32e5fc21e7
commit 46bf9de549
40 changed files with 2048 additions and 118 deletions

View File

@@ -23,6 +23,7 @@ pub struct CveAlert {
pub summary: Option<String>,
pub llm_impact_summary: Option<String>,
pub references: Vec<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
}

View File

@@ -58,7 +58,9 @@ pub struct DastTarget {
pub rate_limit: u32,
/// Whether destructive tests (DELETE, PUT) are allowed
pub allow_destructive: bool,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub updated_at: DateTime<Utc>,
}
@@ -135,7 +137,9 @@ pub struct DastScanRun {
pub error_message: Option<String>,
/// Linked SAST scan run ID (if triggered as part of pipeline)
pub sast_scan_run_id: Option<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub started_at: DateTime<Utc>,
#[serde(default, with = "super::serde_helpers::opt_bson_datetime")]
pub completed_at: Option<DateTime<Utc>>,
}
@@ -240,6 +244,7 @@ pub struct DastFinding {
pub remediation: Option<String>,
/// Linked SAST finding ID (if correlated)
pub linked_sast_finding_id: Option<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
}

View File

@@ -71,7 +71,14 @@ pub struct Finding {
pub status: FindingStatus,
pub tracker_issue_url: Option<String>,
pub scan_run_id: Option<String>,
/// LLM triage action and reasoning
pub triage_action: Option<String>,
pub triage_rationale: Option<String>,
/// Developer feedback on finding quality
pub developer_feedback: Option<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub updated_at: DateTime<Utc>,
}
@@ -108,6 +115,9 @@ impl Finding {
status: FindingStatus::Open,
tracker_issue_url: None,
scan_run_id: None,
triage_action: None,
triage_rationale: None,
developer_feedback: None,
created_at: now,
updated_at: now,
}

View File

@@ -122,7 +122,9 @@ pub struct GraphBuildRun {
pub community_count: u32,
pub languages_parsed: Vec<String>,
pub error_message: Option<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub started_at: DateTime<Utc>,
#[serde(default, with = "super::serde_helpers::opt_bson_datetime")]
pub completed_at: Option<DateTime<Utc>>,
}
@@ -164,6 +166,7 @@ pub struct ImpactAnalysis {
pub direct_callers: Vec<String>,
/// Direct callees of the affected function
pub direct_callees: Vec<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
}

View File

@@ -49,7 +49,9 @@ pub struct TrackerIssue {
pub external_url: String,
pub title: String,
pub status: IssueStatus,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub updated_at: DateTime<Utc>,
}

View File

@@ -62,6 +62,8 @@ pub struct McpServerConfig {
pub mongodb_uri: Option<String>,
/// Database name
pub mongodb_database: Option<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub updated_at: DateTime<Utc>,
}

View File

@@ -10,6 +10,7 @@ pub mod mcp;
pub mod repository;
pub mod sbom;
pub mod scan;
pub(crate) mod serde_helpers;
pub use auth::AuthInfo;
pub use chat::{ChatMessage, ChatRequest, ChatResponse, SourceReference};

View File

@@ -28,17 +28,23 @@ pub struct TrackedRepository {
pub tracker_type: Option<TrackerType>,
pub tracker_owner: Option<String>,
pub tracker_repo: Option<String>,
/// Optional auth token for HTTPS private repos (PAT or password)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth_token: Option<String>,
/// Optional username for HTTPS auth (defaults to "x-access-token" for PATs)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth_username: Option<String>,
pub last_scanned_commit: Option<String>,
#[serde(default, deserialize_with = "deserialize_findings_count")]
pub findings_count: u32,
#[serde(
default = "chrono::Utc::now",
deserialize_with = "deserialize_datetime"
with = "super::serde_helpers::bson_datetime"
)]
pub created_at: DateTime<Utc>,
#[serde(
default = "chrono::Utc::now",
deserialize_with = "deserialize_datetime"
with = "super::serde_helpers::bson_datetime"
)]
pub updated_at: DateTime<Utc>,
}
@@ -47,23 +53,6 @@ fn default_branch() -> String {
"main".to_string()
}
/// Handles findings_count stored as either a plain integer or a BSON Int64
/// which the driver may present as a map `{"low": N, "high": N, "unsigned": bool}`.
/// Handles datetime stored as either a BSON DateTime or an RFC 3339 string.
fn deserialize_datetime<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let bson = bson::Bson::deserialize(deserializer)?;
match bson {
bson::Bson::DateTime(dt) => Ok(dt.into()),
bson::Bson::String(s) => s.parse::<DateTime<Utc>>().map_err(serde::de::Error::custom),
other => Err(serde::de::Error::custom(format!(
"expected DateTime or string, got: {other:?}"
))),
}
}
fn deserialize_findings_count<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: Deserializer<'de>,
@@ -87,6 +76,8 @@ impl TrackedRepository {
default_branch: "main".to_string(),
local_path: None,
scan_schedule: None,
auth_token: None,
auth_username: None,
webhook_enabled: false,
tracker_type: None,
tracker_owner: None,

View File

@@ -20,7 +20,9 @@ pub struct SbomEntry {
pub license: Option<String>,
pub purl: Option<String>,
pub known_vulnerabilities: Vec<VulnRef>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub created_at: DateTime<Utc>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub updated_at: DateTime<Utc>,
}

View File

@@ -13,6 +13,9 @@ pub enum ScanType {
OAuth,
Graph,
Dast,
SecretDetection,
Lint,
CodeReview,
}
impl std::fmt::Display for ScanType {
@@ -25,6 +28,9 @@ impl std::fmt::Display for ScanType {
Self::OAuth => write!(f, "oauth"),
Self::Graph => write!(f, "graph"),
Self::Dast => write!(f, "dast"),
Self::SecretDetection => write!(f, "secret_detection"),
Self::Lint => write!(f, "lint"),
Self::CodeReview => write!(f, "code_review"),
}
}
}
@@ -45,6 +51,9 @@ pub enum ScanPhase {
SbomGeneration,
CveScanning,
PatternScanning,
SecretDetection,
LintScanning,
CodeReview,
GraphBuilding,
LlmTriage,
IssueCreation,
@@ -64,7 +73,9 @@ pub struct ScanRun {
pub phases_completed: Vec<ScanPhase>,
pub new_findings_count: u32,
pub error_message: Option<String>,
#[serde(with = "super::serde_helpers::bson_datetime")]
pub started_at: DateTime<Utc>,
#[serde(default, with = "super::serde_helpers::opt_bson_datetime")]
pub completed_at: Option<DateTime<Utc>>,
}

View File

@@ -0,0 +1,68 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Deserializer, Serializer};
/// Serialize/deserialize `DateTime<Utc>` as BSON DateTime.
/// Handles both BSON DateTime objects and RFC 3339 strings on deserialization.
pub mod bson_datetime {
use super::*;
use serde::Serialize as _;
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let bson_dt: bson::DateTime = (*dt).into();
bson_dt.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let bson_val = bson::Bson::deserialize(deserializer)?;
match bson_val {
bson::Bson::DateTime(dt) => Ok(dt.into()),
bson::Bson::String(s) => s.parse::<DateTime<Utc>>().map_err(serde::de::Error::custom),
other => Err(serde::de::Error::custom(format!(
"expected DateTime or string, got: {other:?}"
))),
}
}
}
/// Serialize/deserialize `Option<DateTime<Utc>>` as BSON DateTime.
pub mod opt_bson_datetime {
use super::*;
use serde::Serialize as _;
pub fn serialize<S>(dt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match dt {
Some(dt) => {
let bson_dt: bson::DateTime = (*dt).into();
bson_dt.serialize(serializer)
}
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: Deserializer<'de>,
{
let bson_val = Option::<bson::Bson>::deserialize(deserializer)?;
match bson_val {
Some(bson::Bson::DateTime(dt)) => Ok(Some(dt.into())),
Some(bson::Bson::String(s)) => s
.parse::<DateTime<Utc>>()
.map(Some)
.map_err(serde::de::Error::custom),
Some(bson::Bson::Null) | None => Ok(None),
Some(other) => Err(serde::de::Error::custom(format!(
"expected DateTime, string, or null, got: {other:?}"
))),
}
}
}