use sanctum::crypto::{ derive_kek, generate_dek, generate_salt, wrap_dek, KdfParams, MIN_MEMORY_COST_KIB, MIN_TIME_COST, }; use sanctum::mount::is_loopback_host; use sanctum::storage::Database; use std::path::PathBuf; #[test] fn test_is_loopback_host_comprehensive() { // Gültige IPv4 Loopback Varianten assert!(is_loopback_host("127.0.0.1")); assert!(is_loopback_host("127.0.0.1:80")); assert!(is_loopback_host("127.0.0.1:8080")); assert!(is_loopback_host("127.0.0.1:65535")); // Gültige Hostname Loopback Varianten assert!(is_loopback_host("localhost")); assert!(is_loopback_host("localhost:80")); assert!(is_loopback_host("localhost:8443")); assert!(is_loopback_host("LOCALHOST")); assert!(is_loopback_host("LocalHost:9000")); // Gültige IPv6 Loopback Varianten assert!(is_loopback_host("[::1]")); assert!(is_loopback_host("[::1]:80")); assert!(is_loopback_host("[::1]:8443")); assert!(is_loopback_host("::1")); // DNS-Rebinding & Spoofing Angriffe (MÜSSEN abgewiesen werden) assert!(!is_loopback_host("127.0.0.1.attacker.com")); assert!(!is_loopback_host("localhost.attacker.com")); assert!(!is_loopback_host("localhost.evil.org:8080")); assert!(!is_loopback_host("notlocalhost")); assert!(!is_loopback_host("attacker.com")); assert!(!is_loopback_host("attacker.com:127001")); assert!(!is_loopback_host("192.168.1.1")); assert!(!is_loopback_host("10.0.0.1")); assert!(!is_loopback_host("0.0.0.0")); assert!(!is_loopback_host("")); assert!(!is_loopback_host(" ")); assert!(!is_loopback_host("foo:bar:baz")); } #[test] fn test_wal_and_shm_cleanup_on_close() { let temp_dir = std::env::temp_dir(); let container_path: PathBuf = temp_dir.join(format!("test_wal_cleanup_{}.sanctum", std::process::id())); if container_path.exists() { let _ = std::fs::remove_file(&container_path); } let salt = generate_salt(); let kdf_params = KdfParams { memory_cost: MIN_MEMORY_COST_KIB, time_cost: MIN_TIME_COST, parallelism: 1, }; let kek = derive_kek("WalCleanupPassword2026!", &salt, &kdf_params).unwrap(); let dek = generate_dek(); let (wrapped_dek, nonce, tag) = wrap_dek(&kek, &dek).unwrap(); let db = Database::open(&container_path).unwrap(); db.init_schema(&salt, &kdf_params, &wrapped_dek, &nonce, &tag) .unwrap(); // Erstelle Knoten, um Schreibaktivität im WAL zu erzeugen let _ = db.create_node(1, "test_file.txt", false).unwrap(); db.checkpoint().unwrap(); drop(db); // Bereinigungslogik (wie in mount.rs unmount) ausführen let base_os = container_path.as_os_str().to_os_string(); let mut wal = base_os.clone(); wal.push("-wal"); let _ = std::fs::remove_file(&wal); let mut shm = base_os; shm.push("-shm"); let _ = std::fs::remove_file(&shm); assert!( !PathBuf::from(wal).exists(), "WAL-Datei darf nach sauberem Unmount nicht zurückbleiben" ); assert!( !PathBuf::from(shm).exists(), "SHM-Datei darf nach sauberem Unmount nicht zurückbleiben" ); let _ = std::fs::remove_file(&container_path); } #[test] fn test_mount_security_multi_auth_and_no_token_in_url() { use sanctum::mount::{check_basic_auth, check_token_header, uri_contains_token}; let session_token = "4f8a12bc90de45f187a23456789abcde"; let port = 8443; let remote_url = format!("http://127.0.0.1:{}/", port); // R-05 & SA-05: Die Remote-URL für Mount-Befehle darf niemals das Session-Token enthalten! assert!( !remote_url.contains(session_token), "Remote URL darf niemals das Session-Token enthalten" ); // 1. Basic Auth Prüfung (Constant-Time) use base64::Engine; let auth_header = format!( "Basic {}", base64::engine::general_purpose::STANDARD.encode(format!("sanctum:{}", session_token)) ); assert!(check_basic_auth(&auth_header, session_token)); let wrong_auth = format!( "Basic {}", base64::engine::general_purpose::STANDARD.encode("sanctum:wrong_token_1234567890abcdef") ); assert!(!check_basic_auth(&wrong_auth, session_token)); // 2. Token Header Prüfung (Constant-Time) let mut headers = hyper::HeaderMap::new(); headers.insert("X-Sanctum-Token", session_token.parse().unwrap()); assert!(check_token_header(&headers, session_token)); let mut wrong_headers = hyper::HeaderMap::new(); wrong_headers.insert( "X-Sanctum-Token", "wrong_token_1234567890abcdef".parse().unwrap(), ); assert!(!check_token_header(&wrong_headers, session_token)); // 3. SA-05: Verifikation, dass Token in URI (Pfad oder Query) erkannt und strikt abgewiesen wird let uri_path: hyper::Uri = format!("http://127.0.0.1:8443/{}/test.txt", session_token) .parse() .unwrap(); assert!( uri_contains_token(&uri_path, session_token), "Token im Pfad muss erkannt werden" ); let uri_query: hyper::Uri = format!("http://127.0.0.1:8443/test.txt?token={}", session_token) .parse() .unwrap(); assert!( uri_contains_token(&uri_query, session_token), "Token im Query-String muss erkannt werden" ); let uri_clean: hyper::Uri = "http://127.0.0.1:8443/test.txt".parse().unwrap(); assert!( !uri_contains_token(&uri_clean, session_token), "Saubere URI darf kein Token enthalten" ); }