feat(oscal): emit unmapped findings as-is + MCP oscal_assessment tool (#214)
This commit was merged in pull request #214.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
//! OSCAL assessment endpoint.
|
||||
//!
|
||||
//! Assesses a target's findings against the breakpilot-compliance control
|
||||
//! catalog and returns a standard OSCAL assessment-results document. Ties
|
||||
//! together the ingest provider ([`OscalControlsProvider`]) and the assessment
|
||||
//! emitter (`compliance_core::models::oscal_assessment`).
|
||||
//! Returns a standard OSCAL assessment-results document for a target's findings,
|
||||
//! driven by each finding's stamped `control_refs` (from the scan's control-triage
|
||||
//! stage): mapped findings target their controls, unmapped findings are reported
|
||||
//! as-is. See `compliance_core::models::oscal_assessment`.
|
||||
|
||||
use axum::extract::Extension;
|
||||
use axum::http::StatusCode;
|
||||
@@ -12,45 +12,24 @@ use axum::Json;
|
||||
use mongodb::bson::doc;
|
||||
use serde::Deserialize;
|
||||
|
||||
use compliance_core::models::onboarding::ComplianceFramework;
|
||||
use compliance_core::models::oscal_assessment::{assess, ControlLinker};
|
||||
use compliance_core::models::oscal_assessment::assess;
|
||||
use compliance_core::models::Finding;
|
||||
use compliance_core::tenant_ctx::TenantCtx;
|
||||
|
||||
use super::dto::{collect_cursor_async, tenant_db, AgentExt};
|
||||
use crate::controls::OscalControlsProvider;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct AssessRequest {
|
||||
/// The target / repo id whose findings are assessed.
|
||||
pub target_id: String,
|
||||
/// Frameworks to assess against; defaults to `[Cra]` when empty.
|
||||
#[serde(default)]
|
||||
pub frameworks: Vec<ComplianceFramework>,
|
||||
}
|
||||
|
||||
/// `POST /api/v1/oscal/assess` — pull the catalog(s), load the target's findings,
|
||||
/// and emit an OSCAL assessment-results document linking findings to controls.
|
||||
/// `POST /api/v1/oscal/assess` — OSCAL assessment-results for a target's findings.
|
||||
pub async fn assess_target(
|
||||
Extension(agent): AgentExt,
|
||||
tenant: TenantCtx,
|
||||
Json(req): Json<AssessRequest>,
|
||||
) -> Response {
|
||||
let cfg = &agent.config.breakpilot;
|
||||
let Some(base_url) = cfg.base_url.clone() else {
|
||||
return (
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
"breakpilot base URL not configured (set BREAKPILOT_BASE_URL)",
|
||||
)
|
||||
.into_response();
|
||||
};
|
||||
|
||||
let frameworks = if req.frameworks.is_empty() {
|
||||
vec![ComplianceFramework::Cra]
|
||||
} else {
|
||||
req.frameworks.clone()
|
||||
};
|
||||
|
||||
let db = match tenant_db(&agent, &tenant).await {
|
||||
Ok(db) => db,
|
||||
Err(code) => return code.into_response(),
|
||||
@@ -65,25 +44,5 @@ pub async fn assess_target(
|
||||
}
|
||||
};
|
||||
|
||||
let provider = OscalControlsProvider::new(
|
||||
agent.http.clone(),
|
||||
base_url,
|
||||
cfg.token.clone(),
|
||||
&cfg.snapshot_dir,
|
||||
);
|
||||
let mut controls = Vec::new();
|
||||
for framework in &frameworks {
|
||||
match provider.load(*framework).await {
|
||||
Ok(document) => controls.extend(document.to_controls()),
|
||||
Err(e) => tracing::warn!(?framework, error = %e, "OSCAL catalog load failed"),
|
||||
}
|
||||
}
|
||||
|
||||
let assessment = assess(
|
||||
&controls,
|
||||
&findings,
|
||||
&ControlLinker::cra_seed(),
|
||||
chrono::Utc::now(),
|
||||
);
|
||||
Json(assessment).into_response()
|
||||
Json(assess(&findings, chrono::Utc::now())).into_response()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user