use crate::{expand_all, expand_cidr, parse_many, Target, TargetList};
use std::io::Write;
use std::str::FromStr;
#[test]
fn adversarial_ipv6_with_zones() {
let cases = vec![
"fe80::1%eth0",
"fe80::1%en0",
"fe80::1%lo0",
"fe80::1%1", ];
for case in &cases {
let result = case.parse::<Target>();
if let Ok(Target::Ip(ip)) = result {
assert!(ip.is_ipv6(), "Expected IPv6 for {}", case);
}
}
}
#[test]
fn adversarial_cidr_zero_prefix() {
let target = Target::from_str("0.0.0.0/0");
assert!(target.is_err());
let target6 = Target::from_str("::/0");
assert!(target6.is_ok());
if let Ok(Target::Cidr { addr, prefix }) = target6 {
assert_eq!(prefix, 0);
let expanded = expand_cidr(addr, prefix);
assert!(expanded.is_empty());
}
}
#[test]
fn adversarial_punycode_domains() {
let cases = vec![
"xn--nxasmq5b.com", "xn--bcher-kva.example", "xn--e1afmkfd.xn--p1ai", "xn--fsq092h.com", "example.xn--nxasmq5b.com",
"sub.xn--bcher-kva.example",
];
for case in &cases {
let result = case.parse::<Target>();
assert!(result.is_ok(), "Failed to parse punycode: {}", case);
if let Ok(Target::Domain(domain)) = result.as_ref() {
assert!(
domain.contains("xn--"),
"Punycode prefix preserved: {}",
domain
);
} else if let Ok(Target::Url(url)) = result.as_ref() {
assert!(
url.contains("xn--") || url.contains("xn--"),
"Punycode in URL: {}",
url
);
}
}
}
#[test]
fn adversarial_targets_with_auth() {
let cases = vec![
"https://user:pass@example.com",
"https://admin:secret123@api.example.com:8443/path",
"http://user:@example.com", "https://:pass@example.com", "https://user:p%40ss@example.com", "ftp://anonymous:@ftp.example.com",
];
for case in &cases {
let result = case.parse::<Target>();
assert!(result.is_ok(), "Failed to parse auth URL: {}", case);
if let Ok(Target::Url(url)) = result {
assert!(url.contains('@'), "Auth info preserved in: {}", url);
}
}
}
#[test]
fn adversarial_100k_targets_from_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("100k_targets.txt");
let mut file = std::fs::File::create(&path).unwrap();
for i in 0..100_000 {
writeln!(file, "target-{}.example.com", i).unwrap();
}
let targets = TargetList::from_file(&path).unwrap();
assert_eq!(targets.len(), 100_000);
}
#[test]
fn adversarial_empty_lines_mixed() {
let input = r#"
example.com
https://test.com
192.168.1.1
10.0.0.0/8
"#;
let targets = parse_many(input);
assert_eq!(targets.len(), 4);
}
#[test]
fn adversarial_whitespace_variants() {
let input = "example.com\n \n\t\t\nhttps://test.com\n \n \n 192.168.1.1 \n";
let targets = parse_many(input);
assert_eq!(targets.len(), 3);
}
#[test]
fn adversarial_malformed_urls() {
let malformed = vec![
"http://", "https://", "://example.com", "http:/example.com", "http://example..com", "http://.example.com", "http://example.com.", "http://example.com:999999", "http://example.com:-80", "http://[:::1]", "", " ", ];
for case in &malformed {
let result = case.parse::<Target>();
let _ = result;
}
}
#[test]
fn adversarial_cidr_edge_cases() {
let cases = vec![
("0.0.0.0/0", false), ("255.255.255.255/32", true), ("192.168.1.0/24", true), ("192.168.1.0/33", false), ("192.168.1.0/", false), ("/24", false), ("256.0.0.0/8", false), ("::/0", true), ("::1/128", true), ("::/129", false), ("fe80::/64", true), ];
for (input, should_parse) in cases {
let result = Target::from_str(input);
if should_parse {
assert!(result.is_ok(), "Should parse: {}", input);
} else {
}
}
}
#[test]
fn adversarial_cidr_huge_expansion() {
let expanded = expand_cidr("10.0.0.0".parse().unwrap(), 8);
assert!(expanded.is_empty() || expanded.len() <= 1_000_000);
let expanded = expand_cidr("192.168.0.0".parse().unwrap(), 16);
assert!(expanded.len() <= 65_536);
let expanded = expand_cidr("2001:db8::".parse().unwrap(), 64);
assert!(expanded.is_empty());
}
#[test]
fn adversarial_target_normalization() {
let cases = vec![
("Example.COM", "https://example.com"), ("example.com:443", "https://example.com:443"), ];
for (input, expected_prefix) in cases {
let target = input.parse::<Target>().unwrap();
let normalized = target.normalize();
if let Target::Url(url) = normalized {
assert!(
url.starts_with(expected_prefix) || url.to_lowercase().starts_with(expected_prefix),
"Expected {} to start with {}, got {}",
input,
expected_prefix,
url
);
}
}
}
#[test]
fn adversarial_targets_with_comments() {
let input = r#"
# This is a comment
example.com
# Another comment
https://test.com
# Comment with URL: http://ignored.com
192.168.1.1
# Final comment
"#;
let targets = parse_many(input);
assert_eq!(targets.len(), 3);
}
#[test]
fn adversarial_very_long_target() {
let long_label = "a".repeat(63); let long_domain = format!("{}.example.com", long_label);
let result = long_domain.parse::<Target>();
assert!(result.is_ok());
let long_path = "/".to_string() + &"a/".repeat(1000);
let long_url = format!("https://example.com{}", long_path);
let result = long_url.parse::<Target>();
assert!(result.is_err());
}
#[test]
fn adversarial_from_args_mixed() {
let dir = tempfile::tempdir().unwrap();
let file_path = dir.path().join("targets.txt");
let mut file = std::fs::File::create(&file_path).unwrap();
writeln!(file, "from-file-1.com").unwrap();
writeln!(file, "from-file-2.com").unwrap();
let args = vec![
file_path.to_string_lossy().to_string(),
"literal-target.com".to_string(),
"10.0.0.1".to_string(),
];
let targets = TargetList::from_args(&args).unwrap();
assert_eq!(targets.len(), 4);
}
#[test]
fn adversarial_deduplication_edge_cases() {
let targets = vec![
Target::Domain("Example.COM".to_string()),
Target::Url("https://example.com/".to_string()),
Target::Domain("example.com".to_string()),
Target::Url("https://EXAMPLE.COM".to_string()),
];
let mut list = TargetList::from(targets);
list.dedup();
assert!(
list.count() <= 2,
"Expected deduped targets, got {}",
list.count()
);
}
#[test]
fn adversarial_ip_edge_cases() {
let cases = vec![
"0.0.0.0", "255.255.255.255", "127.0.0.1", "::", "::1", "::ffff:192.168.1.1", "fe80::1", "ff02::1", "2001:db8::", "2001:0db8:0000:0000:0000:0000:0000:0001", "2001:db8::1", ];
for case in cases {
let result = case.parse::<Target>();
assert!(result.is_ok(), "Failed to parse IP: {}", case);
}
}
#[test]
fn adversarial_path_traversal_targets() {
let cases = vec![
"../../../etc/passwd",
"..\\..\\windows\\system32\\config\\sam",
"....//....//etc/passwd",
"%2e%2e%2f%2e%2e%2fetc/passwd",
"example.com/../../../etc/passwd",
];
for case in &cases {
let result = case.parse::<Target>();
assert!(
result.is_ok() || result.is_err(),
"Unexpected parser failure mode for path traversal case: {case}"
);
}
}
#[test]
fn adversarial_expand_all_mixed() {
let targets = vec![
Target::Domain("example.com".to_string()),
Target::Cidr {
addr: "10.0.0.0".parse().unwrap(),
prefix: 30,
},
Target::Ip("192.168.1.1".parse().unwrap()),
Target::Url("https://test.com".to_string()),
Target::Cidr {
addr: "::1".parse().unwrap(),
prefix: 128,
},
];
let expanded = expand_all(&targets);
assert!(expanded.len() >= 5);
}