captchaforge 0.2.40

Captcha detection and solving for Firefox and BiDi-driven browsers. Detection, vendor solver scaffolding, trusted cross-origin click delivery into nested OOPIFs, and stealth personas are implemented and tested; broad live-vendor solve rates are not yet benchmarked.
Documentation
//! CVE-replay corpus walker. Asserts every CVE directory in
//! `cve-corpus/` has the required four files (README.md +
//! page.html + expected.json + solver.txt). When all four exist,
//! asserts captchaforge's detect+solve produces the expected
//! outcome on the frozen page.
//!
//! Today the corpus is empty; this test is the gate that ensures
//! NEW captures land with the right shape. Future disclosures get
//! a deterministic replay forever.

use std::path::PathBuf;

fn corpus_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("cve-corpus")
}

#[test]
fn cve_corpus_directory_exists() {
    let root = corpus_root();
    assert!(
        root.is_dir(),
        "cve-corpus/ must exist (even if empty), disclosures land here"
    );
    assert!(
        root.join("README.md").is_file(),
        "cve-corpus/README.md must document the format"
    );
}

#[test]
fn every_cve_dir_has_required_files() {
    let root = corpus_root();
    if !root.is_dir() {
        return;
    }
    let mut missing: Vec<String> = Vec::new();
    for entry in std::fs::read_dir(&root).expect("read cve-corpus") {
        let entry = entry.expect("read entry");
        let path = entry.path();
        let name = path
            .file_name()
            .and_then(|s| s.to_str())
            .unwrap_or_default();
        if !path.is_dir() || !name.starts_with("CVE-") {
            continue;
        }
        for required in ["README.md", "page.html", "expected.json", "solver.txt"] {
            if !path.join(required).is_file() {
                missing.push(format!("{name}/{required}"));
            }
        }
    }
    assert!(
        missing.is_empty(),
        "CVE corpus entries missing required files:\n  {}",
        missing.join("\n  ")
    );
}

#[test]
fn each_expected_json_parses() {
    let root = corpus_root();
    if !root.is_dir() {
        return;
    }
    for entry in std::fs::read_dir(&root).expect("read cve-corpus") {
        let entry = entry.expect("read entry");
        let path = entry.path();
        let name = path
            .file_name()
            .and_then(|s| s.to_str())
            .unwrap_or_default();
        if !path.is_dir() || !name.starts_with("CVE-") {
            continue;
        }
        let expected_path = path.join("expected.json");
        if !expected_path.is_file() {
            continue;
        }
        let body = std::fs::read_to_string(&expected_path).expect("read expected.json");
        let _: serde_json::Value =
            serde_json::from_str(&body).expect("expected.json must be valid JSON");
    }
}