rustenium_identity/script/
mod.rs1use crate::identity::{Browser, Identity, Os};
2
3const PROPERTY_MODIFIER_JS: &str = include_str!("../../js/property_modifier.js");
4const MAIN_WORLD_JS: &str = include_str!("../../js/main_world.js");
5const BROWSER_CHROME_JS: &str = include_str!("../../js/browser_chrome.js");
6const BROWSER_SAFARI_JS: &str = include_str!("../../js/browser_safari.js");
7const BROWSER_EDGE_JS: &str = include_str!("../../js/browser_edge.js");
8
9pub fn build_stealth_script(identity: &Identity) -> String {
11 let lang0 = identity
12 .language
13 .first()
14 .map(|s| s.as_str())
15 .unwrap_or("en-US");
16 let languages_json =
17 serde_json::to_string(&identity.language).unwrap_or_else(|_| r#"["en-US"]"#.into());
18 let max_touch = if identity.has_touch { 5 } else { 0 };
19 let avail_height = identity.screen.logical_height.saturating_sub(40);
20 let charging = !identity.has_battery || identity.has_mouse;
21 let charging_time = if identity.has_battery { "Infinity" } else { "0" };
22 let discharging_time = if identity.has_battery { "7200" } else { "Infinity" };
23 let battery_percentage: u8 = rand::random_range(20..=100);
24
25 let history_block = match identity.history_count {
26 Some(count) => format!(
27 r#"(function() {{
28 var historyCount = {};
29 for (var n = 0; n < historyCount; ++n) {{
30 if (window.history.length >= historyCount) break;
31 window.history.pushState(null, '');
32 }}
33}})()"#,
34 count
35 ),
36 None => String::new(),
37 };
38
39 let is_ios = matches!(identity.os, Os::Ios);
41 let browser_block = if is_ios {
42 BROWSER_SAFARI_JS.to_string()
43 } else {
44 match identity.browser {
45 Browser::Chrome => BROWSER_CHROME_JS.to_string(),
46 Browser::Safari => BROWSER_SAFARI_JS.to_string(),
47 Browser::Edge => BROWSER_EDGE_JS.to_string(),
48 }
49 };
50
51 let script = MAIN_WORLD_JS
52 .replace("{{NAVIGATOR_PLATFORM}}", identity.platform.navigator_platform.as_str())
53 .replace("{{HARDWARE_CONCURRENCY}}", &identity.hardware_concurrency.to_string())
54 .replace("{{MEMORY}}", &identity.memory.to_string())
55 .replace("{{LANGUAGES_JSON}}", &languages_json)
56 .replace("{{LANGUAGE_0}}", lang0)
57 .replace("{{MAX_TOUCH_POINTS}}", &max_touch.to_string())
58 .replace("{{LOGICAL_WIDTH}}", &identity.screen.logical_width.to_string())
59 .replace("{{AVAIL_HEIGHT}}", &avail_height.to_string())
60 .replace("{{WEBGL_VENDOR}}", &escape_js(&identity.gpu.webgl_vendor))
61 .replace("{{WEBGL_RENDERER}}", &escape_js(&identity.gpu.webgl_renderer))
62 .replace("{{HISTORY_BLOCK}}", &history_block)
63 .replace("{{CHARGING}}", &charging.to_string())
64 .replace("{{CHARGING_TIME}}", charging_time)
65 .replace("{{DISCHARGING_TIME}}", discharging_time)
66 .replace("{{BATTERY_PERCENTAGE}}", &battery_percentage.to_string())
67 .replace("{{BROWSER_BLOCK}}", &browser_block);
68
69 format!("{}\n{}", PROPERTY_MODIFIER_JS, script)
70}
71
72fn escape_js(s: &str) -> String {
73 s.replace('\\', "\\\\")
74 .replace('"', "\\\"")
75 .replace('\n', "\\n")
76}