captchaforge 0.2.39

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
//! Stealth profile contract tests, verify that Firefox prefs + JS overrides
//! produce the expected navigator surfaces.
//!
//! These tests launch real Firefox instances with `StealthProfile::FirefoxLinux`
//! and assert that `navigator.userAgent`, `navigator.platform`, `navigator.language`,
//! and `navigator.hardwareConcurrency` match the profile's declared values.

mod common;

#[tokio::test]
async fn stealth_profile_user_agent_matches() {
    let browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    // Apply stealth profile (in addition to any launch-time prefs).
    let profile = captchaforge::StealthProfile::FirefoxLinux;
    let overrides = captchaforge::profile_to_overrides(&profile);
    captchaforge::apply_stealth_profile(&page, &profile)
        .await
        .unwrap();

    let ua: String = page
        .evaluate("navigator.userAgent")
        .await
        .unwrap()
        .into_value()
        .unwrap();
    assert!(
        ua == overrides.user_agent,
        "navigator.userAgent mismatch:\n  expected: {}\n  actual:   {}",
        overrides.user_agent,
        ua
    );

    browser.close().await.unwrap();
}

#[tokio::test]
async fn stealth_profile_platform_matches() {
    let browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let profile = captchaforge::StealthProfile::FirefoxLinux;
    let overrides = captchaforge::profile_to_overrides(&profile);
    captchaforge::apply_stealth_profile(&page, &profile)
        .await
        .unwrap();

    let platform: String = page
        .evaluate("navigator.platform")
        .await
        .unwrap()
        .into_value()
        .unwrap();
    assert_eq!(platform, overrides.platform);

    browser.close().await.unwrap();
}

#[tokio::test]
async fn stealth_profile_languages_match() {
    let browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let profile = captchaforge::StealthProfile::FirefoxLinux;
    let overrides = captchaforge::profile_to_overrides(&profile);
    captchaforge::apply_stealth_profile(&page, &profile)
        .await
        .unwrap();

    let langs: Vec<String> = page
        .evaluate("navigator.languages")
        .await
        .unwrap()
        .into_value()
        .unwrap();
    assert_eq!(langs, overrides.languages);

    browser.close().await.unwrap();
}

#[tokio::test]
async fn stealth_profile_hardware_concurrency_matches() {
    let browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let profile = captchaforge::StealthProfile::FirefoxLinux;
    let overrides = captchaforge::profile_to_overrides(&profile);
    captchaforge::apply_stealth_profile(&page, &profile)
        .await
        .unwrap();

    let concurrency: u32 = page
        .evaluate("navigator.hardwareConcurrency")
        .await
        .unwrap()
        .into_value()
        .unwrap();
    assert_eq!(concurrency, overrides.hardware_concurrency);

    browser.close().await.unwrap();
}

#[tokio::test]
async fn stealth_profile_webdriver_is_absent() {
    let browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let profile = captchaforge::StealthProfile::FirefoxLinux;
    captchaforge::apply_stealth_profile(&page, &profile)
        .await
        .unwrap();

    let webdriver: bool = page
        .evaluate("navigator.webdriver === undefined || navigator.webdriver === false")
        .await
        .unwrap()
        .into_value()
        .unwrap();
    assert!(webdriver, "navigator.webdriver should be absent/false");

    browser.close().await.unwrap();
}

#[tokio::test]
async fn stealth_profile_screen_is_self_consistent() {
    // The disguise deliberately does NOT override screen.*, overriding the JS
    // getters desyncs them from the CSS `matchMedia` device-width/height layer
    // (a real, hard tell: incolumitas MQ_SCREEN). The correct contract is that
    // screen is plausible AND self-consistent with the media-query layer, which
    // a real browser always is and a bare headless Firefox already satisfies.
    let browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let profile = captchaforge::StealthProfile::FirefoxLinux;
    captchaforge::apply_stealth_profile(&page, &profile)
        .await
        .unwrap();

    let consistent: bool = page
        .evaluate(
            "(() => { const w = screen.width, h = screen.height; \
             if (!(w > 0 && h > 0)) return false; \
             return window.matchMedia('(device-width: ' + w + 'px)').matches \
                 && window.matchMedia('(device-height: ' + h + 'px)').matches; })()",
        )
        .await
        .unwrap()
        .into_value()
        .unwrap();

    assert!(
        consistent,
        "screen.* must be plausible and consistent with the matchMedia device-width/height layer"
    );

    browser.close().await.unwrap();
}