slice 8: security scan runner + trigger endpoint
Runs the security template's tool set (cargo-audit / gitleaks /
trivy fs / semgrep) inside the mission's team container and
materializes each finding as a mission_task keyed on the tool's
canonical id. The subsequent coding phase picks up the tasks and
applies remediations; the committer closes them by emitting
`COMPLETED: <external_id>` (Slice 5's task-card parser handles it).
Rust surface:
- cm_api::security_scan::run(mission_id, phase_id)
- Per-tool runners with JSON output parsing:
cargo audit --json → vulnerabilities[].advisory.id
gitleaks detect --report-format=json → [{ Fingerprint, RuleID }]
trivy fs --format=json → Results[].Vulnerabilities[].VulnerabilityID
semgrep --config=auto --json → results[] w/ rule+path+line fingerprint
- Tool errors surface as a `warning` task instead of failing the
scan — operator sees which need installing/fixing without a
silent no-op.
- Findings map to mission_tasks with external_id = "<tool>:<id>"
(e.g. cargo_audit:RUSTSEC-2024-0001, gitleaks:<sha>,
trivy_fs:CVE-2024-1234, semgrep:<rule>@<file>:<line>).
API:
- POST /api/missions/{id}/security-scan { phase_id }
→ { findings, tasks[] } — full task list after upsert so the
canvas can render immediately.
Frontend:
- triggerSecurityScan helper in lib/api/missions.ts. Findings
show up in the existing Tasks tab (Slice 5's UPSERT path).
Container requirements (opt-in):
- Runs inside the team container via `docker exec`, so the tools
must be present in that image. Missing = warning task, not fail.
- `-w /workspace/repo` so scanners see the mounted repo. Reads
teams.zeroclaw_container (populated on first phase run).
Follow-ups:
- MCP bundle wrapping the same tools as agent-callable functions
(currently agents scan by shelling out to `cargo audit` etc.
directly; a typed MCP wrap lands with clean audit trail).
- Auto-fire from the security_hardening workflow template on
phase transition (currently manual via API trigger).
- Bundle the four tools into the runtime image (or a dedicated
security-tools image) so operators don't have to install them.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
f843c9ddb1
commit
58963d5083
@@ -85,6 +85,17 @@ pub struct BenchmarkTriggerRequest {
|
||||
pub iteration: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SecurityScanRequest {
|
||||
pub phase_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SecurityScanResponse {
|
||||
pub findings: usize,
|
||||
pub tasks: Vec<MissionTask>,
|
||||
}
|
||||
|
||||
// ── Handlers ─────────────────────────────────────────────────────
|
||||
|
||||
pub async fn list(
|
||||
@@ -187,6 +198,29 @@ pub async fn trigger_benchmark(
|
||||
Ok(Json(snaps))
|
||||
}
|
||||
|
||||
/// POST /api/missions/{id}/security-scan — run the security phase's
|
||||
/// tool set (cargo-audit / gitleaks / trivy fs / semgrep) inside
|
||||
/// the mission's team container and materialize each finding as a
|
||||
/// mission_task keyed on the tool's canonical id.
|
||||
pub async fn trigger_security_scan(
|
||||
State(state): State<AppState>,
|
||||
Authed(user): Authed,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(body): Json<SecurityScanRequest>,
|
||||
) -> Result<Json<SecurityScanResponse>, ApiError> {
|
||||
let _ = cm_db::repo::missions::get(&state.pool, id, user.workspace_id.as_uuid())
|
||||
.await?
|
||||
.ok_or(ApiError::NotFound)?;
|
||||
let findings = crate::security_scan::run(&state.pool, id, body.phase_id)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("security_scan for mission {id}: {e}");
|
||||
ApiError::Internal
|
||||
})?;
|
||||
let tasks = cm_db::repo::missions::tasks_for(&state.pool, id).await?;
|
||||
Ok(Json(SecurityScanResponse { findings, tasks }))
|
||||
}
|
||||
|
||||
pub async fn set_status(
|
||||
State(state): State<AppState>,
|
||||
Authed(user): Authed,
|
||||
|
||||
Reference in New Issue
Block a user