style: fix cargo fmt formatting
Some checks failed
CI / Format (pull_request) Successful in 4s
CI / Clippy (push) Failing after 2m48s
CI / Detect Changes (pull_request) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (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 / Clippy (pull_request) Failing after 2m34s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Format (push) Successful in 3s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Some checks failed
CI / Format (pull_request) Successful in 4s
CI / Clippy (push) Failing after 2m48s
CI / Detect Changes (pull_request) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (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 / Clippy (pull_request) Failing after 2m34s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Format (push) Successful in 3s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -192,7 +192,8 @@ pub async fn build_embeddings(
|
||||
auth_token: repo.auth_token.clone(),
|
||||
auth_username: repo.auth_username.clone(),
|
||||
};
|
||||
let git_ops = crate::pipeline::git::GitOps::new(&agent_clone.config.git_clone_base_path, creds);
|
||||
let git_ops =
|
||||
crate::pipeline::git::GitOps::new(&agent_clone.config.git_clone_base_path, creds);
|
||||
let repo_path = match git_ops.clone_or_fetch(&repo.git_url, &repo.name) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
|
||||
@@ -296,7 +296,8 @@ pub async fn trigger_build(
|
||||
auth_token: repo.auth_token.clone(),
|
||||
auth_username: repo.auth_username.clone(),
|
||||
};
|
||||
let git_ops = crate::pipeline::git::GitOps::new(&agent_clone.config.git_clone_base_path, creds);
|
||||
let git_ops =
|
||||
crate::pipeline::git::GitOps::new(&agent_clone.config.git_clone_base_path, creds);
|
||||
let repo_path = match git_ops.clone_or_fetch(&repo.git_url, &repo.name) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
|
||||
@@ -315,7 +315,12 @@ pub async fn add_repository(
|
||||
.repositories()
|
||||
.insert_one(&repo)
|
||||
.await
|
||||
.map_err(|_| (StatusCode::CONFLICT, "Repository already exists".to_string()))?;
|
||||
.map_err(|_| {
|
||||
(
|
||||
StatusCode::CONFLICT,
|
||||
"Repository already exists".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Json(ApiResponse {
|
||||
data: repo,
|
||||
|
||||
@@ -7,7 +7,10 @@ pub fn build_router() -> Router {
|
||||
Router::new()
|
||||
.route("/api/v1/health", get(handlers::health))
|
||||
.route("/api/v1/stats/overview", get(handlers::stats_overview))
|
||||
.route("/api/v1/settings/ssh-public-key", get(handlers::get_ssh_public_key))
|
||||
.route(
|
||||
"/api/v1/settings/ssh-public-key",
|
||||
get(handlers::get_ssh_public_key),
|
||||
)
|
||||
.route("/api/v1/repositories", get(handlers::list_repositories))
|
||||
.route("/api/v1/repositories", post(handlers::add_repository))
|
||||
.route(
|
||||
|
||||
@@ -47,7 +47,9 @@ pub async fn triage_findings(
|
||||
|
||||
// Enrich with surrounding code context if possible
|
||||
if let Some(context) = read_surrounding_context(finding) {
|
||||
user_prompt.push_str(&format!("\n\n--- Surrounding Code (50 lines) ---\n{context}"));
|
||||
user_prompt.push_str(&format!(
|
||||
"\n\n--- Surrounding Code (50 lines) ---\n{context}"
|
||||
));
|
||||
}
|
||||
|
||||
// Enrich with graph context if available
|
||||
@@ -98,7 +100,8 @@ pub async fn triage_findings(
|
||||
};
|
||||
if let Ok(result) = serde_json::from_str::<TriageResult>(cleaned) {
|
||||
// Apply file-path confidence adjustment
|
||||
let adjusted_confidence = adjust_confidence(result.confidence, &file_classification);
|
||||
let adjusted_confidence =
|
||||
adjust_confidence(result.confidence, &file_classification);
|
||||
finding.confidence = Some(adjusted_confidence);
|
||||
finding.triage_action = Some(result.action.clone());
|
||||
finding.triage_rationale = Some(result.rationale);
|
||||
@@ -235,7 +238,9 @@ fn adjust_confidence(raw_confidence: f64, classification: &str) -> f64 {
|
||||
raw_confidence * multiplier
|
||||
}
|
||||
|
||||
fn downgrade_severity(severity: &compliance_core::models::Severity) -> compliance_core::models::Severity {
|
||||
fn downgrade_severity(
|
||||
severity: &compliance_core::models::Severity,
|
||||
) -> compliance_core::models::Severity {
|
||||
use compliance_core::models::Severity;
|
||||
match severity {
|
||||
Severity::Critical => Severity::High,
|
||||
@@ -246,7 +251,9 @@ fn downgrade_severity(severity: &compliance_core::models::Severity) -> complianc
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade_severity(severity: &compliance_core::models::Severity) -> compliance_core::models::Severity {
|
||||
fn upgrade_severity(
|
||||
severity: &compliance_core::models::Severity,
|
||||
) -> compliance_core::models::Severity {
|
||||
use compliance_core::models::Severity;
|
||||
match severity {
|
||||
Severity::Info => Severity::Low,
|
||||
|
||||
@@ -108,10 +108,7 @@ impl CveScanner {
|
||||
.await
|
||||
.map_err(|e| CoreError::Http(format!("Failed to parse OSV.dev response: {e}")))?;
|
||||
|
||||
let chunk_vulns = result
|
||||
.results
|
||||
.into_iter()
|
||||
.map(|r| {
|
||||
let chunk_vulns = result.results.into_iter().map(|r| {
|
||||
r.vulns
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
|
||||
@@ -37,9 +37,7 @@ impl RepoCredentials {
|
||||
// HTTPS userpass authentication
|
||||
if allowed_types.contains(git2::CredentialType::USER_PASS_PLAINTEXT) {
|
||||
if let Some(ref tok) = token {
|
||||
let user = username
|
||||
.as_deref()
|
||||
.unwrap_or("x-access-token");
|
||||
let user = username.as_deref().unwrap_or("x-access-token");
|
||||
return Cred::userpass_plaintext(user, tok);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,18 @@ impl Scanner for GitleaksScanner {
|
||||
|
||||
async fn scan(&self, repo_path: &Path, repo_id: &str) -> Result<ScanOutput, CoreError> {
|
||||
let output = tokio::process::Command::new("gitleaks")
|
||||
.args(["detect", "--source", ".", "--report-format", "json", "--report-path", "/dev/stdout", "--no-banner", "--exit-code", "0"])
|
||||
.args([
|
||||
"detect",
|
||||
"--source",
|
||||
".",
|
||||
"--report-format",
|
||||
"json",
|
||||
"--report-path",
|
||||
"/dev/stdout",
|
||||
"--no-banner",
|
||||
"--exit-code",
|
||||
"0",
|
||||
])
|
||||
.current_dir(repo_path)
|
||||
.output()
|
||||
.await
|
||||
@@ -32,8 +43,8 @@ impl Scanner for GitleaksScanner {
|
||||
return Ok(ScanOutput::default());
|
||||
}
|
||||
|
||||
let results: Vec<GitleaksResult> = serde_json::from_slice(&output.stdout)
|
||||
.unwrap_or_default();
|
||||
let results: Vec<GitleaksResult> =
|
||||
serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||
|
||||
let findings = results
|
||||
.into_iter()
|
||||
@@ -41,7 +52,9 @@ impl Scanner for GitleaksScanner {
|
||||
.map(|r| {
|
||||
let severity = match r.rule_id.as_str() {
|
||||
s if s.contains("private-key") => Severity::Critical,
|
||||
s if s.contains("token") || s.contains("password") || s.contains("secret") => Severity::High,
|
||||
s if s.contains("token") || s.contains("password") || s.contains("secret") => {
|
||||
Severity::High
|
||||
}
|
||||
s if s.contains("api-key") => Severity::High,
|
||||
_ => Severity::Medium,
|
||||
};
|
||||
|
||||
@@ -60,8 +60,7 @@ fn has_rust_project(repo_path: &Path) -> bool {
|
||||
|
||||
fn has_js_project(repo_path: &Path) -> bool {
|
||||
// Only run if eslint is actually installed in the project
|
||||
repo_path.join("package.json").exists()
|
||||
&& repo_path.join("node_modules/.bin/eslint").exists()
|
||||
repo_path.join("package.json").exists() && repo_path.join("node_modules/.bin/eslint").exists()
|
||||
}
|
||||
|
||||
fn has_python_project(repo_path: &Path) -> bool {
|
||||
@@ -99,7 +98,14 @@ async fn run_with_timeout(
|
||||
|
||||
async fn run_clippy(repo_path: &Path, repo_id: &str) -> Result<Vec<Finding>, CoreError> {
|
||||
let child = Command::new("cargo")
|
||||
.args(["clippy", "--message-format=json", "--quiet", "--", "-W", "clippy::all"])
|
||||
.args([
|
||||
"clippy",
|
||||
"--message-format=json",
|
||||
"--quiet",
|
||||
"--",
|
||||
"-W",
|
||||
"clippy::all",
|
||||
])
|
||||
.current_dir(repo_path)
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
@@ -128,10 +134,7 @@ async fn run_clippy(repo_path: &Path, repo_id: &str) -> Result<Vec<Finding>, Cor
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let level = message
|
||||
.get("level")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("");
|
||||
let level = message.get("level").and_then(|v| v.as_str()).unwrap_or("");
|
||||
|
||||
if level != "warning" && level != "error" {
|
||||
continue;
|
||||
@@ -162,8 +165,13 @@ async fn run_clippy(repo_path: &Path, repo_id: &str) -> Result<Vec<Finding>, Cor
|
||||
Severity::Low
|
||||
};
|
||||
|
||||
let fingerprint =
|
||||
dedup::compute_fingerprint(&[repo_id, "clippy", &code, &file_path, &line_number.to_string()]);
|
||||
let fingerprint = dedup::compute_fingerprint(&[
|
||||
repo_id,
|
||||
"clippy",
|
||||
&code,
|
||||
&file_path,
|
||||
&line_number.to_string(),
|
||||
]);
|
||||
|
||||
let mut finding = Finding::new(
|
||||
repo_id.to_string(),
|
||||
@@ -200,10 +208,7 @@ fn extract_primary_span(message: &serde_json::Value) -> (String, u32) {
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let line = span
|
||||
.get("line_start")
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(0) as u32;
|
||||
let line = span.get("line_start").and_then(|v| v.as_u64()).unwrap_or(0) as u32;
|
||||
return (file, line);
|
||||
}
|
||||
}
|
||||
@@ -233,8 +238,7 @@ async fn run_eslint(repo_path: &Path, repo_id: &str) -> Result<Vec<Finding>, Cor
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let results: Vec<EslintFileResult> =
|
||||
serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||
let results: Vec<EslintFileResult> = serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||
|
||||
let mut findings = Vec::new();
|
||||
for file_result in results {
|
||||
@@ -308,8 +312,7 @@ async fn run_ruff(repo_path: &Path, repo_id: &str) -> Result<Vec<Finding>, CoreE
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let results: Vec<RuffResult> =
|
||||
serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||
let results: Vec<RuffResult> = serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||
|
||||
let findings = results
|
||||
.into_iter()
|
||||
|
||||
@@ -9,9 +9,8 @@ pub fn ensure_ssh_key(key_path: &str) -> Result<String, AgentError> {
|
||||
let public_path = private_path.with_extension("pub");
|
||||
|
||||
if private_path.exists() && public_path.exists() {
|
||||
return std::fs::read_to_string(&public_path).map_err(|e| {
|
||||
AgentError::Config(format!("Failed to read SSH public key: {e}"))
|
||||
});
|
||||
return std::fs::read_to_string(&public_path)
|
||||
.map_err(|e| AgentError::Config(format!("Failed to read SSH public key: {e}")));
|
||||
}
|
||||
|
||||
// Create parent directory
|
||||
@@ -36,9 +35,7 @@ pub fn ensure_ssh_key(key_path: &str) -> Result<String, AgentError> {
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(AgentError::Config(format!(
|
||||
"ssh-keygen failed: {stderr}"
|
||||
)));
|
||||
return Err(AgentError::Config(format!("ssh-keygen failed: {stderr}")));
|
||||
}
|
||||
|
||||
// Set correct permissions
|
||||
@@ -48,9 +45,8 @@ pub fn ensure_ssh_key(key_path: &str) -> Result<String, AgentError> {
|
||||
std::fs::set_permissions(private_path, std::fs::Permissions::from_mode(0o600))?;
|
||||
}
|
||||
|
||||
let public_key = std::fs::read_to_string(&public_path).map_err(|e| {
|
||||
AgentError::Config(format!("Failed to read generated SSH public key: {e}"))
|
||||
})?;
|
||||
let public_key = std::fs::read_to_string(&public_path)
|
||||
.map_err(|e| AgentError::Config(format!("Failed to read generated SSH public key: {e}")))?;
|
||||
|
||||
tracing::info!("Generated new SSH key pair at {key_path}");
|
||||
Ok(public_key)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod auth;
|
||||
pub(crate) mod serde_helpers;
|
||||
pub mod chat;
|
||||
pub mod cve;
|
||||
pub mod dast;
|
||||
@@ -11,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};
|
||||
|
||||
@@ -37,9 +37,15 @@ pub struct TrackedRepository {
|
||||
pub last_scanned_commit: Option<String>,
|
||||
#[serde(default, deserialize_with = "deserialize_findings_count")]
|
||||
pub findings_count: u32,
|
||||
#[serde(default = "chrono::Utc::now", with = "super::serde_helpers::bson_datetime")]
|
||||
#[serde(
|
||||
default = "chrono::Utc::now",
|
||||
with = "super::serde_helpers::bson_datetime"
|
||||
)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
#[serde(default = "chrono::Utc::now", with = "super::serde_helpers::bson_datetime")]
|
||||
#[serde(
|
||||
default = "chrono::Utc::now",
|
||||
with = "super::serde_helpers::bson_datetime"
|
||||
)]
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@ pub mod bson_datetime {
|
||||
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)
|
||||
}
|
||||
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:?}"
|
||||
))),
|
||||
|
||||
@@ -14,6 +14,8 @@ pub fn load_config() -> Result<DashboardConfig, DashboardError> {
|
||||
.ok()
|
||||
.and_then(|p| p.parse().ok())
|
||||
.unwrap_or(8080),
|
||||
mcp_endpoint_url: std::env::var("MCP_ENDPOINT_URL").ok().filter(|v| !v.is_empty()),
|
||||
mcp_endpoint_url: std::env::var("MCP_ENDPOINT_URL")
|
||||
.ok()
|
||||
.filter(|v| !v.is_empty()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -120,10 +120,7 @@ pub async fn bulk_update_finding_status(
|
||||
}
|
||||
|
||||
#[server]
|
||||
pub async fn update_finding_feedback(
|
||||
id: String,
|
||||
feedback: String,
|
||||
) -> Result<(), ServerFnError> {
|
||||
pub async fn update_finding_feedback(id: String, feedback: String) -> Result<(), ServerFnError> {
|
||||
let state: super::server_state::ServerState =
|
||||
dioxus_fullstack::FullstackContext::extract().await?;
|
||||
let url = format!("{}/api/v1/findings/{id}/feedback", state.agent_api_url);
|
||||
|
||||
@@ -141,10 +141,7 @@ pub async fn trigger_repo_scan(repo_id: String) -> Result<(), ServerFnError> {
|
||||
pub async fn check_repo_scanning(repo_id: String) -> Result<bool, ServerFnError> {
|
||||
let state: super::server_state::ServerState =
|
||||
dioxus_fullstack::FullstackContext::extract().await?;
|
||||
let url = format!(
|
||||
"{}/api/v1/scan-runs?page=1&limit=1",
|
||||
state.agent_api_url
|
||||
);
|
||||
let url = format!("{}/api/v1/scan-runs?page=1&limit=1", state.agent_api_url);
|
||||
|
||||
let resp = reqwest::get(&url)
|
||||
.await
|
||||
|
||||
@@ -85,27 +85,17 @@ async fn seed_default_mcp_servers(db: &Database, mcp_endpoint_url: Option<&str>)
|
||||
(
|
||||
"Findings MCP",
|
||||
"Exposes security findings, triage data, and finding summaries to LLM agents",
|
||||
vec![
|
||||
"list_findings",
|
||||
"get_finding",
|
||||
"findings_summary",
|
||||
],
|
||||
vec!["list_findings", "get_finding", "findings_summary"],
|
||||
),
|
||||
(
|
||||
"SBOM MCP",
|
||||
"Exposes software bill of materials and vulnerability reports to LLM agents",
|
||||
vec![
|
||||
"list_sbom_packages",
|
||||
"sbom_vuln_report",
|
||||
],
|
||||
vec!["list_sbom_packages", "sbom_vuln_report"],
|
||||
),
|
||||
(
|
||||
"DAST MCP",
|
||||
"Exposes DAST scan findings and scan summaries to LLM agents",
|
||||
vec![
|
||||
"list_dast_findings",
|
||||
"dast_scan_summary",
|
||||
],
|
||||
vec!["list_dast_findings", "dast_scan_summary"],
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user