1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! Chrome launch flags for stealth profiles.
//!
//! Ported from zendriver Python (`zendriver/core/config.py:119-137`) plus
//! chaser-oxide additions.
use crate::ProfileKind;
/// Flags ALL stealth profiles share (Native + Spoofed + Off-when-not-Off).
/// Off profile uses an empty list (truly stock launch).
fn shared_stealth_flags() -> Vec<String> {
vec![
"--no-first-run".into(),
"--no-service-autorun".into(),
"--no-default-browser-check".into(),
"--homepage=about:blank".into(),
"--no-pings".into(),
"--password-store=basic".into(),
"--disable-infobars".into(),
"--disable-breakpad".into(),
"--disable-component-update".into(),
"--disable-backgrounding-occluded-windows".into(),
"--disable-renderer-backgrounding".into(),
"--disable-background-networking".into(),
"--disable-dev-shm-usage".into(),
"--disable-features=IsolateOrigins,DisableLoadExtensionCommandLineSwitch,site-per-process"
.into(),
"--disable-session-crashed-bubble".into(),
"--disable-search-engine-choice-screen".into(),
"--remote-allow-origins=*".into(),
// WebRTC IP-leak prevention (zendriver Python disable_webrtc=True default)
"--webrtc-ip-handling-policy=disable_non_proxied_udp".into(),
"--force-webrtc-ip-handling-policy".into(),
]
}
/// Build the full flag list for a profile.
#[must_use]
pub fn flags_for_profile(kind: ProfileKind) -> Vec<String> {
match kind {
ProfileKind::Off => Vec::new(),
ProfileKind::Native => shared_stealth_flags(),
ProfileKind::Spoofed => {
let mut v = shared_stealth_flags();
// Stop Blink from injecting `navigator.webdriver` (a native
// getter that returns `true`). Without this flag Chrome's
// own AutomationControlled hook overwrites whichever
// `Object.defineProperty(Navigator.prototype, 'webdriver',
// ...)` patch we install via
// `Page.addScriptToEvaluateOnNewDocument`, because the
// hook runs at context-start AFTER our bootstrap script.
// Disabling the feature removes the native injection and
// lets our shim's prototype getter stick.
v.push("--disable-blink-features=AutomationControlled".into());
// Route WebGL through ANGLE + SwiftShader so headless Chrome
// reports a realistic vendor/renderer pair to fingerprinters
// even when GPU acceleration is disabled. Without this trio
// headless mode returns `Canvas has no webgl context` for
// both `UNMASKED_VENDOR_WEBGL` and `UNMASKED_RENDERER_WEBGL`,
// which detectors like sannysoft flag as bot-tier.
// `--enable-unsafe-swiftshader` is required on Chrome >= 116
// because SwiftShader was deprecated for production use; the
// flag re-enables it for fingerprinting purposes.
v.push("--use-gl=angle".into());
v.push("--use-angle=swiftshader".into());
v.push("--enable-unsafe-swiftshader".into());
v
}
}
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn off_profile_emits_no_flags() {
assert!(flags_for_profile(ProfileKind::Off).is_empty());
}
#[test]
fn native_profile_includes_webrtc_disable() {
let flags = flags_for_profile(ProfileKind::Native);
assert!(
flags
.iter()
.any(|f| f.contains("webrtc-ip-handling-policy"))
);
}
#[test]
fn spoofed_profile_includes_isolate_origins_disable() {
let flags = flags_for_profile(ProfileKind::Spoofed);
assert!(flags.iter().any(|f| f.contains("IsolateOrigins")));
}
#[test]
fn shared_flags_snapshot_native() {
let flags = flags_for_profile(ProfileKind::Native);
insta::assert_yaml_snapshot!("native_profile_flags", flags);
}
#[test]
fn shared_flags_snapshot_spoofed() {
let flags = flags_for_profile(ProfileKind::Spoofed);
insta::assert_yaml_snapshot!("spoofed_profile_flags", flags);
}
#[test]
fn shared_flags_snapshot_off() {
let flags = flags_for_profile(ProfileKind::Off);
insta::assert_yaml_snapshot!("off_profile_flags", flags);
}
}