1use crate::profile::ChromeProfile;
2use http::header::{HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, USER_AGENT};
3
4#[allow(dead_code)]
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum RequestContext {
8 Navigate,
10 Xhr,
12 Form,
14 Iframe,
16 NoCorsScript,
18 NoCorsStyle,
20 NoCorsImage,
22 NoCorsFont,
24 NoCorsMedia,
26 Worker,
28 ServiceWorker,
30 Prefetch,
32}
33
34pub fn inject_chrome_headers(
50 headers: &mut HeaderMap,
51 profile: &ChromeProfile,
52 sec_fetch_site: &str,
53 is_initial_navigation: bool,
54 context: RequestContext,
55 accept_ch: bool,
56 referer: Option<&str>,
57) {
58 if let Ok(val) = HeaderValue::from_str(&profile.headers.sec_ch_ua) {
61 headers.insert("sec-ch-ua", val);
62 }
63 headers.insert("sec-ch-ua-mobile", HeaderValue::from_static("?0"));
64 if let Ok(val) = HeaderValue::from_str(&profile.headers.sec_ch_ua_platform) {
65 headers.insert("sec-ch-ua-platform", val);
66 }
67 if accept_ch {
69 if let Ok(val) = HeaderValue::from_str(&profile.headers.sec_ch_ua_platform_version) {
70 headers.insert("sec-ch-ua-platform-version", val);
71 }
72 }
73
74 headers.insert("upgrade-insecure-requests", HeaderValue::from_static("1"));
76 if let Ok(val) = HeaderValue::from_str(&profile.headers.user_agent) {
77 headers.insert(USER_AGENT, val);
78 }
79
80 if let Ok(val) = HeaderValue::from_str(sec_fetch_site) {
82 headers.insert("sec-fetch-site", val);
83 }
84 let (mode, dest, accept_val) = match context {
85 RequestContext::Navigate | RequestContext::Form => ("navigate", "document", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"),
86 RequestContext::Iframe => ("navigate", "iframe", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"),
87 RequestContext::Xhr => ("cors", "empty", "*/*"),
88 RequestContext::NoCorsScript => ("no-cors", "script", "*/*"),
89 RequestContext::NoCorsStyle => ("no-cors", "style", "text/css,*/*;q=0.1"),
90 RequestContext::NoCorsImage => ("no-cors", "image", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"),
91 RequestContext::NoCorsFont => ("no-cors", "font", "*/*"),
92 RequestContext::NoCorsMedia => ("no-cors", "video", "*/*"),
93 RequestContext::Worker => ("same-origin", "worker", "*/*"),
94 RequestContext::ServiceWorker => ("same-origin", "serviceworker", "*/*"),
95 RequestContext::Prefetch => ("no-cors", "empty", "*/*"),
96 };
97 headers.insert(ACCEPT, HeaderValue::from_static(accept_val));
98 headers.insert("sec-fetch-mode", HeaderValue::from_static(mode));
99
100 if is_initial_navigation
102 && (context == RequestContext::Navigate
103 || context == RequestContext::Form
104 || context == RequestContext::Iframe)
105 {
106 headers.insert("sec-fetch-user", HeaderValue::from_static("?1"));
107 }
108
109 headers.insert("sec-fetch-dest", HeaderValue::from_static(dest));
110
111 if let Some(r) = referer {
112 if let Ok(val) = HeaderValue::from_str(r) {
113 headers.insert(http::header::REFERER, val);
114 }
115 }
116
117 let encoding = if profile.headers.zstd_encoding {
119 "gzip, deflate, br, zstd"
120 } else {
121 "gzip, deflate, br"
122 };
123 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(encoding));
124 if let Ok(val) = HeaderValue::from_str(&profile.headers.accept_language) {
125 headers.insert(ACCEPT_LANGUAGE, val);
126 }
127
128 if profile.headers.include_priority_header {
130 headers.insert("priority", HeaderValue::from_static("u=0, i"));
131 }
132
133 for (name, value) in headers.iter_mut() {
138 if name == "cookie" || name == "authorization" {
139 value.set_sensitive(true);
140 }
141 }
142
143 }
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::profile::chrome_134::chrome_134_windows_x64;
154
155 #[test]
161 fn test_inject_chrome_headers_context_mapping() {
162 let profile = chrome_134_windows_x64();
163
164 let mut headers = HeaderMap::new();
167 inject_chrome_headers(
168 &mut headers,
169 &profile,
170 "same-origin",
171 true,
172 RequestContext::Navigate,
173 false,
174 None,
175 );
176 assert_eq!(
177 headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
178 "document"
179 );
180 assert_eq!(
181 headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
182 "navigate"
183 );
184 assert_eq!(
185 headers.get("sec-fetch-user").unwrap().to_str().unwrap(),
186 "?1"
187 );
188 assert!(headers.get("sec-ch-ua-platform-version").is_none());
189
190 let mut headers = HeaderMap::new();
193 inject_chrome_headers(
194 &mut headers,
195 &profile,
196 "cross-site",
197 false,
198 RequestContext::Xhr,
199 true,
200 None,
201 );
202 assert_eq!(
203 headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
204 "empty"
205 );
206 assert_eq!(
207 headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
208 "cors"
209 );
210 assert!(headers.get("sec-fetch-user").is_none());
211 assert_eq!(
212 headers
213 .get("sec-ch-ua-platform-version")
214 .unwrap()
215 .to_str()
216 .unwrap(),
217 "\"15.0.0\""
218 ); let mut headers = HeaderMap::new();
223 inject_chrome_headers(
224 &mut headers,
225 &profile,
226 "same-site",
227 false,
228 RequestContext::NoCorsImage,
229 false,
230 None,
231 );
232 assert_eq!(
233 headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
234 "image"
235 );
236 assert_eq!(
237 headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
238 "no-cors"
239 );
240 assert!(headers
241 .get("accept")
242 .unwrap()
243 .to_str()
244 .unwrap()
245 .contains("image/avif"));
246
247 let mut headers = HeaderMap::new();
250 inject_chrome_headers(
251 &mut headers,
252 &profile,
253 "same-origin",
254 false,
255 RequestContext::ServiceWorker,
256 false,
257 None,
258 );
259 assert_eq!(
260 headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
261 "serviceworker"
262 );
263 assert_eq!(
264 headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
265 "same-origin"
266 );
267 }
268
269 #[test]
275 fn test_inject_sensitive_headers_marked_properly() {
276 let profile = chrome_134_windows_x64();
277 let mut headers = HeaderMap::new();
278 headers.insert("cookie", HeaderValue::from_static("session=123"));
279 headers.insert("authorization", HeaderValue::from_static("Bearer token"));
280 headers.insert("host", HeaderValue::from_static("example.com"));
281
282 inject_chrome_headers(
283 &mut headers,
284 &profile,
285 "none",
286 true,
287 RequestContext::Navigate,
288 false,
289 None,
290 );
291
292 assert!(headers.get("cookie").unwrap().is_sensitive());
294 assert!(headers.get("authorization").unwrap().is_sensitive());
295
296 assert!(!headers.get("host").unwrap().is_sensitive());
298 }
299}