52 lines
1.6 KiB
Rust
52 lines
1.6 KiB
Rust
//! OSCAL assessment MCP tool.
|
|
//!
|
|
//! Emits a standard OSCAL assessment-results document for a repo's findings —
|
|
//! what breakpilot's scanner MCP client pulls. Mapped findings target their
|
|
//! compliance controls (via the stamped `control_refs`); unmapped findings are
|
|
//! reported as-is, so nothing is lost.
|
|
|
|
use mongodb::bson::doc;
|
|
use rmcp::{model::*, ErrorData as McpError};
|
|
use schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
|
|
use compliance_core::models::oscal_assessment::assess;
|
|
use compliance_core::models::Finding;
|
|
|
|
use crate::database::Database;
|
|
|
|
#[derive(Debug, Deserialize, JsonSchema)]
|
|
pub struct OscalAssessmentParams {
|
|
/// Repository / target id to assess.
|
|
pub repo_id: String,
|
|
}
|
|
|
|
pub async fn oscal_assessment(
|
|
db: &Database,
|
|
params: OscalAssessmentParams,
|
|
) -> Result<CallToolResult, McpError> {
|
|
let mut cursor = db
|
|
.findings()
|
|
.find(doc! { "repo_id": ¶ms.repo_id })
|
|
.await
|
|
.map_err(|e| McpError::internal_error(format!("DB error: {e}"), None))?;
|
|
|
|
let mut findings: Vec<Finding> = Vec::new();
|
|
while cursor
|
|
.advance()
|
|
.await
|
|
.map_err(|e| McpError::internal_error(format!("cursor error: {e}"), None))?
|
|
{
|
|
findings.push(
|
|
cursor
|
|
.deserialize_current()
|
|
.map_err(|e| McpError::internal_error(format!("deserialize error: {e}"), None))?,
|
|
);
|
|
}
|
|
|
|
let document = assess(&findings, chrono::Utc::now());
|
|
let json = serde_json::to_string_pretty(&document)
|
|
.map_err(|e| McpError::internal_error(format!("json error: {e}"), None))?;
|
|
Ok(CallToolResult::success(vec![Content::text(json)]))
|
|
}
|