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
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:
@@ -24,6 +24,7 @@ pub struct AgentConfig {
|
||||
pub scan_schedule: String,
|
||||
pub cve_monitor_schedule: String,
|
||||
pub git_clone_base_path: String,
|
||||
pub ssh_key_path: String,
|
||||
pub keycloak_url: Option<String>,
|
||||
pub keycloak_realm: Option<String>,
|
||||
}
|
||||
@@ -34,4 +35,5 @@ pub struct DashboardConfig {
|
||||
pub mongodb_database: String,
|
||||
pub agent_api_url: String,
|
||||
pub dashboard_port: u16,
|
||||
pub mcp_endpoint_url: Option<String>,
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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>>,
|
||||
}
|
||||
|
||||
|
||||
68
compliance-core/src/models/serde_helpers.rs
Normal file
68
compliance-core/src/models/serde_helpers.rs
Normal 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:?}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user