@@ -362,17 +362,90 @@ void http_request::set_file_cleanup_callback(file_cleanup_callback_ptr callback)
362362 impl_->file_cleanup_callback_ = callback;
363363}
364364
365+ void http_request::set_expose_credentials_in_logs (bool v) {
366+ impl_->expose_credentials_in_logs_ = v;
367+ }
368+
369+ namespace {
370+
371+ // TASK-057: Authorization-class header names whose values carry
372+ // credential material (Basic / Digest / Bearer payloads). Matched
373+ // case-insensitively against the keys in the Headers / Footers maps
374+ // so that the redaction policy is robust to header-case variation.
375+ bool is_authorization_header_key (std::string_view key) noexcept {
376+ constexpr std::string_view kAuth = " Authorization" ;
377+ constexpr std::string_view kProxyAuth = " Proxy-Authorization" ;
378+ if (key.size () != kAuth .size () && key.size () != kProxyAuth .size ()) {
379+ return false ;
380+ }
381+ auto ieq = [](std::string_view a, std::string_view b) noexcept {
382+ if (a.size () != b.size ()) return false ;
383+ for (std::size_t i = 0 ; i < a.size (); ++i) {
384+ const unsigned char ca = static_cast <unsigned char >(a[i]);
385+ const unsigned char cb = static_cast <unsigned char >(b[i]);
386+ if (std::tolower (ca) != std::tolower (cb)) return false ;
387+ }
388+ return true ;
389+ };
390+ return ieq (key, kAuth ) || ieq (key, kProxyAuth );
391+ }
392+
393+ constexpr std::string_view kRedacted = " <redacted>" ;
394+
395+ // TASK-057: emit a Headers/Footers map with Authorization-class header
396+ // values replaced by the fixed redaction token. Mirrors the wire shape
397+ // of http::dump_header_map(header_view_map) for non-authorization
398+ // entries so the on-the-wire diagnostic format is unchanged.
399+ void dump_header_map_redacted (std::ostream& os, const std::string& prefix,
400+ const http::header_view_map& map) {
401+ if (map.empty ()) return ;
402+ os << " " << prefix << " [" ;
403+ for (const auto & kv : map) {
404+ os << kv.first << " :\" " ;
405+ if (is_authorization_header_key (kv.first )) {
406+ os << kRedacted ;
407+ } else {
408+ os << kv.second ;
409+ }
410+ os << " \" " ;
411+ }
412+ os << " ]" << std::endl;
413+ }
414+
415+ // TASK-057: cookie values are credential material by default (session
416+ // IDs, CSRF tokens, JWTs); redaction is unconditional on the keys
417+ // (which remain visible for log triage).
418+ void dump_cookie_map_redacted (std::ostream& os, const std::string& prefix,
419+ const http::header_view_map& map) {
420+ if (map.empty ()) return ;
421+ os << " " << prefix << " [" ;
422+ for (const auto & kv : map) {
423+ os << kv.first << " :\" " << kRedacted << " \" " ;
424+ }
425+ os << " ]" << std::endl;
426+ }
427+
428+ } // namespace
429+
365430std::ostream& operator << (std::ostream& os, const http_request& r) {
431+ const bool expose = r.impl_ ->expose_credentials_in_logs_ ;
366432 os << r.get_method () << " Request [" ;
367- // TASK-034: get_user/get_pass are unconditional; they return empty
368- // on HAVE_BAUTH-off builds, so the dump prints two empty quoted
369- // strings in that case (harmless).
370- os << " user:\" " << r.get_user () << " \" pass:\" " << r.get_pass () << " \" " ;
433+ if (expose) {
434+ os << " user:\" " << r.get_user () << " \" pass:\" " << r.get_pass () << " \" " ;
435+ } else {
436+ os << " user:\" " << r.get_user () << " \" pass:\" " << kRedacted << " \" " ;
437+ }
371438 os << " ] path:\" " << r.get_path () << " \" " << std::endl;
372439
373- http::dump_header_map (os, " Headers" , r.get_headers ());
374- http::dump_header_map (os, " Footers" , r.get_footers ());
375- http::dump_header_map (os, " Cookies" , r.get_cookies ());
440+ if (expose) {
441+ http::dump_header_map (os, " Headers" , r.get_headers ());
442+ http::dump_header_map (os, " Footers" , r.get_footers ());
443+ http::dump_header_map (os, " Cookies" , r.get_cookies ());
444+ } else {
445+ dump_header_map_redacted (os, " Headers" , r.get_headers ());
446+ dump_header_map_redacted (os, " Footers" , r.get_footers ());
447+ dump_cookie_map_redacted (os, " Cookies" , r.get_cookies ());
448+ }
376449 http::dump_arg_map (os, " Query Args" , r.get_args ());
377450
378451 os << " Version [ " << r.get_version () << " ] Requestor [ " << r.get_requestor ()
0 commit comments