//! Integration tests for conflict detection (Phase 2A). use crate::*; use ed25519_dalek::SigningKey; use stemedb_core::types::{Assertion, ObjectValue, SourceClass}; /// Create a minimal test corpus for integration tests. #[allow(clippy::vec_init_then_push)] #[allow(dead_code)] fn create_test_corpus(signing_key: &SigningKey) -> Vec { let timestamp = crate::episteme::current_timestamp(); let mut assertions = Vec::new(); // TLS verification (RFC 5246 + OWASP) assertions.push(crate::episteme::create_authoritative_assertion( signing_key, "rfc://5246/tls/cert_verification", "enabled", ObjectValue::Boolean(true), SourceClass::Regulatory, "TLS certificate verification MUST be enabled (RFC 5246)", timestamp, )); assertions.push(crate::episteme::create_authoritative_assertion( signing_key, "owasp://transport_layer/tls/cert_verification", "enabled", ObjectValue::Boolean(true), SourceClass::Clinical, "OWASP: Always verify TLS certificates", timestamp, )); // JWT validation (RFC 7519) assertions.push(crate::episteme::create_authoritative_assertion( signing_key, "rfc://7519/jwt/audience_validation", "enabled", ObjectValue::Boolean(true), SourceClass::Regulatory, "JWT audience claim MUST be validated (RFC 7519 Section 4.1.3)", timestamp, )); assertions.push(crate::episteme::create_authoritative_assertion( signing_key, "rfc://7519/jwt/expiry_validation", "enabled", ObjectValue::Boolean(true), SourceClass::Regulatory, "JWT expiry claim MUST be validated (RFC 7519 Section 4.1.4)", timestamp, )); // CORS security (OWASP) assertions.push(crate::episteme::create_authoritative_assertion( signing_key, "owasp://cors/allow_origin", "config_value", ObjectValue::Text("explicit_list".to_string()), SourceClass::Clinical, "OWASP: Never use wildcard (*) for CORS Allow-Origin in production", timestamp, )); // Secrets management (OWASP) assertions.push(crate::episteme::create_authoritative_assertion( signing_key, "owasp://secrets/api_key", "storage_method", ObjectValue::Text("environment_or_vault".to_string()), SourceClass::Clinical, "OWASP: Never hardcode API keys in source code", timestamp, )); assertions } #[ignore = "Needs corpus refactor after hardcoded deletion"] #[tokio::test] async fn test_conflict_detection_tls_disabled() { // Create temp project with danger_accept_invalid_certs(true) let temp_dir = tempfile::Builder::new().prefix("aphoria_tls_conflict").tempdir().expect("create temp dir"); let src_dir = temp_dir.path().join("src"); std::fs::create_dir_all(&src_dir).expect("create src dir"); // Write a Rust file with TLS verification disabled std::fs::write( src_dir.join("client.rs"), r#" fn create_client() -> Result { let client = reqwest::Client::builder() .danger_accept_invalid_certs(true) .build()?; Ok(client) } "#, ) .expect("write file"); // Create Cargo.toml so it's detected as a Rust project std::fs::write( temp_dir.path().join("Cargo.toml"), r#" [package] name = "testproject" version = "0.1.0" "#, ) .expect("write cargo.toml"); let args = ScanArgs { path: temp_dir.path().to_path_buf(), format: "table".to_string(), exit_code_enabled: true, mode: ScanMode::Ephemeral, debug: false, sync: false, file_source: FileSource::All, benchmark: false, show_claims: false, strict: false, show_observations: false, explain_authority: false, suggest_convergence: false, }; let mut config = AphoriaConfig::default(); config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db"); let result = run_scan(args, &config).await.expect("scan should succeed"); // Assert: conflicts not empty, has_blocks() == true assert!( !result.conflicts.is_empty(), "Should detect conflicts for TLS verification disabled. \ Claims extracted: {}, Files scanned: {}", result.claims_extracted, result.files_scanned ); assert!( result.has_blocks(), "TLS verification disabled should be a BLOCK verdict. \ Conflicts: {:?}", result.conflicts.iter().map(|c| (&c.claim.concept_path, &c.verdict)).collect::>() ); } #[ignore = "Needs corpus refactor after hardcoded deletion"] #[tokio::test] async fn test_conflict_detection_jwt_audience_disabled() { // Create temp project with JWT audience validation disabled let temp_dir = tempfile::Builder::new().prefix("aphoria_jwt_conflict").tempdir().expect("create temp dir"); let src_dir = temp_dir.path().join("src"); std::fs::create_dir_all(&src_dir).expect("create src dir"); // Write a Rust file with JWT audience validation disabled std::fs::write( src_dir.join("auth.rs"), r#" fn validate_token(token: &str) -> Result { let mut validation = Validation::default(); validation.validate_aud = false; // Disabled! let token_data = decode::(token, &key, &validation)?; Ok(token_data.claims) } "#, ) .expect("write file"); // Create Cargo.toml std::fs::write( temp_dir.path().join("Cargo.toml"), r#" [package] name = "testproject" version = "0.1.0" "#, ) .expect("write cargo.toml"); let args = ScanArgs { path: temp_dir.path().to_path_buf(), format: "table".to_string(), exit_code_enabled: true, mode: ScanMode::Ephemeral, debug: false, sync: false, file_source: FileSource::All, benchmark: false, show_claims: false, strict: false, show_observations: false, explain_authority: false, suggest_convergence: false, }; let mut config = AphoriaConfig::default(); config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db"); let result = run_scan(args, &config).await.expect("scan should succeed"); // Assert: conflicts not empty for JWT audience validation assert!( !result.conflicts.is_empty(), "Should detect conflicts for JWT audience validation disabled. \ Claims extracted: {}, Files scanned: {}", result.claims_extracted, result.files_scanned ); // Check that at least one conflict is about JWT audience let has_jwt_conflict = result .conflicts .iter() .any(|c| c.claim.concept_path.contains("jwt") && c.claim.concept_path.contains("audience")); assert!( has_jwt_conflict, "Should have a conflict about JWT audience validation. \ Conflicts: {:?}", result.conflicts.iter().map(|c| &c.claim.concept_path).collect::>() ); } #[ignore = "Needs corpus refactor after hardcoded deletion"] #[tokio::test] async fn test_no_conflicts_when_compliant() { // Create temp project with compliant code (no dangerous patterns) let temp_dir = tempfile::Builder::new().prefix("aphoria_compliant").tempdir().expect("create temp dir"); let src_dir = temp_dir.path().join("src"); std::fs::create_dir_all(&src_dir).expect("create src dir"); // Write a Rust file with compliant code std::fs::write( src_dir.join("main.rs"), r#" fn main() { println!("Hello, world!"); } "#, ) .expect("write file"); // Create Cargo.toml std::fs::write( temp_dir.path().join("Cargo.toml"), r#" [package] name = "testproject" version = "0.1.0" "#, ) .expect("write cargo.toml"); let args = ScanArgs { path: temp_dir.path().to_path_buf(), format: "table".to_string(), exit_code_enabled: true, mode: ScanMode::Ephemeral, debug: false, sync: false, file_source: FileSource::All, benchmark: false, show_claims: false, strict: false, show_observations: false, explain_authority: false, suggest_convergence: false, }; let mut config = AphoriaConfig::default(); config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db"); let result = run_scan(args, &config).await.expect("scan should succeed"); // No dangerous patterns = no claims = no conflicts assert!( result.conflicts.is_empty(), "Compliant code should have no conflicts. Found: {:?}", result.conflicts.iter().map(|c| &c.claim.concept_path).collect::>() ); }