Skip to main content

stygian_browser/coherence/
mod.rs

1//! Cross-context stealth coherence probes.
2//!
3//! Verifies that the browser's identity surface (user agent,
4//! platform, languages, `navigator.webdriver`, screen metrics) is
5//! **coherent across the top-level document, same-origin iframes,
6//! and dedicated/shared workers**. Drift between contexts is a
7//! strong anti-bot detection signal — most anti-bot vendors probe
8//! at least one of these auxiliary contexts and compare.
9//!
10//! ## What is a "context"?
11//!
12//! The browser exposes three isolation levels that an anti-bot
13//! script can probe:
14//!
15//! - **Top-level document** — `window` of the main frame.
16//! - **Same-origin iframe** — a `<iframe srcdoc=…>` injected into
17//!   the document at probe time. `srcdoc` iframes inherit the
18//!   parent's origin and provide a deterministic, network-free
19//!   sub-context.
20//! - **Dedicated/shared worker** — a `Worker` constructed from a
21//!   `Blob` URL. The worker has its own `WorkerGlobalScope` and
22//!   its own `navigator`. Worker probes are **best-effort**: when
23//!   the runtime does not expose `Worker` / `Blob` /
24//!   `URL.createObjectURL`, the worker slot in the report is
25//!   populated with a [`ContextObservation::Skipped`] marker
26//!   rather than panicking.
27//!
28//! ## Feature flag
29//!
30//! This module is **default-on** and is always compiled as part of
31//! the `stygian-browser` crate. It requires CDP (the
32//! `stygian-browser` default), since the probe runner uses
33//! `PageHandle::eval` to send JavaScript to the page.
34//!
35//! ## Hard failures vs known limitations
36//!
37//! Drift is split into two severity bands (see
38//! [`report::DriftSeverity`]):
39//!
40//! - **Hard** — `user_agent`, `platform`, `languages`, and
41//!   `navigator.webdriver` MUST be identical across all
42//!   observed contexts.
43//! - **Known limitation** — `hardware_concurrency`,
44//!   `device_memory`, screen metrics, and timezone are documented
45//!   to differ between Document and Worker contexts in some
46//!   browser engines.
47//!
48//! ## Idempotence
49//!
50//! All probe methods are safely re-runnable. The iframe is removed
51//! from the DOM at the end of the probe, the worker is
52//! `terminate()`-ed, and the `Blob` URL is `revokeObjectURL`-ed
53//! before the probe returns.
54//!
55//! # Example
56//!
57//! ```no_run
58//! # async fn run() -> stygian_browser::error::Result<()> {
59//! use stygian_browser::{BrowserPool, BrowserConfig, WaitUntil};
60//! use stygian_browser::coherence::CoherenceProbe;
61//! use std::time::Duration;
62//!
63//! let pool = BrowserPool::new(BrowserConfig::default()).await?;
64//! let handle = pool.acquire().await?;
65//! let mut page = handle
66//!     .browser()
67//!     .expect("valid browser")
68//!     .new_page()
69//!     .await?;
70//! page.navigate(
71//!     "https://example.com",
72//!     WaitUntil::DomContentLoaded,
73//!     Duration::from_secs(30),
74//! )
75//! .await?;
76//!
77//! let probe = CoherenceProbe::new();
78//! let report = probe.run(&page).await?;
79//! println!(
80//!     "coherent={} hard_drift={} contexts={}/3",
81//!     report.is_coherent(),
82//!     report.has_hard_drift(),
83//!     report.observed_context_count(),
84//! );
85//! # Ok(())
86//! # }
87//! ```
88
89pub mod probes;
90pub mod report;
91
92pub use probes::CoherenceProbe;
93pub use report::{
94    CoherenceDriftReport, ContextKind, ContextObservation, ContextPair, DriftDiagnostic,
95    DriftSeverity, IdentitySurface, build_report, diff_surfaces, field_severity, surface_signature,
96};