use sentry::protocol::Event;
use crate::privacy::{filter_value, PrivacyFilter};
pub fn scrub_event(mut event: Event<'static>, filter: &PrivacyFilter) -> Option<Event<'static>> {
if let Some(ref mut msg) = event.message {
*msg = scrub_string(msg, filter);
}
for exception in event.exception.iter_mut() {
if let Some(ref mut v) = exception.value {
*v = scrub_string(v, filter);
}
if let Some(ref mut stacktrace) = exception.stacktrace {
for frame in stacktrace.frames.iter_mut() {
if let Some(ref mut f) = frame.filename {
*f = scrub_string(f, filter);
}
if let Some(ref mut f) = frame.function {
*f = scrub_string(f, filter);
}
}
}
}
Some(event)
}
fn scrub_string(s: &str, filter: &PrivacyFilter) -> String {
let mut v = serde_json::Value::String(s.to_string());
filter_value(&mut v, filter);
match v {
serde_json::Value::String(out) => out,
_ => s.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use sentry::protocol::{Exception, Frame, Stacktrace, Values};
fn filter() -> PrivacyFilter {
PrivacyFilter::new(&[])
}
fn panic_event(message: &str) -> Event<'static> {
Event {
exception: Values::from(vec![Exception {
ty: "panic".into(),
value: Some(message.to_string()),
..Default::default()
}]),
..Default::default()
}
}
#[test]
fn test_scrub_redacts_aws_key_in_panic_message() {
let filter = filter();
let fake_aws = format!("{}{}", "AKIA", "1234567890ABCDEF"); let ev = panic_event(&format!("boom: {fake_aws} leaked"));
let scrubbed = scrub_event(ev, &filter).unwrap();
let value = scrubbed.exception[0].value.as_ref().unwrap();
assert!(value.contains("[AWS_KEY:AKIA***]"), "got: {value}");
assert!(!value.contains(&fake_aws));
}
#[test]
fn test_scrub_redacts_github_pat_in_frame_filename() {
let filter = filter();
let mut ev = panic_event("boom");
ev.exception[0].stacktrace = Some(Stacktrace {
frames: vec![Frame {
filename: Some("/tmp/ghp_abcdefghijklmnopqrstuvwxyz0123456789/main.rs".into()),
function: Some("my_fn".into()),
..Default::default()
}],
..Default::default()
});
let scrubbed = scrub_event(ev, &filter).unwrap();
let fname = scrubbed.exception[0].stacktrace.as_ref().unwrap().frames[0]
.filename
.as_ref()
.unwrap();
assert!(fname.contains("[GITHUB_TOKEN:ghp_***]"), "got: {fname}");
}
#[test]
fn test_scrub_redacts_bearer_token_in_message() {
let filter = filter();
let ev = panic_event("auth failed for Bearer eyJhbGciOiJIUzI1NiJ9.payload.sig");
let scrubbed = scrub_event(ev, &filter).unwrap();
let value = scrubbed.exception[0].value.as_ref().unwrap();
assert!(
!value.contains("eyJhbGciOiJIUzI1NiJ9.payload.sig"),
"got: {value}"
);
}
#[test]
fn test_scrub_preserves_event_when_nothing_matches() {
let filter = filter();
let ev = panic_event("ordinary panic with no secrets");
let scrubbed = scrub_event(ev, &filter).unwrap();
assert_eq!(
scrubbed.exception[0].value.as_ref().unwrap(),
"ordinary panic with no secrets"
);
}
}