use derusted::mitm::{
HstsManager, HstsPolicy,
PinningDetector, PinningPatterns, PinningPolicy, SniUtils, TlsConfigBuilder,
TlsHardeningConfig, TlsVersion, UpstreamTlsConfig,
};
use derusted::{BypassConfig, BypassManager, LoggingPolicy};
use std::sync::Arc;
#[test]
fn test_tls_version_defaults() {
let version = TlsVersion::default();
assert_eq!(version, TlsVersion::Tls12And13);
}
#[test]
fn test_tls_hardening_config_defaults() {
let config = TlsHardeningConfig::default();
assert!(config.reject_old_tls);
assert_eq!(config.min_version, TlsVersion::Tls12And13);
assert!(config.enforce_hostname_verification);
}
#[test]
fn test_tls_hardening_config_strict() {
let config = TlsHardeningConfig::strict();
assert!(config.reject_old_tls);
assert_eq!(config.min_version, TlsVersion::Tls13Only);
assert!(config.require_alpn);
assert!(config.enforce_hostname_verification);
}
#[test]
fn test_sni_validate_hostname() {
assert!(SniUtils::validate_hostname("example.com"));
assert!(SniUtils::validate_hostname("sub.example.com"));
assert!(SniUtils::validate_hostname("a.b.c.example.com"));
assert!(!SniUtils::validate_hostname(""));
assert!(!SniUtils::validate_hostname(".example.com"));
assert!(!SniUtils::validate_hostname("example.com."));
}
#[test]
fn test_sni_parse_server_name() {
let result = SniUtils::parse_server_name("example.com");
assert!(result.is_ok());
let result = SniUtils::parse_server_name("192.168.1.1");
assert!(
result.is_ok(),
"rustls 0.22+ accepts IP addresses in ServerName"
);
let result = SniUtils::parse_server_name("");
assert!(result.is_err(), "Empty hostname should fail");
}
#[test]
fn test_tls_config_builder_defaults() {
let builder = TlsConfigBuilder::new();
}
#[test]
fn test_tls_config_builder_customization() {
let builder = TlsConfigBuilder::new()
.tls_version(TlsVersion::Tls13Only)
.enable_sni(false)
.verify_hostname(false);
}
#[test]
fn test_upstream_tls_config_creation() {
let config = UpstreamTlsConfig::new();
assert!(config.is_ok());
let config = UpstreamTlsConfig::new_with_options(TlsVersion::Tls13Only);
assert!(config.is_ok());
}
#[tokio::test]
async fn test_hsts_policy_defaults() {
let policy = HstsPolicy::default();
assert!(policy.honor_hsts);
assert!(!policy.strip_hsts_headers);
assert_eq!(policy.max_cache_age, 31536000);
assert!(policy.use_preload_list);
}
#[tokio::test]
async fn test_hsts_policy_strip_for_testing() {
let policy = HstsPolicy::strip_for_testing();
assert!(!policy.honor_hsts);
assert!(policy.strip_hsts_headers);
}
#[tokio::test]
async fn test_hsts_manager_preload_list() {
let manager = HstsManager::new();
assert!(manager.is_hsts_domain("google.com").await);
assert!(manager.is_hsts_domain("github.com").await);
assert!(manager.is_hsts_domain("facebook.com").await);
assert!(!manager.is_hsts_domain("example.com").await);
}
#[tokio::test]
async fn test_hsts_manager_add_from_header() {
let manager = HstsManager::new();
manager
.add_from_header("example.com", "max-age=31536000; includeSubDomains")
.await;
assert!(manager.is_hsts_domain("example.com").await);
assert!(manager.is_hsts_domain("sub.example.com").await);
}
#[tokio::test]
async fn test_hsts_manager_max_age_zero() {
let manager = HstsManager::new();
manager
.add_from_header("example.com", "max-age=31536000")
.await;
assert!(manager.is_hsts_domain("example.com").await);
manager.add_from_header("example.com", "max-age=0").await;
assert!(!manager.is_hsts_domain("example.com").await);
}
#[tokio::test]
async fn test_hsts_manager_disabled() {
let policy = HstsPolicy {
honor_hsts: false,
..Default::default()
};
let manager = HstsManager::with_policy(policy);
assert!(!manager.is_hsts_domain("google.com").await);
}
#[tokio::test]
async fn test_hsts_strip_headers() {
use std::collections::HashMap;
let policy = HstsPolicy::strip_for_testing();
let manager = HstsManager::with_policy(policy);
let mut headers = HashMap::new();
headers.insert(
"strict-transport-security".to_string(),
"max-age=31536000".to_string(),
);
headers.insert("content-type".to_string(), "text/html".to_string());
manager.process_response_headers(&mut headers);
assert!(!headers.contains_key("strict-transport-security"));
assert!(headers.contains_key("content-type"));
}
#[tokio::test]
async fn test_hsts_cache_size() {
let manager = HstsManager::new();
let size = manager.cache_size().await;
assert!(size >= 20); }
#[tokio::test]
async fn test_hsts_cleanup_expired() {
let manager = HstsManager::new();
manager
.add_from_header("example.com", "max-age=31536000")
.await;
manager.cleanup_expired().await;
assert!(manager.is_hsts_domain("example.com").await);
}
#[tokio::test]
async fn test_pinning_policy_defaults() {
let policy = PinningPolicy::default();
assert!(policy.detect_pinning);
assert!(!policy.auto_bypass); assert_eq!(policy.failure_threshold, 3);
assert_eq!(policy.failure_window, 300);
assert!(policy.alert_on_pinning);
}
#[tokio::test]
async fn test_pinning_detector_threshold() {
let detector = PinningDetector::new();
let result = detector
.record_failure("pinned.com", "cert verify failed")
.await;
assert!(!result.detected);
assert_eq!(result.failure_count, 1);
let result = detector
.record_failure("pinned.com", "cert verify failed")
.await;
assert!(!result.detected);
assert_eq!(result.failure_count, 2);
let result = detector
.record_failure("pinned.com", "cert verify failed")
.await;
assert!(result.detected);
assert_eq!(result.failure_count, 3);
}
#[tokio::test]
async fn test_pinning_detector_clear_failures() {
let detector = PinningDetector::new();
detector.record_failure("example.com", "error").await;
assert!(detector.has_failures("example.com").await);
detector.clear_failures("example.com").await;
assert!(!detector.has_failures("example.com").await);
}
#[tokio::test]
async fn test_pinning_stats() {
let detector = PinningDetector::new();
for _ in 0..3 {
detector.record_failure("pinned1.com", "error").await;
}
for _ in 0..2 {
detector.record_failure("pinned2.com", "error").await;
}
let stats = detector.stats().await;
assert_eq!(stats.total_domains, 2);
assert_eq!(stats.pinned_domains, 1); }
#[test]
fn test_pinning_patterns() {
assert!(PinningPatterns::is_pinning_error(
"certificate verify failed"
));
assert!(PinningPatterns::is_pinning_error("TLS handshake failed"));
assert!(PinningPatterns::is_pinning_error("Certificate not trusted"));
assert!(!PinningPatterns::is_pinning_error("connection timeout"));
assert!(!PinningPatterns::is_pinning_error("dns resolution failed"));
}
#[tokio::test]
async fn test_pinning_detector_get_failure_count() {
let detector = PinningDetector::new();
assert_eq!(detector.get_failure_count("example.com").await, 0);
detector.record_failure("example.com", "error").await;
assert_eq!(detector.get_failure_count("example.com").await, 1);
detector.record_failure("example.com", "error").await;
assert_eq!(detector.get_failure_count("example.com").await, 2);
}
#[tokio::test]
async fn test_pinning_detector_clear_all() {
let detector = PinningDetector::new();
detector.record_failure("example1.com", "error").await;
detector.record_failure("example2.com", "error").await;
assert!(detector.has_failures("example1.com").await);
assert!(detector.has_failures("example2.com").await);
detector.clear_all_failures().await;
assert!(!detector.has_failures("example1.com").await);
assert!(!detector.has_failures("example2.com").await);
}
#[tokio::test]
async fn test_bypass_manager_creation() {
let config = BypassConfig::default();
let manager = BypassManager::new(config);
}
#[tokio::test]
async fn test_logging_policy_defaults() {
let policy = LoggingPolicy::default();
assert!(!policy.log_request_headers);
assert!(!policy.log_response_headers);
assert!(!policy.log_request_body);
assert!(!policy.log_response_body);
assert!(policy.enable_pii_redaction);
}
#[tokio::test]
async fn test_hsts_and_bypass_integration() {
let hsts_manager = HstsManager::new();
let bypass_config = BypassConfig::default();
let bypass_manager = BypassManager::new(bypass_config);
assert!(hsts_manager.is_hsts_domain("google.com").await);
let result = bypass_manager.should_bypass("google.com").await;
}
#[tokio::test]
async fn test_pinning_with_bypass() {
let bypass_config = BypassConfig::default();
let bypass_manager = Arc::new(BypassManager::new(bypass_config));
let mut policy = PinningPolicy::default();
policy.auto_bypass = true; policy.failure_threshold = 2;
let detector =
PinningDetector::with_policy(policy).with_bypass_manager(Arc::clone(&bypass_manager));
let result = detector
.record_failure("pinned.com", "cert verify failed")
.await;
assert!(!result.detected);
let result = detector
.record_failure("pinned.com", "cert verify failed")
.await;
assert!(result.detected);
}
#[tokio::test]
async fn test_complete_mitm_setup() {
let upstream_tls = UpstreamTlsConfig::new();
assert!(upstream_tls.is_ok());
let hsts_manager = HstsManager::new();
assert!(hsts_manager.is_hsts_domain("github.com").await);
let pinning_detector = PinningDetector::new();
assert_eq!(pinning_detector.get_failure_count("test.com").await, 0);
let bypass_manager = BypassManager::new(BypassConfig::default());
let logging_policy = LoggingPolicy::default();
assert!(logging_policy.enable_pii_redaction);
}
#[test]
fn test_tls_version_enum() {
let v1 = TlsVersion::Tls12And13;
let v2 = TlsVersion::Tls13Only;
assert_ne!(v1, v2);
}
#[test]
fn test_tls_hardening_config_compatible() {
let config = TlsHardeningConfig::compatible();
assert_eq!(config.min_version, TlsVersion::Tls12And13);
assert!(!config.require_alpn); }
#[tokio::test]
async fn test_hsts_from_env() {
let manager = HstsManager::from_env();
assert!(manager.is_hsts_domain("google.com").await);
}
#[tokio::test]
async fn test_pinning_from_env() {
let detector = PinningDetector::from_env();
assert!(!detector.has_failures("test.com").await);
}