feat(stealth): inject anti-detection patches in native Rust architecture
- Created cli/src/native/stealth.rs with stealth JS injection via CDP - Extracted 32 patch IIFEs from TS stealth.ts into stealth_scripts.js - Injected via Page.addScriptToEvaluateOnNewDocument on every launch/connect - Added stealth Chrome args (disable AutomationControlled, use ANGLE GL) - Auto-detects and cleans HeadlessChrome from User-Agent string - Overrides navigator.userAgentData high-entropy hints - Stealth enabled by default, disable with AGENT_BROWSER_STEALTH=0 Track 2 of native-stealth migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
6addc80aa1
commit
77616a209c
@@ -33,6 +33,7 @@ use super::recording::{self, RecordingState};
|
||||
use super::screenshot::{self, ScreenshotOptions};
|
||||
use super::snapshot::{self, SnapshotOptions};
|
||||
use super::state;
|
||||
use super::stealth;
|
||||
use super::storage;
|
||||
use super::stream::{self, StreamServer};
|
||||
use super::tracing::{self as native_tracing, TracingState};
|
||||
@@ -1550,6 +1551,7 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
|
||||
apply_launch_init_scripts(state).await;
|
||||
try_auto_restore_state(state).await;
|
||||
try_load_storage_state(state, &storage_state_path).await;
|
||||
apply_stealth_to_browser(state).await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1563,6 +1565,7 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
|
||||
apply_launch_init_scripts(state).await;
|
||||
try_auto_restore_state(state).await;
|
||||
try_load_storage_state(state, &storage_state_path).await;
|
||||
apply_stealth_to_browser(state).await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1633,6 +1636,8 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
|
||||
apply_launch_init_scripts(state).await;
|
||||
try_auto_restore_state(state).await;
|
||||
try_load_storage_state(state, &storage_state_path).await;
|
||||
// Apply stealth anti-detection patches after browser is ready
|
||||
apply_stealth_to_browser(state).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1683,6 +1688,36 @@ async fn apply_launch_init_scripts(state: &DaemonState) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Inject stealth scripts into the active browser session.
|
||||
/// Called after every successful launch / CDP connect / auto-connect.
|
||||
async fn apply_stealth_to_browser(state: &DaemonState) {
|
||||
if env::var("AGENT_BROWSER_STEALTH").map(|v| v == "0").unwrap_or(false) {
|
||||
return; // Explicitly disabled
|
||||
}
|
||||
let Some(ref mgr) = state.browser else {
|
||||
return;
|
||||
};
|
||||
let Ok(session_id) = mgr.active_session_id() else {
|
||||
return;
|
||||
};
|
||||
let locale = env::var("AGENT_BROWSER_LOCALE").ok();
|
||||
if let Err(e) = stealth::apply_stealth(
|
||||
&mgr.client,
|
||||
session_id,
|
||||
locale.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!("[stealth] Failed to apply stealth patches: {}", e);
|
||||
}
|
||||
// Also inject into the current page (already loaded before our init script)
|
||||
if let Err(e) =
|
||||
stealth::apply_stealth_to_current_page(&mgr.client, session_id, locale.as_deref()).await
|
||||
{
|
||||
eprintln!("[stealth] Failed to patch current page: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn launch_options_from_env() -> LaunchOptions {
|
||||
let headed = env::var("AGENT_BROWSER_HEADED")
|
||||
.map(|v| v == "1" || v == "true")
|
||||
|
||||
Reference in New Issue
Block a user