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 HARDWARE_JS: &str = include_str!("../../js/hardware.js");
6const WORKER_SCOPE_JS: &str = include_str!("../../js/worker_scope.js");
7const BROWSER_CHROME_JS: &str = include_str!("../../js/browser_chrome.js");
8const BROWSER_SAFARI_JS: &str = include_str!("../../js/browser_safari.js");
9const BROWSER_EDGE_JS: &str = include_str!("../../js/browser_edge.js");
10
11pub fn build_stealth_script(identity: &Identity) -> String {
13 let charging = !identity.has_battery || identity.has_mouse;
14 let charging_time = if identity.has_battery { "Infinity" } else { "0" };
15 let discharging_time = if identity.has_battery { "7200" } else { "Infinity" };
16 let battery_percentage: u8 = rand::random_range(20..=100);
17
18 let history_block = match identity.history_count {
25 Some(count) => format!(
26 r#"(function() {{
27 var historyCount = {};
28 for (var n = 0; n < historyCount; ++n) {{
29 if (window.history.length >= historyCount) break;
30 window.history.pushState(null, '');
31 }}
32}})();"#,
33 count
34 ),
35 None => String::new(),
36 };
37
38 let is_ios = matches!(identity.os, Os::Ios);
40 let browser_block = if is_ios {
41 BROWSER_SAFARI_JS.to_string()
42 } else {
43 match identity.browser {
44 Browser::Chrome => BROWSER_CHROME_JS.to_string(),
45 Browser::Safari => BROWSER_SAFARI_JS.to_string(),
46 Browser::Edge => BROWSER_EDGE_JS.to_string(),
47 }
48 };
49
50 let shared: [(&str, String); 6] = [
54 ("{{NAVIGATOR_PLATFORM}}", escape_js(identity.platform.navigator_platform.as_str())),
55 ("{{HARDWARE_CONCURRENCY}}", identity.hardware_concurrency.to_string()),
56 ("{{MEMORY}}", identity.memory.to_string()),
57 ("{{WEBGL_VENDOR}}", escape_js(&identity.gpu.webgl_vendor)),
58 ("{{WEBGL_RENDERER}}", escape_js(&identity.gpu.webgl_renderer)),
59 ("{{USER_AGENT}}", crate::ua::build_user_agent(identity).map(|s| escape_js(&s)).unwrap_or_default()),
61 ];
62 let apply = |src: &str| {
63 shared.iter().fold(src.to_string(), |acc, (key, val)| acc.replace(key, val))
64 };
65
66 let script = apply(MAIN_WORLD_JS)
67 .replace("{{HISTORY_BLOCK}}", &history_block)
68 .replace("{{CHARGING}}", &charging.to_string())
69 .replace("{{CHARGING_TIME}}", charging_time)
70 .replace("{{DISCHARGING_TIME}}", discharging_time)
71 .replace("{{BATTERY_PERCENTAGE}}", &battery_percentage.to_string())
72 .replace("{{BROWSER_BLOCK}}", &browser_block);
73
74 let hardware = HARDWARE_JS.replace("{{FP_SEED}}", &fingerprint_seed(identity).to_string());
77
78 format!(
84 "(function() {{\n{}\n;\n{}\n;\n{}\n}})();",
85 PROPERTY_MODIFIER_JS, script, hardware
86 )
87}
88
89pub fn build_worker_script(identity: &Identity) -> String {
103 let shared: [(&str, String); 6] = [
104 ("{{NAVIGATOR_PLATFORM}}", escape_js(identity.platform.navigator_platform.as_str())),
105 ("{{HARDWARE_CONCURRENCY}}", identity.hardware_concurrency.to_string()),
106 ("{{MEMORY}}", identity.memory.to_string()),
107 ("{{WEBGL_VENDOR}}", escape_js(&identity.gpu.webgl_vendor)),
108 ("{{WEBGL_RENDERER}}", escape_js(&identity.gpu.webgl_renderer)),
109 ("{{USER_AGENT}}", crate::ua::build_user_agent(identity).map(|s| escape_js(&s)).unwrap_or_default()),
110 ];
111 let worker_scope = shared
112 .iter()
113 .fold(WORKER_SCOPE_JS.to_string(), |acc, (key, val)| acc.replace(key, val));
114
115 format!(
116 "(function() {{\n{}\n;\n{}\n}})();",
117 PROPERTY_MODIFIER_JS, worker_scope
118 )
119}
120
121pub(crate) fn fingerprint_seed(identity: &Identity) -> u32 {
125 let material = format!(
126 "{:?}|{}|{}|{}|{}|{}x{}|{}|{}|{}",
127 identity.os,
128 identity.os_version,
129 identity.gpu.webgl_vendor,
130 identity.gpu.webgl_renderer,
131 identity.id.unwrap_or(0),
132 identity.screen.original_width,
133 identity.screen.original_height,
134 identity.hardware_concurrency,
135 identity.memory,
136 identity.platform.navigator_platform.as_str(),
137 );
138 let mut h: u32 = 0x811c_9dc5;
140 for b in material.as_bytes() {
141 h ^= *b as u32;
142 h = h.wrapping_mul(0x0100_0193);
143 }
144 h
145}
146
147fn escape_js(s: &str) -> String {
148 s.replace('\\', "\\\\")
149 .replace('"', "\\\"")
150 .replace('\n', "\\n")
151}