use captchaforge::solver::{CaptchaType, PatternStore, SolveConfig, SolveMethod};
#[test]
fn fuzz_detect_arbitrary_html() {
let htmls = [
b"<html><body></body></html>" as &[u8],
b"{\"kind\": \"turnstile\", \"site_key\": \"abc\"}",
b"{\"kind\": \"recaptcha_v2\", \"site_key\": null}",
b"garbage data here",
b"",
&[0u8, 159, 146, 150], ];
for data in &htmls {
if let Ok(html) = std::str::from_utf8(data) {
let _ = captchaforge::detect::DETECT_JS.len();
if let Ok(val) = serde_json::from_str::<serde_json::Value>(html) {
let kind_str = val["kind"].as_str().unwrap_or("none");
let _ = matches!(
kind_str,
"turnstile" | "recaptcha_v2" | "recaptcha_v3" | "hcaptcha" | "image" | "audio"
);
}
}
}
}
#[test]
fn fuzz_solve_config_arbitrary_bytes() {
let inputs: Vec<Vec<u8>> = vec![
vec![0; 64],
vec![255; 64],
(0..64).collect(),
vec![
1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 0,
0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
],
];
for data in inputs {
if data.len() >= 64 {
let cfg = SolveConfig {
checkbox_poll_interval_ms: u64::from_le_bytes(data[0..8].try_into().unwrap()),
checkbox_max_attempts: u32::from_le_bytes(data[8..12].try_into().unwrap()),
token_poll_interval_ms: u64::from_le_bytes(data[12..20].try_into().unwrap()),
token_max_attempts: u32::from_le_bytes(data[20..24].try_into().unwrap()),
audio_button_delay_ms: u64::from_le_bytes(data[24..32].try_into().unwrap()),
audio_submit_delay_ms: u64::from_le_bytes(data[32..40].try_into().unwrap()),
vlm_http_timeout_ms: u64::from_le_bytes(data[40..48].try_into().unwrap()),
client_http_timeout_ms: u64::from_le_bytes(data[48..56].try_into().unwrap()),
};
let _ = cfg.checkbox_poll_interval_ms;
let _ = cfg.checkbox_max_attempts;
}
}
}
#[test]
fn fuzz_selector_arbitrary_strings() {
let selectors = ["#id", ".class", "[data-a='b']", "", "\"", "\\", "\n", "\0"];
for selector in &selectors {
let escaped = selector.replace('"', "\\\"");
let _js = format!(r#"document.querySelector("{}")"#, escaped);
}
}
#[test]
fn fuzz_pattern_key_arbitrary_strings() {
let keys = ["", "a", "example.com", "foo/bar", "\n", "unicode_\u{1F600}"];
for s in &keys {
let store = PatternStore::default();
let captcha_type = CaptchaType::Custom(s.to_string());
store.record(
"example.com",
&captcha_type,
true,
100,
SolveMethod::BehavioralBypass,
);
let _ = store.best_method("example.com", &captcha_type);
let _ = store.all_patterns();
}
}
#[test]
fn fuzz_pattern_key_with_many_types() {
let store = PatternStore::default();
for i in 0..100 {
let ct = CaptchaType::Custom(format!("type_{}", i));
store.record(
"example.com",
&ct,
i % 2 == 0,
i as u64 * 10,
SolveMethod::BehavioralBypass,
);
}
assert_eq!(store.all_patterns().len(), 100);
}