use crate::common::TestServer; use serde_json::json; /// Insert a DAST target directly into MongoDB linked to a repo. async fn insert_dast_target(server: &TestServer, repo_id: &str, name: &str) -> String { let mongodb_uri = std::env::var("TEST_MONGODB_URI") .unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into()); let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap(); let db = client.database(&server.db_name()); let result = db .collection::("dast_targets") .insert_one(mongodb::bson::doc! { "name": name, "base_url": format!("https://{name}.example.com"), "target_type": "webapp", "repo_id": repo_id, "rate_limit": 10, "allow_destructive": false, "created_at": mongodb::bson::DateTime::now(), }) .await .unwrap(); result.inserted_id.as_object_id().unwrap().to_hex() } /// Insert a pentest session linked to a target. async fn insert_pentest_session(server: &TestServer, target_id: &str, repo_id: &str) -> String { let mongodb_uri = std::env::var("TEST_MONGODB_URI") .unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into()); let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap(); let db = client.database(&server.db_name()); let result = db .collection::("pentest_sessions") .insert_one(mongodb::bson::doc! { "target_id": target_id, "repo_id": repo_id, "strategy": "comprehensive", "status": "completed", "findings_count": 1_i32, "exploitable_count": 0_i32, "created_at": mongodb::bson::DateTime::now(), }) .await .unwrap(); result.inserted_id.as_object_id().unwrap().to_hex() } /// Insert an attack chain node linked to a session. async fn insert_attack_node(server: &TestServer, session_id: &str) { let mongodb_uri = std::env::var("TEST_MONGODB_URI") .unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into()); let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap(); let db = client.database(&server.db_name()); db.collection::("attack_chain_nodes") .insert_one(mongodb::bson::doc! { "session_id": session_id, "node_id": "node-1", "tool_name": "recon", "status": "completed", "created_at": mongodb::bson::DateTime::now(), }) .await .unwrap(); } /// Insert a DAST finding linked to a target. async fn insert_dast_finding(server: &TestServer, target_id: &str, session_id: &str) { let mongodb_uri = std::env::var("TEST_MONGODB_URI") .unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into()); let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap(); let db = client.database(&server.db_name()); db.collection::("dast_findings") .insert_one(mongodb::bson::doc! { "scan_run_id": "run-1", "target_id": target_id, "vuln_type": "xss", "title": "Reflected XSS", "description": "XSS in search param", "severity": "high", "endpoint": "https://example.com/search", "method": "GET", "exploitable": true, "evidence": [], "session_id": session_id, "created_at": mongodb::bson::DateTime::now(), }) .await .unwrap(); } /// Helper to count documents in a collection async fn count_docs(server: &TestServer, collection: &str) -> u64 { let mongodb_uri = std::env::var("TEST_MONGODB_URI") .unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into()); let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap(); let db = client.database(&server.db_name()); db.collection::(collection) .count_documents(mongodb::bson::doc! {}) .await .unwrap() } #[tokio::test] async fn delete_repo_cascades_to_dast_and_pentest_data() { let server = TestServer::start().await; // Create a repo let resp = server .post( "/api/v1/repositories", &json!({ "name": "cascade-test", "git_url": "https://github.com/example/cascade-test.git", }), ) .await; let body: serde_json::Value = resp.json().await.unwrap(); let repo_id = body["data"]["id"].as_str().unwrap().to_string(); // Insert DAST target linked to repo let target_id = insert_dast_target(&server, &repo_id, "cascade-target").await; // Insert pentest session linked to target let session_id = insert_pentest_session(&server, &target_id, &repo_id).await; // Insert downstream data insert_attack_node(&server, &session_id).await; insert_dast_finding(&server, &target_id, &session_id).await; // Verify data exists assert_eq!(count_docs(&server, "dast_targets").await, 1); assert_eq!(count_docs(&server, "pentest_sessions").await, 1); assert_eq!(count_docs(&server, "attack_chain_nodes").await, 1); assert_eq!(count_docs(&server, "dast_findings").await, 1); // Delete the repo let resp = server .delete(&format!("/api/v1/repositories/{repo_id}")) .await; assert_eq!(resp.status(), 200); // All downstream data should be gone assert_eq!(count_docs(&server, "dast_targets").await, 0); assert_eq!(count_docs(&server, "pentest_sessions").await, 0); assert_eq!(count_docs(&server, "attack_chain_nodes").await, 0); assert_eq!(count_docs(&server, "dast_findings").await, 0); server.cleanup().await; } #[tokio::test] async fn delete_repo_cascades_sast_findings_and_sbom() { let server = TestServer::start().await; // Create a repo let resp = server .post( "/api/v1/repositories", &json!({ "name": "sast-cascade", "git_url": "https://github.com/example/sast-cascade.git", }), ) .await; let body: serde_json::Value = resp.json().await.unwrap(); let repo_id = body["data"]["id"].as_str().unwrap().to_string(); // Insert SAST finding and SBOM entry let mongodb_uri = std::env::var("TEST_MONGODB_URI") .unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into()); let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap(); let db = client.database(&server.db_name()); let now = mongodb::bson::DateTime::now(); db.collection::("findings") .insert_one(mongodb::bson::doc! { "repo_id": &repo_id, "fingerprint": "fp-test-1", "scanner": "semgrep", "scan_type": "sast", "title": "SQL Injection", "description": "desc", "severity": "critical", "status": "open", "created_at": now, "updated_at": now, }) .await .unwrap(); db.collection::("sbom_entries") .insert_one(mongodb::bson::doc! { "repo_id": &repo_id, "name": "lodash", "version": "4.17.20", "package_manager": "npm", "known_vulnerabilities": [], }) .await .unwrap(); assert_eq!(count_docs(&server, "findings").await, 1); assert_eq!(count_docs(&server, "sbom_entries").await, 1); // Delete repo server .delete(&format!("/api/v1/repositories/{repo_id}")) .await; // Both should be gone assert_eq!(count_docs(&server, "findings").await, 0); assert_eq!(count_docs(&server, "sbom_entries").await, 0); server.cleanup().await; }