@@ -203,25 +203,32 @@ impl<F: forge_app::FileWriterInfra + 'static> ForgeHttpInfra<F> {
203203 reqwest:: header:: CONNECTION ,
204204 HeaderValue :: from_static ( "keep-alive" ) ,
205205 ) ;
206- debug ! ( headers = ?Self :: sanitize_headers( & headers) , "Request Headers" ) ;
206+ debug ! ( headers = ?sanitize_headers( & headers) , "Request Headers" ) ;
207207 headers
208208 }
209+ }
209210
210- fn sanitize_headers ( headers : & HeaderMap ) -> HeaderMap {
211- let sensitive_headers = [ AUTHORIZATION . as_str ( ) ] ;
212- headers
213- . iter ( )
214- . map ( |( name, value) | {
215- let name_str = name. as_str ( ) . to_lowercase ( ) ;
216- let value_str = if sensitive_headers. contains ( & name_str. as_str ( ) ) {
217- HeaderValue :: from_static ( "[REDACTED]" )
218- } else {
219- value. clone ( )
220- } ;
221- ( name. clone ( ) , value_str)
222- } )
223- . collect ( )
224- }
211+ /// Sanitizes headers for logging by redacting sensitive values like
212+ /// authorization tokens and API keys.
213+ pub fn sanitize_headers ( headers : & HeaderMap ) -> HeaderMap {
214+ let sensitive_headers = [
215+ AUTHORIZATION . as_str ( ) ,
216+ "x-api-key" ,
217+ "x-goog-api-key" ,
218+ "api-key" ,
219+ ] ;
220+ headers
221+ . iter ( )
222+ . map ( |( name, value) | {
223+ let name_str = name. as_str ( ) . to_lowercase ( ) ;
224+ let value_str = if sensitive_headers. contains ( & name_str. as_str ( ) ) {
225+ HeaderValue :: from_static ( "[REDACTED]" )
226+ } else {
227+ value. clone ( )
228+ } ;
229+ ( name. clone ( ) , value_str)
230+ } )
231+ . collect ( )
225232}
226233
227234impl < F : forge_app:: FileWriterInfra + ' static > ForgeHttpInfra < F > {
@@ -489,4 +496,47 @@ mod tests {
489496 assert_eq ! ( writes[ 0 ] . 0 , debug_path) ;
490497 assert_eq ! ( writes[ 0 ] . 1 , body) ;
491498 }
499+
500+ #[ test]
501+ fn test_sanitize_headers_redacts_sensitive_values ( ) {
502+ use reqwest:: header:: HeaderValue ;
503+
504+ let mut headers = HeaderMap :: new ( ) ;
505+ headers. insert (
506+ AUTHORIZATION ,
507+ HeaderValue :: from_static ( "Bearer secret-api-key" ) ,
508+ ) ;
509+ headers. insert ( "x-api-key" , HeaderValue :: from_static ( "another-secret" ) ) ;
510+ headers. insert ( "x-goog-api-key" , HeaderValue :: from_static ( "google-secret" ) ) ;
511+ headers. insert ( "api-key" , HeaderValue :: from_static ( "generic-secret" ) ) ;
512+ headers. insert ( "x-title" , HeaderValue :: from_static ( "forge" ) ) ;
513+ headers. insert ( "content-type" , HeaderValue :: from_static ( "application/json" ) ) ;
514+
515+ let sanitized = sanitize_headers ( & headers) ;
516+
517+ assert_eq ! (
518+ sanitized. get( "authorization" ) ,
519+ Some ( & HeaderValue :: from_static( "[REDACTED]" ) )
520+ ) ;
521+ assert_eq ! (
522+ sanitized. get( "x-api-key" ) ,
523+ Some ( & HeaderValue :: from_static( "[REDACTED]" ) )
524+ ) ;
525+ assert_eq ! (
526+ sanitized. get( "x-goog-api-key" ) ,
527+ Some ( & HeaderValue :: from_static( "[REDACTED]" ) )
528+ ) ;
529+ assert_eq ! (
530+ sanitized. get( "api-key" ) ,
531+ Some ( & HeaderValue :: from_static( "[REDACTED]" ) )
532+ ) ;
533+ assert_eq ! (
534+ sanitized. get( "x-title" ) ,
535+ Some ( & HeaderValue :: from_static( "forge" ) )
536+ ) ;
537+ assert_eq ! (
538+ sanitized. get( "content-type" ) ,
539+ Some ( & HeaderValue :: from_static( "application/json" ) )
540+ ) ;
541+ }
492542}
0 commit comments