2929}
3030
3131
32- def clean_for_csv (value ):
33- """Clean a value for safe CSV output """
32+ def safe_str (value ):
33+ """Convert value to string, handling encoding issues only when they occur """
3434 if value is None :
3535 return ""
3636
37- # Convert to string and handle encoding issues
3837 try :
39- str_value = str (value )
40- # Replace problematic characters
41- str_value = str_value .replace ("\x00 " , "" ) # Remove null bytes
42- str_value = str_value .replace ("\r " , "\\ r" ) # Escape carriage returns
43- str_value = str_value .replace ("\n " , "\\ n" ) # Escape newlines
44- return str_value
38+ return str (value )
4539 except (UnicodeDecodeError , UnicodeEncodeError ):
46- return "[ENCODING ERROR]"
40+ # Only if encoding fails, try with error replacement
41+ try :
42+ if isinstance (value , bytes ):
43+ return value .decode ("utf-8" , errors = "replace" )
44+ return repr (value ) # Fall back to repr() which handles anything
45+ except Exception :
46+ return "[ENCODING ERROR]"
4747
4848
4949# Open CSV file for writing requests with proper encoding and quoting
@@ -120,22 +120,22 @@ def response(flow):
120120 reason = flow .response .reason or ""
121121 content_type = flow .response .headers .get ("content-type" , "" )
122122
123- # Write row to CSV with cleaned data
123+ # Write row to CSV with safe string conversion
124124 writer .writerow (
125125 [
126- clean_for_csv (timestamp ),
127- clean_for_csv (method ),
128- clean_for_csv (url ),
129- clean_for_csv (host ),
130- clean_for_csv (path ),
131- clean_for_csv (status_code ),
132- clean_for_csv (reason ),
133- clean_for_csv (request_size ),
134- clean_for_csv (response_size ),
135- clean_for_csv (content_type ),
136- clean_for_csv (duration_ms ),
137- clean_for_csv (request_headers ),
138- clean_for_csv (response_headers ),
126+ safe_str (timestamp ),
127+ safe_str (method ),
128+ safe_str (url ),
129+ safe_str (host ),
130+ safe_str (path ),
131+ safe_str (status_code ),
132+ safe_str (reason ),
133+ safe_str (request_size ),
134+ safe_str (response_size ),
135+ safe_str (content_type ),
136+ safe_str (duration_ms ),
137+ safe_str (request_headers ),
138+ safe_str (response_headers ),
139139 "" , # No error for successful requests
140140 ]
141141 )
@@ -159,10 +159,10 @@ def response(flow):
159159
160160 writer .writerow (
161161 [
162- clean_for_csv (datetime .now ().isoformat ()),
163- clean_for_csv (error_method ),
162+ safe_str (datetime .now ().isoformat ()),
163+ safe_str (error_method ),
164164 "" , # Empty URL for errors
165- clean_for_csv (error_host ),
165+ safe_str (error_host ),
166166 "" , # Empty path for errors
167167 "" , # Empty status code for errors
168168 "" , # Empty reason for errors
@@ -172,7 +172,7 @@ def response(flow):
172172 "" , # Empty duration for errors
173173 "" , # Empty request headers for errors
174174 "" , # Empty response headers for errors
175- clean_for_csv (
175+ safe_str (
176176 SecretDetector .mask_secrets (str (e )).masked_text
177177 ), # Error message
178178 ]
0 commit comments