feat(stealth): shrink detectable surface — lazy Runtime.enable, native timezone/WebRTC, opt-in canvas noise
Borrow anti-detection hardening from Scrapling/patchright, preferring native CDP/Chrome overrides over JS lies: - Runtime.enable is now opt-in via AGENT_BROWSER_CAPTURE_CONSOLE (default off). It was called on every session INCLUDING CdpAttach (the user's real Chrome), leaking the patchright/rebrowser "runtime" CDP signal and undermining the "real browser, no lies" guarantee. Runtime.evaluate/callFunctionOn and runIfWaitingForDebugger work without it; only console/error capture needs it. The console/errors commands now return a hint when capture is disabled. - Timezone alignment via native Emulation.setTimezoneOverride, opt-in with AGENT_BROWSER_TIMEZONE=<IANA>|auto (FullLaunch only). Intl and Date both follow with no JS artifact. - WebRTC IP-leak handling via the --force-webrtc-ip-handling-policy Chrome flag: auto disable_non_proxied_udp when a proxy is set (so the real IP can't leak past the proxy); AGENT_BROWSER_BLOCK_WEBRTC=1 hides the local IP when there is no proxy; =0 opts out. - Opt-in canvas/audio fingerprint noise via AGENT_BROWSER_HIDE_CANVAS=1 (FullLaunch only). Session-stable seed so reads stay consistent within a session while differing from the headless-stable hash. Adds 5 unit tests; full suite 751 passed, 0 failed.
This commit is contained in:
@@ -3690,13 +3690,38 @@ async fn handle_console(cmd: &Value, state: &mut DaemonState) -> Result<Value, S
|
||||
state.event_tracker.clear_console();
|
||||
Ok(json!({ "cleared": true }))
|
||||
} else {
|
||||
let result = state.event_tracker.get_console_json();
|
||||
let mut result = state.event_tracker.get_console_json();
|
||||
if !console_capture_active(state) {
|
||||
if let Some(obj) = result.as_object_mut() {
|
||||
obj.insert("hint".to_string(), json!(CONSOLE_DISABLED_HINT));
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the active browser session has the CDP `Runtime` domain enabled
|
||||
/// (required to receive console/error events). OFF by default for stealth.
|
||||
fn console_capture_active(state: &DaemonState) -> bool {
|
||||
state
|
||||
.browser
|
||||
.as_ref()
|
||||
.map(|b| b.capture_console)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
const CONSOLE_DISABLED_HINT: &str =
|
||||
"console/error capture is disabled for stealth (Runtime.enable is a detectable CDP \
|
||||
signal). Restart the session with AGENT_BROWSER_CAPTURE_CONSOLE=1 to capture page output.";
|
||||
|
||||
async fn handle_errors(state: &DaemonState) -> Result<Value, String> {
|
||||
Ok(state.event_tracker.get_errors_json())
|
||||
let mut result = state.event_tracker.get_errors_json();
|
||||
if !console_capture_active(state) {
|
||||
if let Some(obj) = result.as_object_mut() {
|
||||
obj.insert("hint".to_string(), json!(CONSOLE_DISABLED_HINT));
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn handle_state_save(cmd: &Value, state: &DaemonState) -> Result<Value, String> {
|
||||
|
||||
Reference in New Issue
Block a user