rustenium-identity 0.1.11

A versatile stealth overlay for rustenium
Documentation
//! Client hints on the wire, not just in JS.
//!
//! `getHighEntropyValues()` and the `Sec-CH-UA-*` request headers are two
//! separate surfaces, and a detector can read both. This drives a local server
//! that asks for every high-entropy hint via `Accept-CH` and echoes back what it
//! received, so the two can be compared directly.
//!
//! Start the server first:
//!
//! ```text
//! python tests/support/ch_server.py &
//! cargo test --test clienthints -- --ignored --nocapture
//! ```

mod common;

use common::{Detector, dump};
use std::time::Duration;

const URL: &str = "http://127.0.0.1:8761/";
const READY: &str = r#"document.getElementById('o').textContent.startsWith('CH') ? 'yes' : 'no'"#;
const SCRAPE: &str = r#"document.getElementById('o').textContent.slice(2)"#;

#[tokio::test]
#[ignore = "needs the local client-hints server on 127.0.0.1:8761"]
async fn client_hints_reach_the_wire() {
    let mut d = Detector::open(1, URL).await;
    assert!(
        d.wait_until(READY, Duration::from_secs(30)).await,
        "the local client-hints server did not answer — is ch_server.py running?"
    );

    let r = d.eval(SCRAPE).await;
    dump("client hints: headers vs JS", &r);
    let identity_platform_version = d.identity().platform.version.clone();
    d.close().await;

    let headers = &r["headers"];
    let js = &r["js"];

    // The second request carries the hints the first response asked for.
    for (header, js_key) in [
        ("Sec-CH-UA-Arch", "architecture"),
        ("Sec-CH-UA-Bitness", "bitness"),
        ("Sec-CH-UA-Platform-Version", "platformVersion"),
    ] {
        let sent = headers[header].as_str().unwrap_or_default().trim_matches('"').to_string();
        let reported = js[js_key].as_str().unwrap_or_default().to_string();
        println!("{header}: header={sent:?} js={reported:?}");
        assert_eq!(
            sent, reported,
            "{header} on the wire disagrees with getHighEntropyValues()"
        );
    }

    assert_eq!(
        headers["Sec-CH-UA-Platform-Version"].as_str().map(|s| s.trim_matches('"').to_string()),
        Some(identity_platform_version),
        "the platform version on the wire is not the persona's"
    );
}