//! C5 live verification — the semantic master-controls path end to end against the //! deployed api-dev catalog. Ignored (hits api-dev + LiteLLM). Run explicitly: //! //! set -a; . ./.env; set +a //! BREAKPILOT_BASE_URL=https://api-dev.breakpilot.ai \ //! cargo test -p compliance-agent --test c5_semantic_live -- --ignored --nocapture //! //! Pulls the live master-controls catalog, embeds the corpus (chunked), then for a //! couple of real vulnerable findings retrieves the nearest master controls and //! grounded-judges them, stamping master-control refs. mod common; use std::sync::Arc; use compliance_agent::llm::LlmClient; use compliance_core::config::BreakpilotConfig; use compliance_core::models::finding::{Finding, Severity}; use compliance_core::models::scan::ScanType; use secrecy::SecretString; fn env(k: &str) -> String { std::env::var(k).unwrap_or_else(|_| panic!("env {k} must be set for the live C5 test")) } fn mk_finding(file: &str, line: u32, title: &str) -> Finding { let mut f = Finding::new( "repo-c5".into(), format!("{file}:{line}"), "semgrep".into(), ScanType::Sast, title.into(), title.into(), Severity::High, ); f.file_path = Some(file.into()); f.line_number = Some(line); f } #[tokio::test] #[ignore = "live: requires deployed api-dev master-controls (fetch+parse only, no LLM)"] async fn c5_ingest_master_controls_catalog() { use compliance_agent::controls::OscalControlsProvider; let provider = OscalControlsProvider::new( reqwest::Client::new(), env("BREAKPILOT_BASE_URL"), None, std::env::temp_dir().join("c5-ingest-snap"), ); let doc = provider .load_master_controls() .await .expect("pull + parse master-controls catalog"); let controls = doc.to_controls(); println!( "\n=== C5 ingest: {} master controls parsed ===", controls.len() ); for c in controls.iter().take(4) { let text: String = c.text.chars().take(90).collect(); println!(" {} | {} | {}", c.id, c.title, text); } assert!( !controls.is_empty(), "expected a non-empty master-control corpus" ); } #[tokio::test] #[ignore = "live: requires deployed api-dev master-controls + LiteLLM"] async fn c5_semantic_stamps_master_control_refs() { let llm = Arc::new(LlmClient::new( env("LITELLM_URL"), SecretString::from(env("LITELLM_API_KEY")), env("LITELLM_MODEL"), env("LITELLM_EMBED_MODEL"), )); let mut config = common::dev_config("mongodb://unused".into(), "c5".into()); let snapshot = std::env::temp_dir().join("c5-oscal-snap"); config.breakpilot = BreakpilotConfig { base_url: Some(env("BREAKPILOT_BASE_URL")), token: None, snapshot_dir: snapshot.to_string_lossy().into_owned(), semantic_mapping: true, grounded_control_checks: false, }; // Fixture repo with recognizable code-checkable surfaces. let repo = std::env::temp_dir().join("c5-fixture-repo"); let _ = std::fs::remove_dir_all(&repo); std::fs::create_dir_all(repo.join("app")).expect("mkdir"); std::fs::write( repo.join("app/auth.py"), concat!( "import hashlib\n", "\n", "def store_password(user, password):\n", " # weak, unsalted password hashing\n", " digest = hashlib.md5(password.encode()).hexdigest()\n", " db.save(user, digest)\n", "\n", "@app.route('/login', methods=['POST'])\n", "def login():\n", " u = request.form['username']\n", " p = request.form['password']\n", " return 'ok' if check(u, p) else ('bad', 401)\n", ), ) .expect("write fixture"); let mut findings = vec![ mk_finding("app/auth.py", 5, "Weak password hash (md5, unsalted)"), mk_finding( "app/auth.py", 9, "Login endpoint without brute-force protection", ), ]; let tagged = compliance_agent::controls::semantic_stamp_findings(&config, llm, &repo, &mut findings) .await; println!("\n=== C5 semantic master-controls stamping ==="); for f in &findings { println!( " {:50} {}:{:?} -> {:?}", f.title, f.file_path.as_deref().unwrap_or(""), f.line_number, f.control_refs ); } println!("findings that gained >=1 master-control ref: {tagged}"); let _ = std::fs::remove_dir_all(&repo); // Live corpus — assert only that the path runs and stamps at least one ref. assert!( tagged >= 1, "expected at least one finding to gain a master-control ref" ); }