http_client_vcr/
form_data.rs1use std::collections::HashMap;
2
3pub fn parse_form_data(data: &str) -> HashMap<String, String> {
5 let mut params = HashMap::new();
6
7 for pair in data.split('&') {
8 if let Some((key, value)) = pair.split_once('=') {
9 let decoded_key = urlencoding::decode(key).unwrap_or_else(|_| key.into());
11 let decoded_value = urlencoding::decode(value).unwrap_or_else(|_| value.into());
12 params.insert(decoded_key.to_string(), decoded_value.to_string());
13 }
14 }
15
16 params
17}
18
19pub fn encode_form_data(params: &HashMap<String, String>) -> String {
21 params
22 .iter()
23 .map(|(key, value)| {
24 format!(
25 "{}={}",
26 urlencoding::encode(key),
27 urlencoding::encode(value)
28 )
29 })
30 .collect::<Vec<_>>()
31 .join("&")
32}
33
34pub fn find_credential_fields(params: &HashMap<String, String>) -> Vec<(String, String)> {
36 let mut credentials = Vec::new();
37
38 let credential_patterns = [
40 "username",
42 "user",
43 "login",
44 "email",
45 "username_or_email",
46 "user_name",
47 "password",
49 "pass",
50 "passwd",
51 "pwd",
52 "secret",
53 "_token",
55 "session",
57 "sessionid",
58 "sid",
59 "auth",
60 "authorization",
61 "api_key",
63 "apikey",
64 "key",
65 "client_secret",
66 "access_token",
67 "refresh_token",
68 ];
69
70 for (key, value) in params {
71 let key_lower = key.to_lowercase();
72
73 for pattern in &credential_patterns {
75 if key_lower.contains(pattern) {
76 credentials.push((key.clone(), value.clone()));
77 break;
78 }
79 }
80
81 if value.len() > 10 && value.chars().all(|c| c.is_alphanumeric()) {
83 credentials.push((key.clone(), value.clone()));
85 }
86 }
87
88 credentials
89}
90
91pub fn filter_form_data(data: &str, replacement_pattern: &str) -> String {
93 let mut params = parse_form_data(data);
94 let credentials = find_credential_fields(¶ms);
95
96 for (key, _) in credentials {
98 if let Some(value) = params.get_mut(&key) {
99 *value = format!("{replacement_pattern}_{}", key.to_uppercase());
100 }
101 }
102
103 encode_form_data(¶ms)
104}
105
106pub fn analyze_form_data(data: &str) -> FormDataAnalysis {
108 let params = parse_form_data(data);
109 let credentials = find_credential_fields(¶ms);
110
111 FormDataAnalysis {
112 total_fields: params.len(),
113 credential_fields: credentials,
114 all_fields: params,
115 }
116}
117
118#[derive(Debug)]
119pub struct FormDataAnalysis {
120 pub total_fields: usize,
121 pub credential_fields: Vec<(String, String)>,
122 pub all_fields: HashMap<String, String>,
123}
124
125impl FormDataAnalysis {
126 pub fn print_summary(&self) {
128 println!("Form Data Analysis:");
129 println!(" Total fields: {}", self.total_fields);
130 println!(
131 " Credential fields found: {}",
132 self.credential_fields.len()
133 );
134
135 if !self.credential_fields.is_empty() {
136 println!(" Sensitive fields:");
137 for (key, value) in &self.credential_fields {
138 let preview = if value.len() > 20 {
139 format!("{}...", &value[..20])
140 } else {
141 value.clone()
142 };
143 println!(" {key}: {preview}");
144 }
145 }
146
147 println!(" All fields:");
148 for (key, value) in &self.all_fields {
149 let preview = if value.len() > 50 {
150 format!("{}...", &value[..50])
151 } else {
152 value.clone()
153 };
154 println!(" {key}: {preview}");
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn test_parse_form_data() {
165 let data = "username=testuser&password=secret123&csrf_token=abc123";
166 let params = parse_form_data(data);
167
168 assert_eq!(params.get("username"), Some(&"testuser".to_string()));
169 assert_eq!(params.get("password"), Some(&"secret123".to_string()));
170 assert_eq!(params.get("csrf_token"), Some(&"abc123".to_string()));
171 }
172
173 #[test]
174 fn test_find_credential_fields() {
175 let mut params = HashMap::new();
176 params.insert("username".to_string(), "testuser".to_string());
177 params.insert("password".to_string(), "secret123".to_string());
178 params.insert("normal_field".to_string(), "value".to_string());
179
180 let credentials = find_credential_fields(¶ms);
181 assert_eq!(credentials.len(), 2);
182
183 let keys: Vec<&String> = credentials.iter().map(|(k, _)| k).collect();
184 assert!(keys.contains(&&"username".to_string()));
185 assert!(keys.contains(&&"password".to_string()));
186 }
187
188 #[test]
189 fn test_filter_form_data() {
190 let data = "username=testuser&password=secret123&normal=value";
191 let filtered = filter_form_data(data, "[FILTERED]");
192
193 assert!(filtered.contains("%5BFILTERED%5D_USERNAME"));
195 assert!(filtered.contains("%5BFILTERED%5D_PASSWORD"));
196 assert!(filtered.contains("normal=value"));
197 }
198}