feat(controls): B2 — grounded surface checks for absence-based CRA controls (#219)
This commit was merged in pull request #219.
This commit is contained in:
@@ -16,9 +16,10 @@ use compliance_core::models::onboarding::ComplianceFramework;
|
||||
use compliance_core::AgentConfig;
|
||||
use control_map::ControlMap;
|
||||
|
||||
use super::surface;
|
||||
use super::{
|
||||
ControlIndex, ControlTriage, LlmControlJudge, OscalControlsProvider, SemanticControlChecker,
|
||||
TriageOutcome,
|
||||
ControlIndex, ControlTriage, GroundedControlChecker, LlmControlJudge, OscalControlsProvider,
|
||||
SemanticControlChecker, TriageOutcome,
|
||||
};
|
||||
use crate::llm::LlmClient;
|
||||
|
||||
@@ -105,6 +106,50 @@ async fn build_specs(provider: &OscalControlsProvider) -> HashMap<String, Contro
|
||||
specs
|
||||
}
|
||||
|
||||
/// Absence-based control pass (the grounded half of the hybrid coverage): for each
|
||||
/// control with a [`surface`] definition, deterministically retrieve the code
|
||||
/// surfaces it governs (login routes, logging setup, update/download code) and have
|
||||
/// the grounded judge decide whether the control holds there. Returns net-new
|
||||
/// findings, each already tagged with its control and grounded to a real snippet.
|
||||
///
|
||||
/// Gated: the orchestrator runs this only when `breakpilot.grounded_control_checks`
|
||||
/// is set. Absence detection is the least deterministic path (the judge decides
|
||||
/// presence/absence, not a syntactic pattern), so it stays off until tuned live.
|
||||
pub async fn grounded_surface_findings(
|
||||
config: &AgentConfig,
|
||||
llm: Arc<LlmClient>,
|
||||
repo_path: &Path,
|
||||
repo_id: &str,
|
||||
) -> Vec<Finding> {
|
||||
let Some(base_url) = config.breakpilot.base_url.clone() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let provider = OscalControlsProvider::new(
|
||||
reqwest::Client::new(),
|
||||
base_url,
|
||||
config.breakpilot.token.clone(),
|
||||
&config.breakpilot.snapshot_dir,
|
||||
);
|
||||
let specs = build_specs(&provider).await;
|
||||
if specs.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let checker = GroundedControlChecker::new(LlmControlJudge::new(llm));
|
||||
|
||||
let mut out = Vec::new();
|
||||
for surf in surface::SURFACES {
|
||||
let Some(spec) = specs.get(surf.control_id) else {
|
||||
continue; // catalog doesn't carry this control
|
||||
};
|
||||
let regions = surface::retrieve(repo_path, surf.terms);
|
||||
if regions.is_empty() {
|
||||
continue;
|
||||
}
|
||||
out.extend(checker.check(spec, ®ions, repo_id).await);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Read a window of lines around `line` (1-based) from `repo_path/file`.
|
||||
fn fetch_region(repo_path: &Path, file: &str, line: u32) -> Option<CandidateRegion> {
|
||||
let content = std::fs::read_to_string(repo_path.join(file)).ok()?;
|
||||
|
||||
Reference in New Issue
Block a user