use crate::page::escape_js_string;
use crate::stealth::fingerprint::{Fingerprint, GENERIC_FONTS};
use crate::StealthConfig;
pub const NATIVE_TOSTRING_EVASION: &str = include_str!("evasions/scripts/native_tostring.js");
pub const WEBDRIVER_EVASION: &str = include_str!("evasions/scripts/webdriver.js");
pub const CDP_EVASION: &str = include_str!("evasions/scripts/cdp.js");
pub const CHROME_RUNTIME_EVASION: &str = include_str!("evasions/scripts/chrome_runtime.js");
pub const PERMISSIONS_EVASION: &str = include_str!("evasions/scripts/permissions.js");
pub const PLUGINS_EVASION: &str = include_str!("evasions/scripts/plugins.js");
pub const NAVIGATOR_PROPS_EVASION: &str = include_str!("evasions/scripts/navigator_props.js");
pub const HEADLESS_EVASION: &str = include_str!("evasions/scripts/headless.js");
pub const BATTERY_EVASION: &str = include_str!("evasions/scripts/battery.js");
pub const NAVIGATOR_EXTRA_EVASION: &str = include_str!("evasions/scripts/navigator_extra.js");
pub const FINGERPRINT_EVASION: &str = include_str!("evasions/scripts/fingerprint.js");
pub const WEBRTC_EVASION: &str = include_str!("evasions/scripts/webrtc.js");
pub const SPEECH_EVASION: &str = include_str!("evasions/scripts/speech.js");
pub const MEDIA_DEVICES_EVASION: &str = include_str!("evasions/scripts/media_devices.js");
pub const BLUETOOTH_EVASION: &str = include_str!("evasions/scripts/bluetooth.js");
pub const FONTS_EVASION: &str = include_str!("evasions/scripts/fonts.js");
pub const KEYBOARD_EVASION: &str = include_str!("evasions/scripts/keyboard.js");
pub const TIMEZONE_EVASION: &str = include_str!("evasions/scripts/timezone.js");
pub(crate) const COMMON_TIMEZONES: &[&str] = &[
"America/New_York",
"America/Chicago",
"America/Los_Angeles",
"America/Sao_Paulo",
"Europe/London",
"Europe/Paris",
"Europe/Berlin",
"Europe/Moscow",
"Asia/Tokyo",
"Asia/Shanghai",
"Asia/Kolkata",
"Australia/Sydney",
];
pub fn build_evasion_script_for(config: &StealthConfig, fp: &Fingerprint) -> String {
let timezone = config
.timezone
.clone()
.unwrap_or_else(|| fp.timezone.clone());
let mut scripts = vec![
NATIVE_TOSTRING_EVASION, WEBDRIVER_EVASION,
CHROME_RUNTIME_EVASION,
PERMISSIONS_EVASION,
PLUGINS_EVASION,
NAVIGATOR_PROPS_EVASION,
HEADLESS_EVASION,
BATTERY_EVASION,
NAVIGATOR_EXTRA_EVASION,
WEBRTC_EVASION,
SPEECH_EVASION,
MEDIA_DEVICES_EVASION,
FONTS_EVASION,
KEYBOARD_EVASION,
BLUETOOTH_EVASION,
TIMEZONE_EVASION,
];
if config.aggressive_cdp_evasion {
scripts.push(CDP_EVASION);
}
if config.webgl_spoof || config.canvas_spoof || config.audio_spoof {
scripts.push(FINGERPRINT_EVASION);
}
let languages_json = format!(
"[{}]",
fp.languages
.iter()
.map(|l| format!("'{}'", escape_js_string(l)))
.collect::<Vec<_>>()
.join(",")
);
let language = fp.languages.first().cloned().unwrap_or_default();
let noise_seed = fp.noise_seed;
let script = format!("(function(){{{}}})();", scripts.join("\n"));
let placeholders: Vec<(&str, String)> = vec![
("__EOKA_TIMEZONE__", escape_js_string(&timezone)),
("__EOKA_PLATFORM__", escape_js_string(fp.nav_platform())),
("__EOKA_LANGUAGES__", languages_json),
("__EOKA_LANGUAGE__", escape_js_string(&language)),
("__EOKA_HW__", fp.hardware_concurrency.to_string()),
("__EOKA_MEM__", fp.device_memory.to_string()),
("__EOKA_WEBGL_VENDOR__", escape_js_string(&fp.webgl_vendor)),
(
"__EOKA_WEBGL_RENDERER__",
escape_js_string(&fp.webgl_renderer),
),
("__EOKA_NOISE_SEED__", noise_seed.to_string()),
(
"__EOKA_SPEECH_VOICES__",
serde_json::to_string(&fp.voices).unwrap_or_else(|_| "[]".to_string()),
),
("__EOKA_MEDIA_DEVICES__", fp.media_devices_json()),
("__EOKA_UI_CHROME__", fp.ui_chrome_height().to_string()),
(
"__EOKA_FONTS__",
serde_json::to_string(&fp.fonts).unwrap_or_else(|_| "[]".to_string()),
),
(
"__EOKA_GENERIC_FONTS__",
serde_json::to_string(GENERIC_FONTS).unwrap_or_else(|_| "[]".to_string()),
),
("__EOKA_KEYBOARD_MAP__", fp.keyboard_map_json()),
];
placeholders
.into_iter()
.fold(script, |acc, (placeholder, value)| {
acc.replace(placeholder, &value)
})
}
pub fn build_evasion_script(config: &StealthConfig) -> String {
let fp = Fingerprint::resolve(config.user_agent.as_deref(), config.timezone.as_deref());
build_evasion_script_for(config, &fp)
}
#[cfg(test)]
mod tests;