From 0e5a2d7e43e35adeb901e5c8a44c2d2dd905fdd9 Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:56:39 +0200 Subject: [PATCH 1/2] feat(core): OSCAL 1.1 catalog types + mapping to controls corpus Deserialise the OSCAL catalog served by breakpilot-compliance and flatten it into the framework-agnostic traits::Control the mapping engine consumes. Pure serde (no reqwest); exposes content-hash for snapshot/drift on the consumer side. Fixture-tested against the real 40-control CRA catalog (3 lib tests). Co-Authored-By: Claude Opus 4.8 --- compliance-core/src/models/mod.rs | 2 + compliance-core/src/models/oscal.rs | 250 ++ compliance-core/tests/data/cra_catalog.json | 2765 +++++++++++++++++++ 3 files changed, 3017 insertions(+) create mode 100644 compliance-core/src/models/oscal.rs create mode 100644 compliance-core/tests/data/cra_catalog.json diff --git a/compliance-core/src/models/mod.rs b/compliance-core/src/models/mod.rs index fb9bd58..926dd78 100644 --- a/compliance-core/src/models/mod.rs +++ b/compliance-core/src/models/mod.rs @@ -10,6 +10,7 @@ pub mod mcp; pub mod mcp_token; pub mod notification; pub mod onboarding; +pub mod oscal; pub mod pentest; pub mod repository; pub mod sbom; @@ -39,6 +40,7 @@ pub use onboarding::{ GitArtifactConfig, IssueTrackerConfig, OnboardedTarget, PlcArtifactConfig, PlcFormat, TargetScanConfig, TargetType, TargetTypeCandidate, WebArtifactConfig, }; +pub use oscal::OscalDocument; pub use pentest::{ AttackChainNode, AttackNodeStatus, AuthMode, CodeContextHint, Environment, IdentityProvider, PentestAuthConfig, PentestConfig, PentestEvent, PentestMessage, PentestSession, PentestStats, diff --git a/compliance-core/src/models/oscal.rs b/compliance-core/src/models/oscal.rs new file mode 100644 index 0000000..d65e6d5 --- /dev/null +++ b/compliance-core/src/models/oscal.rs @@ -0,0 +1,250 @@ +//! OSCAL 1.1 catalog types + mapping into the controls corpus. +//! +//! Deserialises the OSCAL catalog served by breakpilot-compliance +//! (`GET /api/compliance/v1/oscal/catalog`) and maps its controls into the +//! framework-agnostic [`crate::traits::Control`] that the mapping engine consumes. +//! Only the fields we use are modelled; unknown OSCAL fields are ignored so the +//! producer can add detail without breaking us. +//! +//! Scope boundary: this is the *catalog* (domain content). Assessment objectives +//! and scanner routing live in our assessment layer, not here — see +//! [`crate::traits::ControlsProvider`]. + +use serde::Deserialize; + +use crate::models::onboarding::ComplianceFramework; +use crate::traits::Control as CorpusControl; + +/// A parsed OSCAL catalog document (`{"catalog": {...}}`). +#[derive(Debug, Clone, Deserialize)] +pub struct OscalDocument { + pub catalog: Catalog, +} + +/// An OSCAL catalog: metadata + a tree of control groups. +#[derive(Debug, Clone, Deserialize)] +pub struct Catalog { + pub uuid: String, + pub metadata: Metadata, + #[serde(default)] + pub groups: Vec, + #[serde(rename = "back-matter", default)] + pub back_matter: Option, +} + +/// Catalog metadata (title/version + provenance props). +#[derive(Debug, Clone, Deserialize)] +pub struct Metadata { + pub title: String, + pub version: String, + #[serde(rename = "oscal-version")] + pub oscal_version: String, + #[serde(default)] + pub props: Vec, +} + +/// A name/value property, optionally namespaced. +#[derive(Debug, Clone, Deserialize)] +pub struct Prop { + pub name: String, + pub value: String, + #[serde(default)] + pub ns: Option, +} + +/// A control group (may nest sub-groups and controls). +#[derive(Debug, Clone, Deserialize)] +pub struct Group { + #[serde(default)] + pub id: String, + #[serde(default)] + pub title: String, + #[serde(default)] + pub controls: Vec, + #[serde(default)] + pub groups: Vec, +} + +/// An OSCAL control (may nest enhancement controls). +#[derive(Debug, Clone, Deserialize)] +pub struct Control { + pub id: String, + #[serde(default)] + pub title: String, + #[serde(default)] + pub props: Vec, + #[serde(default)] + pub parts: Vec, + #[serde(default)] + pub links: Vec, + #[serde(default)] + pub controls: Vec, +} + +/// A control part (e.g. the `statement`), may nest sub-parts. +#[derive(Debug, Clone, Deserialize)] +pub struct Part { + #[serde(default)] + pub name: String, + #[serde(default)] + pub prose: Option, + #[serde(default)] + pub parts: Vec, +} + +/// A link, e.g. a `reference` to a back-matter resource. +#[derive(Debug, Clone, Deserialize)] +pub struct Link { + pub href: String, + #[serde(default)] + pub rel: Option, +} + +/// Back-matter holding referenced resources (e.g. the CRA measures). +#[derive(Debug, Clone, Deserialize)] +pub struct BackMatter { + #[serde(default)] + pub resources: Vec, +} + +/// A back-matter resource referenced by control links. +#[derive(Debug, Clone, Deserialize)] +pub struct Resource { + pub uuid: String, + #[serde(default)] + pub title: Option, + #[serde(default)] + pub description: Option, +} + +impl Metadata { + /// First prop value with the given name. + pub fn prop(&self, name: &str) -> Option<&str> { + self.props + .iter() + .find(|p| p.name == name) + .map(|p| p.value.as_str()) + } +} + +impl Control { + /// First prop value with the given name. + pub fn prop(&self, name: &str) -> Option<&str> { + self.props + .iter() + .find(|p| p.name == name) + .map(|p| p.value.as_str()) + } + + /// The control's `statement` prose, if present. + pub fn statement(&self) -> Option<&str> { + self.parts + .iter() + .find(|p| p.name == "statement") + .and_then(|p| p.prose.as_deref()) + } +} + +impl OscalDocument { + /// The framework this catalog declares (`metadata.props[name="framework"]`). + pub fn framework(&self) -> Option { + framework_from_str(self.catalog.metadata.prop("framework")?) + } + + /// The catalog `content-hash` prop — consumers pin this to snapshot/detect drift. + pub fn content_hash(&self) -> Option<&str> { + self.catalog.metadata.prop("content-hash") + } + + /// Flatten the catalog into the corpus controls the mapping engine consumes. + pub fn to_controls(&self) -> Vec { + let framework = self.framework().unwrap_or(ComplianceFramework::Cra); + let source_label = self.catalog.metadata.title.as_str(); + let mut out = Vec::new(); + for group in &self.catalog.groups { + collect_group(group, framework, source_label, &mut out); + } + out + } +} + +/// Map an OSCAL framework token (e.g. `"cra"`) to [`ComplianceFramework`] via its +/// serde snake_case representation. +fn framework_from_str(raw: &str) -> Option { + serde_json::from_value(serde_json::Value::String(raw.to_string())).ok() +} + +fn collect_group( + group: &Group, + framework: ComplianceFramework, + source_label: &str, + out: &mut Vec, +) { + for control in &group.controls { + collect_control(control, framework, source_label, out); + } + for sub in &group.groups { + collect_group(sub, framework, source_label, out); + } +} + +fn collect_control( + control: &Control, + framework: ComplianceFramework, + source_label: &str, + out: &mut Vec, +) { + let source = match control.prop("annex-anchor") { + Some(anchor) => Some(format!("{source_label} · {anchor}")), + None => Some(source_label.to_string()), + }; + out.push(CorpusControl { + id: control.id.clone(), + framework, + title: control.title.clone(), + text: control.statement().unwrap_or_default().to_string(), + source, + }); + for enhancement in &control.controls { + collect_control(enhancement, framework, source_label, out); + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::*; + + const CATALOG: &str = include_str!("../../tests/data/cra_catalog.json"); + + fn parse() -> OscalDocument { + serde_json::from_str(CATALOG).unwrap() + } + + #[test] + fn parses_full_catalog() { + let doc = parse(); + assert_eq!(doc.catalog.metadata.oscal_version, "1.1.2"); + assert!(!doc.catalog.groups.is_empty()); + assert!(doc.catalog.back_matter.is_some()); + } + + #[test] + fn maps_all_controls_to_corpus() { + let doc = parse(); + let controls = doc.to_controls(); + assert_eq!(controls.len(), 40); + assert_eq!(doc.framework(), Some(ComplianceFramework::Cra)); + + let c8 = controls.iter().find(|c| c.id == "cra-ai-8").unwrap(); + assert_eq!(c8.framework, ComplianceFramework::Cra); + assert!(!c8.title.is_empty()); + assert!(!c8.text.is_empty(), "statement prose should map into text"); + assert!(c8.source.as_deref().unwrap_or_default().contains("Annex I")); + } + + #[test] + fn exposes_content_hash_for_snapshotting() { + assert_eq!(parse().content_hash().map(str::len), Some(64)); + } +} diff --git a/compliance-core/tests/data/cra_catalog.json b/compliance-core/tests/data/cra_catalog.json new file mode 100644 index 0000000..41d1e57 --- /dev/null +++ b/compliance-core/tests/data/cra_catalog.json @@ -0,0 +1,2765 @@ +{ + "catalog": { + "uuid": "5bceade2-3b8b-5c7a-a959-5c2245d21ca9", + "metadata": { + "title": "EU Cyber Resilience Act — Annex I Essential Cybersecurity Requirements", + "last-modified": "2026-07-20T00:00:00+00:00", + "version": "1.0.0", + "oscal-version": "1.1.2", + "props": [ + { + "name": "framework", + "value": "cra", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "content-hash", + "value": "d093358ce572fd9868698c633be10173ad28e8c1dd4a35c2e9e4e87ecd3201c4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "control-count", + "value": "40", + "ns": "https://breakpilot.ai/ns/oscal" + } + ] + }, + "groups": [ + { + "id": "cra-secure-by-design", + "class": "cra-category", + "title": "Secure-by-Design", + "controls": [ + { + "id": "cra-ai-1", + "class": "cra-annex-i", + "title": "Secure-by-Default-Konfiguration", + "props": [ + { + "name": "label", + "value": "CRA-AI-1" + }, + { + "name": "sort-id", + "value": "cra-ai-0001" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "hybrid", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(1)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Secure-by-Design", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "5", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.9", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-1_smt", + "name": "statement", + "prose": "Produkte muessen mit sicheren Standardeinstellungen ausgeliefert werden. Keine offenen Ports, keine aktivierten Debug-Schnittstellen, keine unnoetig laufenden Dienste." + } + ], + "links": [ + { + "href": "#14abd9ee-f7d1-51d6-a82d-689bc7d5f772", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-2", + "class": "cra-annex-i", + "title": "Minimale Angriffsflaeche", + "props": [ + { + "name": "label", + "value": "CRA-AI-2" + }, + { + "name": "sort-id", + "value": "cra-ai-0002" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(2)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Secure-by-Design", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.9", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.20", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-2_smt", + "name": "statement", + "prose": "Nur notwendige Schnittstellen, Dienste und Protokolle aktivieren." + } + ], + "links": [ + { + "href": "#7e9734b8-a25b-5f55-8d20-323c4e2847b7", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-3", + "class": "cra-annex-i", + "title": "Sichere Systemarchitektur", + "props": [ + { + "name": "label", + "value": "CRA-AI-3" + }, + { + "name": "sort-id", + "value": "cra-ai-0003" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Secure-by-Design", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "10", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.27", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-3_smt", + "name": "statement", + "prose": "Sicherheitskritische Komponenten muessen isoliert werden (Sandboxing, Containerisierung, Privilege Separation)." + } + ], + "links": [ + { + "href": "#90b00c59-21c9-50fe-b07b-6a5167cf9498", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-4", + "class": "cra-annex-i", + "title": "Least-Privilege-Prinzip", + "props": [ + { + "name": "label", + "value": "CRA-AI-4" + }, + { + "name": "sort-id", + "value": "cra-ai-0004" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(d)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Secure-by-Design", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "5", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.3", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-4_smt", + "name": "statement", + "prose": "Jede Komponente, jeder Prozess und jeder Benutzer erhaelt nur die minimal notwendigen Berechtigungen." + } + ], + "links": [ + { + "href": "#d2417ab3-6e3d-5c3d-90d5-65a37b59038e", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-5", + "class": "cra-annex-i", + "title": "Manipulationsschutz", + "props": [ + { + "name": "label", + "value": "CRA-AI-5" + }, + { + "name": "sort-id", + "value": "cra-ai-0005" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(c)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Secure-by-Design", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-5_smt", + "name": "statement", + "prose": "Schutz vor unautorisierter Aenderung von Software und Konfiguration (Code Signing, Secure Boot, TPM)." + } + ], + "links": [ + { + "href": "#27c3027f-f409-589c-b71f-d9a5fc4da3f5", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-6", + "class": "cra-annex-i", + "title": "Integritaetspruefung", + "props": [ + { + "name": "label", + "value": "CRA-AI-6" + }, + { + "name": "sort-id", + "value": "cra-ai-0006" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(c)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Secure-by-Design", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-6_smt", + "name": "statement", + "prose": "Automatische Ueberpruefung der Integritaet von Software, Firmware und Konfigurationsdaten bei Start und Laufzeit." + } + ], + "links": [ + { + "href": "#10070107-67d5-5c8d-b735-62677e85a24d", + "rel": "reference" + } + ] + } + ] + }, + { + "id": "cra-authentifizierung", + "class": "cra-category", + "title": "Authentifizierung", + "controls": [ + { + "id": "cra-ai-7", + "class": "cra-annex-i", + "title": "Starke Authentifizierung", + "props": [ + { + "name": "label", + "value": "CRA-AI-7" + }, + { + "name": "sort-id", + "value": "cra-ai-0007" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(d)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Authentifizierung", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "6", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.5", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-7_smt", + "name": "statement", + "prose": "Sichere Authentifizierungsmechanismen, MFA fuer administrative Zugriffe, FIDO2/WebAuthn." + } + ], + "links": [ + { + "href": "#5bbe7860-2c3d-5ef9-9a8b-3f0cd3d785f4", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-8", + "class": "cra-annex-i", + "title": "Keine Default-Passwoerter", + "props": [ + { + "name": "label", + "value": "CRA-AI-8" + }, + { + "name": "sort-id", + "value": "cra-ai-0008" + }, + { + "name": "severity", + "value": "critical", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(d)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Authentifizierung", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.5", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-8_smt", + "name": "statement", + "prose": "Produkte duerfen keine universellen Standardpasswoerter verwenden. Aenderung bei Ersteinrichtung erzwingen." + } + ], + "links": [ + { + "href": "#d5eab715-2450-576d-bead-6d5ff8d3e7a3", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-9", + "class": "cra-annex-i", + "title": "Sicheres Credential-Management", + "props": [ + { + "name": "label", + "value": "CRA-AI-9" + }, + { + "name": "sort-id", + "value": "cra-ai-0009" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(d)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Authentifizierung", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.5", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-9_smt", + "name": "statement", + "prose": "Zugangsdaten verschluesselt speichern (bcrypt, Argon2id). Keine Klartextspeicherung. Tokens rotieren." + } + ], + "links": [ + { + "href": "#d5eab715-2450-576d-bead-6d5ff8d3e7a3", + "rel": "reference" + }, + { + "href": "#891e0a3a-f623-54f5-be4d-f315ea5570fe", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-10", + "class": "cra-annex-i", + "title": "Sitzungsmanagement", + "props": [ + { + "name": "label", + "value": "CRA-AI-10" + }, + { + "name": "sort-id", + "value": "cra-ai-0010" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(d)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Authentifizierung", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.5", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-10_smt", + "name": "statement", + "prose": "Session-Verwaltung mit Timeout, Token-Binding, Invalidierung bei Logout. CSRF-Schutz." + } + ], + "links": [ + { + "href": "#02a48325-6895-506d-b712-ac83671cc157", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-11", + "class": "cra-annex-i", + "title": "Brute-Force-Schutz", + "props": [ + { + "name": "label", + "value": "CRA-AI-11" + }, + { + "name": "sort-id", + "value": "cra-ai-0011" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(d)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Authentifizierung", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.5", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.16", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-11_smt", + "name": "statement", + "prose": "Schutz vor Brute-Force und Credential-Stuffing via Rate Limiting, Account Lockout, CAPTCHA." + } + ], + "links": [ + { + "href": "#a58e81bd-4a6a-5176-ade3-6957af0db534", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-12", + "class": "cra-annex-i", + "title": "Rollenbasierte Autorisierung", + "props": [ + { + "name": "label", + "value": "CRA-AI-12" + }, + { + "name": "sort-id", + "value": "cra-ai-0012" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(d)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Authentifizierung", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.3", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-12_smt", + "name": "statement", + "prose": "RBAC implementieren. Trennung administrativ vs Nutzer. Least-Privilege durchsetzen." + } + ], + "links": [ + { + "href": "#d2417ab3-6e3d-5c3d-90d5-65a37b59038e", + "rel": "reference" + } + ] + } + ] + }, + { + "id": "cra-kryptografie", + "class": "cra-category", + "title": "Kryptografie", + "controls": [ + { + "id": "cra-ai-13", + "class": "cra-annex-i", + "title": "Verschluesselung sensibler Daten", + "props": [ + { + "name": "label", + "value": "CRA-AI-13" + }, + { + "name": "sort-id", + "value": "cra-ai-0013" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(e)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Kryptografie", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-13_smt", + "name": "statement", + "prose": "Sensible Daten at rest (AES-256) und in transit (TLS 1.2+) verschluesseln." + } + ], + "links": [ + { + "href": "#f62d92ba-188d-5718-8df9-f101758c5ed7", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-14", + "class": "cra-annex-i", + "title": "Speicher-Schutz (Data at Rest)", + "props": [ + { + "name": "label", + "value": "CRA-AI-14" + }, + { + "name": "sort-id", + "value": "cra-ai-0014" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(e)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Kryptografie", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-14_smt", + "name": "statement", + "prose": "Verschluesselung von Festplatten, Datenbanken, Backups. Schluessel getrennt von Daten." + } + ], + "links": [ + { + "href": "#f62d92ba-188d-5718-8df9-f101758c5ed7", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-15", + "class": "cra-annex-i", + "title": "Transport-Schutz (Data in Transit)", + "props": [ + { + "name": "label", + "value": "CRA-AI-15" + }, + { + "name": "sort-id", + "value": "cra-ai-0015" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(e)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Kryptografie", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-15_smt", + "name": "statement", + "prose": "TLS 1.2+ fuer alle Netzwerkkommunikation. SSL/TLS 1.0/1.1 deaktivieren. Certificate Pinning." + } + ], + "links": [ + { + "href": "#903ec471-c64a-51e1-8d42-55ad0cfee74d", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-16", + "class": "cra-annex-i", + "title": "Sicheres Schluesselmanagement", + "props": [ + { + "name": "label", + "value": "CRA-AI-16" + }, + { + "name": "sort-id", + "value": "cra-ai-0016" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "hybrid", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(e)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Kryptografie", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-16_smt", + "name": "statement", + "prose": "Schluessel in HSM/Vault. Mind. jaehrliche Rotation. Dokumentation der Lebenszyklen." + } + ], + "links": [ + { + "href": "#891e0a3a-f623-54f5-be4d-f315ea5570fe", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-17", + "class": "cra-annex-i", + "title": "Datenminimierung", + "props": [ + { + "name": "label", + "value": "CRA-AI-17" + }, + { + "name": "sort-id", + "value": "cra-ai-0017" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(f)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Kryptografie", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.10", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.11", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-17_smt", + "name": "statement", + "prose": "Nur Daten erfassen, die fuer die Produktfunktion erforderlich sind. DSGVO-Grundsaetze beachten." + } + ], + "links": [ + { + "href": "#bd8cbb1d-4236-5919-b6b7-72a7fc93c672", + "rel": "reference" + } + ] + } + ] + }, + { + "id": "cra-ssdlc", + "class": "cra-category", + "title": "SSDLC", + "controls": [ + { + "id": "cra-ai-18", + "class": "cra-annex-i", + "title": "Strukturierter SSDLC", + "props": [ + { + "name": "label", + "value": "CRA-AI-18" + }, + { + "name": "sort-id", + "value": "cra-ai-0018" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(1)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "SSDLC", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "15", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.25", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.26", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-18_smt", + "name": "statement", + "prose": "Formaler Secure Software Development Lifecycle mit Security Gates in jeder Phase." + } + ], + "links": [ + { + "href": "#7c7074aa-d4e0-5839-b790-f85666d3cf60", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-19", + "class": "cra-annex-i", + "title": "Systematische Code Reviews", + "props": [ + { + "name": "label", + "value": "CRA-AI-19" + }, + { + "name": "sort-id", + "value": "cra-ai-0019" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(1)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "SSDLC", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "5", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.25", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-19_smt", + "name": "statement", + "prose": "Peer Reviews mit Security-Fokus fuer jeden Commit. OWASP Top 10 + CWE Top 25 Checklisten." + } + ], + "links": [ + { + "href": "#7c7074aa-d4e0-5839-b790-f85666d3cf60", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-20", + "class": "cra-annex-i", + "title": "Automatisierte Sicherheitstests", + "props": [ + { + "name": "label", + "value": "CRA-AI-20" + }, + { + "name": "sort-id", + "value": "cra-ai-0020" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(1)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "SSDLC", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.25", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-20_smt", + "name": "statement", + "prose": "SAST, DAST, SCA und Secrets Detection in der CI/CD-Pipeline." + } + ], + "links": [ + { + "href": "#6176460f-c8e2-5699-9d51-7e18a47923c6", + "rel": "reference" + } + ] + } + ] + }, + { + "id": "cra-supply-chain", + "class": "cra-category", + "title": "Supply Chain", + "controls": [ + { + "id": "cra-ai-21", + "class": "cra-annex-i", + "title": "Supply-Chain-Security", + "props": [ + { + "name": "label", + "value": "CRA-AI-21" + }, + { + "name": "sort-id", + "value": "cra-ai-0021" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(5)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Supply Chain", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "5", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.19", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.21", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-21_smt", + "name": "statement", + "prose": "Drittanbieter-Komponenten systematisch auf Schwachstellen und Lizenz-Compliance pruefen." + } + ], + "links": [ + { + "href": "#701f74a5-16be-59e7-b24f-fe0385b4654a", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-22", + "class": "cra-annex-i", + "title": "Dependency-Monitoring", + "props": [ + { + "name": "label", + "value": "CRA-AI-22" + }, + { + "name": "sort-id", + "value": "cra-ai-0022" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(5)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Supply Chain", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.25", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-22_smt", + "name": "statement", + "prose": "Kontinuierliche CVE-Ueberwachung aller Abhaengigkeiten. Automatische Benachrichtigungen." + } + ], + "links": [ + { + "href": "#d0a1e513-e068-55ac-bce8-092189b8bf80", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-23", + "class": "cra-annex-i", + "title": "Software Bill of Materials (SBOM)", + "props": [ + { + "name": "label", + "value": "CRA-AI-23" + }, + { + "name": "sort-id", + "value": "cra-ai-0023" + }, + { + "name": "severity", + "value": "critical", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(5)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Supply Chain", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.25", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-23_smt", + "name": "statement", + "prose": "Maschinenlesbares SBOM (CycloneDX oder SPDX). Top-Level-Abhaengigkeiten mit Name, Version, Lizenz. Bei jedem Release aktualisieren." + } + ], + "links": [ + { + "href": "#324d56b6-d2bd-58b4-b601-38f1e8d37ac0", + "rel": "reference" + } + ] + } + ] + }, + { + "id": "cra-logging", + "class": "cra-category", + "title": "Logging", + "controls": [ + { + "id": "cra-ai-24", + "class": "cra-annex-i", + "title": "Security-Logging", + "props": [ + { + "name": "label", + "value": "CRA-AI-24" + }, + { + "name": "sort-id", + "value": "cra-ai-0024" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(g)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Logging", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.15", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-24_smt", + "name": "statement", + "prose": "Logs aller sicherheitsrelevanten Ereignisse: Login, Berechtigungen, Admin-Aktionen, APIs, Fehler." + } + ], + "links": [ + { + "href": "#a58e81bd-4a6a-5176-ade3-6957af0db534", + "rel": "reference" + }, + { + "href": "#cea5c4a7-72a0-5a3a-9fb1-823e275df1a9", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-25", + "class": "cra-annex-i", + "title": "Ereignis-Monitoring", + "props": [ + { + "name": "label", + "value": "CRA-AI-25" + }, + { + "name": "sort-id", + "value": "cra-ai-0025" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(g)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Logging", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "10", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.16", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-25_smt", + "name": "statement", + "prose": "Zentrale Sammlung und Echtzeit-Ueberwachung. SIEM oder vergleichbares. Event-Korrelation." + } + ], + "links": [ + { + "href": "#cea5c4a7-72a0-5a3a-9fb1-823e275df1a9", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-26", + "class": "cra-annex-i", + "title": "Anomalie-Erkennung", + "props": [ + { + "name": "label", + "value": "CRA-AI-26" + }, + { + "name": "sort-id", + "value": "cra-ai-0026" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(g)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Logging", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.16", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-26_smt", + "name": "statement", + "prose": "Automatische Erkennung von Angriffsmustern. Alarmierung bei Baseline-Abweichungen. Threat Intel." + } + ], + "links": [ + { + "href": "#cea5c4a7-72a0-5a3a-9fb1-823e275df1a9", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-27", + "class": "cra-annex-i", + "title": "Log-Integritaet und -Aufbewahrung", + "props": [ + { + "name": "label", + "value": "CRA-AI-27" + }, + { + "name": "sort-id", + "value": "cra-ai-0027" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(3)(g)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Logging", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.15", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-27_smt", + "name": "statement", + "prose": "Manipulationssichere Logs (append-only, signiert oder WORM). Mind. 12 Monate Aufbewahrung." + } + ], + "links": [ + { + "href": "#9682b9df-e3b4-560c-8b1d-3c20cac75b29", + "rel": "reference" + } + ] + } + ] + }, + { + "id": "cra-updates", + "class": "cra-category", + "title": "Updates", + "controls": [ + { + "id": "cra-ai-28", + "class": "cra-annex-i", + "title": "Sichere Update-Mechanismen", + "props": [ + { + "name": "label", + "value": "CRA-AI-28" + }, + { + "name": "sort-id", + "value": "cra-ai-0028" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(4)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Updates", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.19", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-28_smt", + "name": "statement", + "prose": "Updates ueber sichere Kanaele (HTTPS, signiert). Automatische oder einfach zugaengliche Update-Moeglichkeit. Rollback-Faehigkeit." + } + ], + "links": [ + { + "href": "#27c3027f-f409-589c-b71f-d9a5fc4da3f5", + "rel": "reference" + }, + { + "href": "#10070107-67d5-5c8d-b735-62677e85a24d", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-29", + "class": "cra-annex-i", + "title": "Update-Authentizitaet", + "props": [ + { + "name": "label", + "value": "CRA-AI-29" + }, + { + "name": "sort-id", + "value": "cra-ai-0029" + }, + { + "name": "severity", + "value": "critical", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(4)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Updates", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-29_smt", + "name": "statement", + "prose": "Updates digital signiert. Signaturpruefung vor Installation. Dokumentierte Key Ceremony." + } + ], + "links": [ + { + "href": "#27c3027f-f409-589c-b71f-d9a5fc4da3f5", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-30", + "class": "cra-annex-i", + "title": "Update-Integritaet", + "props": [ + { + "name": "label", + "value": "CRA-AI-30" + }, + { + "name": "sort-id", + "value": "cra-ai-0030" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(4)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Updates", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.24", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-30_smt", + "name": "statement", + "prose": "Integritaetspruefung jedes Update-Pakets (Hash, Signatur). Manipulationen waehrend Uebertragung erkennen." + } + ], + "links": [ + { + "href": "#10070107-67d5-5c8d-b735-62677e85a24d", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-31", + "class": "cra-annex-i", + "title": "Lifecycle-Support", + "props": [ + { + "name": "label", + "value": "CRA-AI-31" + }, + { + "name": "sort-id", + "value": "cra-ai-0031" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 1(4)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Updates", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.8", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-31_smt", + "name": "statement", + "prose": "Security-Updates fuer mind. 5 Jahre ab Inverkehrbringen oder erwartete Nutzungsdauer. End-of-Life klar kommunizieren." + } + ], + "links": [ + { + "href": "#33bdbdea-1435-5758-a20a-afbb8b6dff44", + "rel": "reference" + } + ] + } + ] + }, + { + "id": "cra-vulnerability-handling", + "class": "cra-category", + "title": "Vulnerability Handling", + "controls": [ + { + "id": "cra-ai-32", + "class": "cra-annex-i", + "title": "Schwachstellen-Identifikation", + "props": [ + { + "name": "label", + "value": "CRA-AI-32" + }, + { + "name": "sort-id", + "value": "cra-ai-0032" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(1)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "4", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.8", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-32_smt", + "name": "statement", + "prose": "Kontinuierliches CVE-Monitoring aller eingesetzten Komponenten. Bug Bounty oder Responsible Disclosure." + } + ], + "links": [ + { + "href": "#d0a1e513-e068-55ac-bce8-092189b8bf80", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-33", + "class": "cra-annex-i", + "title": "SBOM-Pflege und Analyse", + "props": [ + { + "name": "label", + "value": "CRA-AI-33" + }, + { + "name": "sort-id", + "value": "cra-ai-0033" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "code", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(1)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.8", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.25", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-33_smt", + "name": "statement", + "prose": "SBOM aktuell halten und kontinuierlich gegen CVE-Datenbanken pruefen. Auto-Alarmierung bei neuen CVEs." + } + ], + "links": [ + { + "href": "#324d56b6-d2bd-58b4-b601-38f1e8d37ac0", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-34", + "class": "cra-annex-i", + "title": "Risikobasierte Priorisierung", + "props": [ + { + "name": "label", + "value": "CRA-AI-34" + }, + { + "name": "sort-id", + "value": "cra-ai-0034" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(2)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.8", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-34_smt", + "name": "statement", + "prose": "CVSS-basierte Priorisierung. SLAs: Kritisch 24-72h, Hoch 7 Tage, Mittel 30 Tage, Niedrig naechster Zyklus." + } + ], + "links": [ + { + "href": "#33bdbdea-1435-5758-a20a-afbb8b6dff44", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-35", + "class": "cra-annex-i", + "title": "Coordinated Vulnerability Disclosure", + "props": [ + { + "name": "label", + "value": "CRA-AI-35" + }, + { + "name": "sort-id", + "value": "cra-ai-0035" + }, + { + "name": "severity", + "value": "critical", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "document", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(5)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.5", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.6", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-35_smt", + "name": "statement", + "prose": "CVD-Policy mit Meldeprozess. Kontaktadresse fuer Forscher. Eingangsbestaetigung innerhalb 5 Werktagen." + } + ], + "links": [ + { + "href": "#fca99555-6c17-539e-aec8-26aa071c733b", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-36", + "class": "cra-annex-i", + "title": "Incident-Response-Prozess", + "props": [ + { + "name": "label", + "value": "CRA-AI-36" + }, + { + "name": "sort-id", + "value": "cra-ai-0036" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(5)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "10", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.24", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.25", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.26", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-36_smt", + "name": "statement", + "prose": "Dokumentierter Prozess: Detection -> Classification -> Containment -> Investigation -> Recovery -> Reporting -> Lessons Learned." + } + ], + "links": [ + { + "href": "#b9bfabf1-bf87-5512-a69f-1e6f7cca06cd", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-37", + "class": "cra-annex-i", + "title": "Fruehwarnung (24h)", + "props": [ + { + "name": "label", + "value": "CRA-AI-37" + }, + { + "name": "sort-id", + "value": "cra-ai-0037" + }, + { + "name": "severity", + "value": "critical", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(7) + Art. 14(2)(a)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.24", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.26", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-37_smt", + "name": "statement", + "prose": "Bei aktiv ausgenutzten Schwachstellen oder schweren Vorfaellen: Fruehwarnung an ENISA/CSIRT innerhalb 24 Stunden." + } + ], + "links": [ + { + "href": "#b9bfabf1-bf87-5512-a69f-1e6f7cca06cd", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-38", + "class": "cra-annex-i", + "title": "Detaillierter Vorfallsbericht (72h)", + "props": [ + { + "name": "label", + "value": "CRA-AI-38" + }, + { + "name": "sort-id", + "value": "cra-ai-0038" + }, + { + "name": "severity", + "value": "critical", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(7) + Art. 14(2)(b)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "2", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.24", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.26", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-38_smt", + "name": "statement", + "prose": "72h: Detaillierter Bericht mit Umfang, Auswirkung, Ursachenanalyse, Gegenmassnahmen. Bei personenbezogenen Daten zusaetzlich DSGVO Art. 33/34." + } + ], + "links": [ + { + "href": "#b9bfabf1-bf87-5512-a69f-1e6f7cca06cd", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-39", + "class": "cra-annex-i", + "title": "Patch-Bereitstellung", + "props": [ + { + "name": "label", + "value": "CRA-AI-39" + }, + { + "name": "sort-id", + "value": "cra-ai-0039" + }, + { + "name": "severity", + "value": "high", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "process", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(3)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "5", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.8.8", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-39_smt", + "name": "statement", + "prose": "Patches fuer gemeldete Schwachstellen so schnell wie moeglich. Security Advisories (CSAF-Format empfohlen)." + } + ], + "links": [ + { + "href": "#33bdbdea-1435-5758-a20a-afbb8b6dff44", + "rel": "reference" + } + ] + }, + { + "id": "cra-ai-40", + "class": "cra-annex-i", + "title": "Dokumentation und Nachbereitung", + "props": [ + { + "name": "label", + "value": "CRA-AI-40" + }, + { + "name": "sort-id", + "value": "cra-ai-0040" + }, + { + "name": "severity", + "value": "medium", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "evidence-type", + "value": "document", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "annex-anchor", + "value": "Annex I, 2(6)", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "category", + "value": "Vulnerability Handling", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "effort-days", + "value": "3", + "ns": "https://breakpilot.ai/ns/oscal" + }, + { + "name": "iso-27001", + "value": "A.5.27", + "ns": "https://breakpilot.ai/ns/oscal" + } + ], + "parts": [ + { + "id": "cra-ai-40_smt", + "name": "statement", + "prose": "Lueckenlose Dokumentation aller Schwachstellen + Vorfaelle, mind. 10 Jahre Aufbewahrung. Lessons-Learned-Prozess." + } + ], + "links": [ + { + "href": "#14abd9ee-f7d1-51d6-a82d-689bc7d5f772", + "rel": "reference" + }, + { + "href": "#9682b9df-e3b4-560c-8b1d-3c20cac75b29", + "rel": "reference" + } + ] + } + ] + } + ], + "back-matter": { + "resources": [ + { + "uuid": "14abd9ee-f7d1-51d6-a82d-689bc7d5f772", + "title": "M545", + "props": [ + { + "name": "label", + "value": "M545" + } + ], + "description": "Secure-by-Default-Konfiguration und Cybersecurity-Hardening-Guide beilegen" + }, + { + "uuid": "7e9734b8-a25b-5f55-8d20-323c4e2847b7", + "title": "M600", + "props": [ + { + "name": "label", + "value": "M600" + } + ], + "description": "Nicht benötigte Dienste, Ports und Protokolle standardmäßig deaktivieren" + }, + { + "uuid": "90b00c59-21c9-50fe-b07b-6a5167cf9498", + "title": "M601", + "props": [ + { + "name": "label", + "value": "M601" + } + ], + "description": "Sichere Systemarchitektur mit Netzwerkzonen und Conduits umsetzen" + }, + { + "uuid": "d2417ab3-6e3d-5c3d-90d5-65a37b59038e", + "title": "M602", + "props": [ + { + "name": "label", + "value": "M602" + } + ], + "description": "Least-Privilege und rollenbasierte Autorisierung erzwingen" + }, + { + "uuid": "27c3027f-f409-589c-b71f-d9a5fc4da3f5", + "title": "M541", + "props": [ + { + "name": "label", + "value": "M541" + } + ], + "description": "Signierte Software- und Firmware-Updates mit Rollback-Schutz" + }, + { + "uuid": "10070107-67d5-5c8d-b735-62677e85a24d", + "title": "M547", + "props": [ + { + "name": "label", + "value": "M547" + } + ], + "description": "Updates über authentisierten Kanal mit Integritätsprüfung" + }, + { + "uuid": "5bbe7860-2c3d-5ef9-9a8b-3f0cd3d785f4", + "title": "M603", + "props": [ + { + "name": "label", + "value": "M603" + } + ], + "description": "Starke Authentifizierung für privilegierte Zugänge einsetzen" + }, + { + "uuid": "d5eab715-2450-576d-bead-6d5ff8d3e7a3", + "title": "M542", + "props": [ + { + "name": "label", + "value": "M542" + } + ], + "description": "Initiale Default-Passwörter beim ersten Start erzwungen ändern" + }, + { + "uuid": "891e0a3a-f623-54f5-be4d-f315ea5570fe", + "title": "M604", + "props": [ + { + "name": "label", + "value": "M604" + } + ], + "description": "Credential- und Schlüsselmanagement etablieren" + }, + { + "uuid": "02a48325-6895-506d-b712-ac83671cc157", + "title": "M605", + "props": [ + { + "name": "label", + "value": "M605" + } + ], + "description": "Sitzungen automatisch absichern und beenden" + }, + { + "uuid": "a58e81bd-4a6a-5176-ade3-6957af0db534", + "title": "M606", + "props": [ + { + "name": "label", + "value": "M606" + } + ], + "description": "Brute-Force-Angriffe begrenzen und Anmeldungen protokollieren" + }, + { + "uuid": "f62d92ba-188d-5718-8df9-f101758c5ed7", + "title": "M607", + "props": [ + { + "name": "label", + "value": "M607" + } + ], + "description": "Vertrauliche Daten im Gerät und in Backups verschlüsseln" + }, + { + "uuid": "903ec471-c64a-51e1-8d42-55ad0cfee74d", + "title": "M608", + "props": [ + { + "name": "label", + "value": "M608" + } + ], + "description": "Externe Kommunikation kryptographisch absichern" + }, + { + "uuid": "bd8cbb1d-4236-5919-b6b7-72a7fc93c672", + "title": "M609", + "props": [ + { + "name": "label", + "value": "M609" + } + ], + "description": "Datenminimierung für Erhebung, Telemetrie und Logs umsetzen" + }, + { + "uuid": "7c7074aa-d4e0-5839-b790-f85666d3cf60", + "title": "M610", + "props": [ + { + "name": "label", + "value": "M610" + } + ], + "description": "Secure Software Development Lifecycle mit Code Reviews betreiben" + }, + { + "uuid": "6176460f-c8e2-5699-9d51-7e18a47923c6", + "title": "M548", + "props": [ + { + "name": "label", + "value": "M548" + } + ], + "description": "Sicherheitsbewertung / Penetrationstest vor Inverkehrbringen durchführen" + }, + { + "uuid": "701f74a5-16be-59e7-b24f-fe0385b4654a", + "title": "M612", + "props": [ + { + "name": "label", + "value": "M612" + } + ], + "description": "Supply-Chain-Security: Drittkomponenten vor Integration bewerten" + }, + { + "uuid": "d0a1e513-e068-55ac-bce8-092189b8bf80", + "title": "M611", + "props": [ + { + "name": "label", + "value": "M611" + } + ], + "description": "Schwachstellen identifizieren und Abhängigkeiten kontinuierlich überwachen" + }, + { + "uuid": "324d56b6-d2bd-58b4-b601-38f1e8d37ac0", + "title": "M540", + "props": [ + { + "name": "label", + "value": "M540" + } + ], + "description": "Software Bill of Materials (SBOM) erstellen und mitliefern" + }, + { + "uuid": "cea5c4a7-72a0-5a3a-9fb1-823e275df1a9", + "title": "M613", + "props": [ + { + "name": "label", + "value": "M613" + } + ], + "description": "Sicherheitsereignisse protokollieren, überwachen und auf Anomalien prüfen" + }, + { + "uuid": "9682b9df-e3b4-560c-8b1d-3c20cac75b29", + "title": "M614", + "props": [ + { + "name": "label", + "value": "M614" + } + ], + "description": "Log-Integrität, Aufbewahrung und Vorfalldokumentation sicherstellen" + }, + { + "uuid": "33bdbdea-1435-5758-a20a-afbb8b6dff44", + "title": "M544", + "props": [ + { + "name": "label", + "value": "M544" + } + ], + "description": "Patch-SLA mit Severity-Tiers dokumentieren und Lifecycle-Support festlegen" + }, + { + "uuid": "fca99555-6c17-539e-aec8-26aa071c733b", + "title": "M543", + "props": [ + { + "name": "label", + "value": "M543" + } + ], + "description": "Coordinated-Vulnerability-Disclosure-Policy veröffentlichen und betreiben" + }, + { + "uuid": "b9bfabf1-bf87-5512-a69f-1e6f7cca06cd", + "title": "M546", + "props": [ + { + "name": "label", + "value": "M546" + } + ], + "description": "Incident-Meldeprozess an ENISA / nationale CSIRT definieren" + } + ] + } + } +} \ No newline at end of file -- 2.54.0 From 51aae6665aae12f9ee23ef03232e02982ed7f005 Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:01:03 +0200 Subject: [PATCH 2/2] =?UTF-8?q?feat(agent):=20OscalControlsProvider=20?= =?UTF-8?q?=E2=80=94=20pull=20+=20snapshot=20breakpilot=20OSCAL=20catalog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements ControlsProvider by fetching GET /api/compliance/v1/oscal/catalog from breakpilot-compliance, snapshotting the exact bytes to disk, and mapping the catalog into the corpus controls the mapping engine consumes. Deterministic / offline-capable: on fetch failure it falls back to the last snapshot so scans still run (matters for on-prem/werkbank). - controls/oscal_provider.rs: HTTP fetch (bearer-optional) + atomic snapshot + offline fallback + naive context ranking; ControlsProvider impl ("breakpilot-oscal") - controls/mod.rs + lib.rs: register module - 3 lib tests: url/path building, context ranking, snapshot round-trip + fallback Not yet wired into startup — no ControlsProvider consumer exists until the assessment-plan router lands. clippy -D warnings + fmt --all --check clean. Co-Authored-By: Claude Opus 4.8 --- compliance-agent/src/controls/mod.rs | 10 + .../src/controls/oscal_provider.rs | 233 ++++++++++++++++++ compliance-agent/src/lib.rs | 1 + 3 files changed, 244 insertions(+) create mode 100644 compliance-agent/src/controls/mod.rs create mode 100644 compliance-agent/src/controls/oscal_provider.rs diff --git a/compliance-agent/src/controls/mod.rs b/compliance-agent/src/controls/mod.rs new file mode 100644 index 0000000..ce3d5da --- /dev/null +++ b/compliance-agent/src/controls/mod.rs @@ -0,0 +1,10 @@ +//! Controls corpus providers. +//! +//! Implementations of [`compliance_core::traits::ControlsProvider`] that supply +//! the control corpus the mapping engine assesses findings against. Currently: +//! [`OscalControlsProvider`], which pulls breakpilot-compliance's OSCAL catalog +//! and snapshots it locally. + +mod oscal_provider; + +pub use oscal_provider::OscalControlsProvider; diff --git a/compliance-agent/src/controls/oscal_provider.rs b/compliance-agent/src/controls/oscal_provider.rs new file mode 100644 index 0000000..0b484d3 --- /dev/null +++ b/compliance-agent/src/controls/oscal_provider.rs @@ -0,0 +1,233 @@ +//! Pull + snapshot [`ControlsProvider`] backed by breakpilot-compliance's OSCAL +//! catalog export. +//! +//! Fetches `GET {base}/api/compliance/v1/oscal/catalog?framework=`, snapshots +//! the exact bytes to disk (so scans are deterministic and keep working offline / +//! on-prem), and maps the catalog into the corpus controls the mapping engine +//! consumes. The producer owns the catalog; we own the assessment — this is the +//! ingest half of the loop. + +use std::path::PathBuf; + +use secrecy::{ExposeSecret, SecretString}; + +use compliance_core::error::CoreError; +use compliance_core::models::onboarding::ComplianceFramework; +use compliance_core::models::oscal::OscalDocument; +use compliance_core::traits::{Control, ControlQuery, ControlsProvider}; + +/// A [`ControlsProvider`] that pulls the OSCAL catalog from breakpilot-compliance +/// and snapshots it locally for deterministic / offline reuse. +pub struct OscalControlsProvider { + http: reqwest::Client, + base_url: String, + token: Option, + snapshot_dir: PathBuf, +} + +impl OscalControlsProvider { + /// Create a provider. `base_url` is the breakpilot-compliance root (e.g. + /// `http://backend-compliance:8002`); `snapshot_dir` is where catalog + /// snapshots are written so a later scan can reuse them without the network. + pub fn new( + http: reqwest::Client, + base_url: impl Into, + token: Option, + snapshot_dir: impl Into, + ) -> Self { + Self { + http, + base_url: base_url.into(), + token, + snapshot_dir: snapshot_dir.into(), + } + } + + fn catalog_url(&self, framework: ComplianceFramework) -> String { + format!( + "{}/api/compliance/v1/oscal/catalog?framework={framework}", + self.base_url.trim_end_matches('/') + ) + } + + fn snapshot_path(&self, framework: ComplianceFramework) -> PathBuf { + self.snapshot_dir + .join(format!("oscal-catalog-{framework}.json")) + } + + /// Fetch the raw catalog bytes for a framework over HTTP. + async fn fetch_raw(&self, framework: ComplianceFramework) -> Result, CoreError> { + let mut req = self.http.get(self.catalog_url(framework)); + if let Some(token) = &self.token { + req = req.bearer_auth(token.expose_secret()); + } + let resp = req + .send() + .await + .map_err(|e| CoreError::Http(e.to_string()))?; + if !resp.status().is_success() { + return Err(CoreError::Http(format!( + "catalog fetch for {framework} returned HTTP {}", + resp.status() + ))); + } + resp.bytes() + .await + .map(|b| b.to_vec()) + .map_err(|e| CoreError::Http(e.to_string())) + } + + /// Write a catalog snapshot atomically (temp file + rename). + async fn write_snapshot( + &self, + framework: ComplianceFramework, + raw: &[u8], + ) -> Result<(), CoreError> { + tokio::fs::create_dir_all(&self.snapshot_dir).await?; + let path = self.snapshot_path(framework); + let tmp = path.with_extension("json.tmp"); + tokio::fs::write(&tmp, raw).await?; + tokio::fs::rename(&tmp, &path).await?; + Ok(()) + } + + /// Read a previously written snapshot, if one exists. + async fn read_snapshot( + &self, + framework: ComplianceFramework, + ) -> Result, CoreError> { + match tokio::fs::read(self.snapshot_path(framework)).await { + Ok(raw) => Ok(Some(serde_json::from_slice(&raw)?)), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(e.into()), + } + } + + /// Load the catalog for a framework: fetch fresh + snapshot the exact bytes; + /// on network failure, fall back to the last snapshot so scans still run. + pub async fn load(&self, framework: ComplianceFramework) -> Result { + match self.fetch_raw(framework).await { + Ok(raw) => { + let doc: OscalDocument = serde_json::from_slice(&raw)?; + if let Err(e) = self.write_snapshot(framework, &raw).await { + tracing::warn!(%framework, error = %e, "failed to write OSCAL snapshot"); + } + Ok(doc) + } + Err(fetch_err) => match self.read_snapshot(framework).await? { + Some(doc) => { + tracing::warn!( + %framework, error = %fetch_err, + "OSCAL catalog fetch failed; falling back to snapshot" + ); + Ok(doc) + } + None => Err(fetch_err), + }, + } + } +} + +/// Order controls whose title/text mention the query context first (stable), then +/// truncate to the requested limit. Naive relevance — refined when the assessment +/// layer lands. +fn rank_and_truncate(mut controls: Vec, context: &str, limit: usize) -> Vec { + if !context.is_empty() { + let needle = context.to_lowercase(); + controls.sort_by_key(|c| { + let hit = + c.title.to_lowercase().contains(&needle) || c.text.to_lowercase().contains(&needle); + u8::from(!hit) + }); + } + controls.truncate(limit); + controls +} + +impl ControlsProvider for OscalControlsProvider { + fn name(&self) -> &str { + "breakpilot-oscal" + } + + async fn controls(&self, query: &ControlQuery<'_>) -> Result, CoreError> { + let mut out: Vec = Vec::new(); + for &framework in query.frameworks { + match self.load(framework).await { + Ok(doc) => out.extend(doc.to_controls()), + Err(e) => { + tracing::warn!(%framework, error = %e, "skipping framework: catalog unavailable") + } + } + } + Ok(rank_and_truncate(out, query.context, query.limit)) + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::*; + + const MINI_CATALOG: &str = r#"{"catalog":{"uuid":"u","metadata":{"title":"T", + "version":"1.0.0","oscal-version":"1.1.2","props":[{"name":"framework","value":"cra"}]}, + "groups":[{"id":"g","title":"G","controls":[{"id":"cra-ai-1","title":"MFA", + "props":[],"parts":[{"name":"statement","prose":"require mfa"}]}]}]}}"#; + + fn provider(dir: &std::path::Path) -> OscalControlsProvider { + OscalControlsProvider::new(reqwest::Client::new(), "http://unused/", None, dir) + } + + #[test] + fn builds_catalog_url_and_snapshot_path() { + let p = provider(std::path::Path::new("/snap")); + assert_eq!( + p.catalog_url(ComplianceFramework::Cra), + "http://unused/api/compliance/v1/oscal/catalog?framework=cra" + ); + assert_eq!( + p.snapshot_path(ComplianceFramework::Cra), + std::path::Path::new("/snap/oscal-catalog-cra.json") + ); + } + + #[test] + fn ranks_context_hits_first_then_truncates() { + let mk = |id: &str, title: &str| Control { + id: id.into(), + framework: ComplianceFramework::Cra, + title: title.into(), + text: String::new(), + source: None, + }; + let controls = vec![ + mk("a", "logging policy"), + mk("b", "multi-factor auth"), + mk("c", "backup"), + ]; + let ranked = rank_and_truncate(controls, "auth", 2); + assert_eq!(ranked.len(), 2); + assert_eq!(ranked[0].id, "b"); // the "auth" hit floats to the top + } + + #[tokio::test] + async fn snapshot_round_trip_and_offline_fallback() { + let dir = std::env::temp_dir().join(format!("oscal-test-{}", uuid::Uuid::new_v4())); + let p = provider(&dir); + assert!(p + .read_snapshot(ComplianceFramework::Cra) + .await + .unwrap() + .is_none()); + p.write_snapshot(ComplianceFramework::Cra, MINI_CATALOG.as_bytes()) + .await + .unwrap(); + let doc = p + .read_snapshot(ComplianceFramework::Cra) + .await + .unwrap() + .unwrap(); + assert_eq!(doc.to_controls().len(), 1); + assert_eq!(doc.framework(), Some(ComplianceFramework::Cra)); + let _ = std::fs::remove_dir_all(&dir); + } +} diff --git a/compliance-agent/src/lib.rs b/compliance-agent/src/lib.rs index a28c50f..00b0691 100644 --- a/compliance-agent/src/lib.rs +++ b/compliance-agent/src/lib.rs @@ -4,6 +4,7 @@ pub mod agent; pub mod api; pub mod classify; pub mod config; +pub mod controls; pub mod database; pub mod error; pub mod ingest; -- 2.54.0