11use crate :: config:: SentryConfig ;
2+ use http:: header:: AUTHORIZATION ;
23use sentry:: protocol:: Event ;
34use sentry:: { ClientInitGuard , ClientOptions , TransactionContext } ;
45use std:: sync:: Arc ;
@@ -20,6 +21,10 @@ pub fn init() -> Option<ClientInitGuard> {
2021 }
2122 } ;
2223
24+ Some ( sentry:: init ( options ( config) ) )
25+ }
26+
27+ fn options ( config : SentryConfig ) -> ClientOptions {
2328 let traces_sampler = move |ctx : & TransactionContext | -> f32 {
2429 if let Some ( sampled) = ctx. sampled ( ) {
2530 return if sampled { 1.0 } else { 0.0 } ;
@@ -53,16 +58,22 @@ pub fn init() -> Option<ClientInitGuard> {
5358 } ;
5459
5560 let before_send = |mut event : Event < ' static > | {
56- // Remove cookies from the request to avoid sending sensitive
57- // information like the `cargo_session`.
5861 if let Some ( request) = & mut event. request {
62+ // Remove cookies from the request to avoid sending sensitive information like the
63+ // `cargo_session`.
5964 request. cookies . take ( ) ;
65+
66+ // Also remove `Authorization`, just so it never even gets sent to Sentry, even if
67+ // they're redacting it downstream.
68+ request
69+ . headers
70+ . retain ( |name, _value| AUTHORIZATION != name. as_str ( ) ) ;
6071 }
6172
6273 Some ( event)
6374 } ;
6475
65- let opts = ClientOptions {
76+ ClientOptions {
6677 auto_session_tracking : true ,
6778 dsn : config. dsn ,
6879 environment : config. environment . map ( Into :: into) ,
@@ -71,7 +82,86 @@ pub fn init() -> Option<ClientInitGuard> {
7182 session_mode : sentry:: SessionMode :: Request ,
7283 traces_sampler : Some ( Arc :: new ( traces_sampler) ) ,
7384 ..Default :: default ( )
85+ }
86+ }
87+
88+ #[ cfg( test) ]
89+ mod tests {
90+ use super :: * ;
91+
92+ use sentry:: {
93+ capture_error,
94+ protocol:: { Request , SpanStatus , Url } ,
95+ start_transaction,
7496 } ;
7597
76- Some ( sentry:: init ( opts) )
98+ #[ test]
99+ fn test_redaction ( ) -> anyhow:: Result < ( ) > {
100+ let req = Request {
101+ url : Some ( Url :: parse ( "https://crates.io/api/v1/foo" ) ?) ,
102+ method : Some ( "GET" . into ( ) ) ,
103+ data : None ,
104+ cookies : Some ( "cargo_session=foobar" . into ( ) ) ,
105+ headers : [
106+ ( "Authorization" , "secret" ) ,
107+ ( "authorization" , "another secret" ) ,
108+ ( "Accept" , "application/json" ) ,
109+ ]
110+ . into_iter ( )
111+ . map ( |( k, v) | ( k. to_string ( ) , v. to_string ( ) ) )
112+ . collect ( ) ,
113+ query_string : None ,
114+ env : Default :: default ( ) ,
115+ } ;
116+ let err = std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , "error" ) ;
117+
118+ let opts = options ( SentryConfig :: default ( ) ) ;
119+ let event_req = req. clone ( ) ;
120+ let mut events = sentry:: test:: with_captured_events_options (
121+ move || {
122+ let scope_req = event_req. clone ( ) ;
123+ sentry:: configure_scope ( |scope| {
124+ // This is straight up replicated from the implementation of SentryHttpFuture,
125+ // and is how requests are attached by the Tower middleware.
126+ scope. add_event_processor ( move |mut event| {
127+ if event. request . is_none ( ) {
128+ event. request = Some ( scope_req. clone ( ) ) ;
129+ }
130+ Some ( event)
131+ } ) ;
132+ } ) ;
133+
134+ let ctx = TransactionContext :: new ( "test" , "http.server" ) ;
135+ let txn = start_transaction ( ctx) ;
136+ txn. set_request ( event_req) ;
137+ txn. set_status ( SpanStatus :: InternalError ) ;
138+
139+ capture_error ( & err) ;
140+
141+ txn. finish ( ) ;
142+ } ,
143+ opts,
144+ ) ;
145+
146+ // OK, so there should be exactly one event, and it should match `req` except that its
147+ // cookies are removed and its headers have been cleaned of all Authorization values. Let's
148+ // see what we actually have.
149+ assert_eq ! ( events. len( ) , 1 ) ;
150+ let event = assert_some ! ( events. pop( ) ) ;
151+ let event_req = assert_some ! ( event. request) ;
152+
153+ // Things that shouldn't change.
154+ assert_eq ! ( & req. url, & event_req. url) ;
155+ assert_eq ! ( & req. method, & event_req. method) ;
156+ assert_eq ! ( & req. data, & event_req. data) ;
157+ assert_eq ! ( & req. query_string, & event_req. query_string) ;
158+ assert_eq ! ( & req. env, & event_req. env) ;
159+
160+ // Things that should.
161+ assert_none ! ( & event_req. cookies) ;
162+ assert_eq ! ( event_req. headers. len( ) , 1 ) ;
163+ assert_some ! ( event_req. headers. get( "Accept" ) ) ;
164+
165+ Ok ( ( ) )
166+ }
77167}
0 commit comments