Skip to content
This repository has been archived by the owner on Dec 14, 2024. It is now read-only.

Commit

Permalink
fix: Redact emails in Sentry error messages (#208)
Browse files Browse the repository at this point in the history
  • Loading branch information
amaury1093 authored Jul 8, 2021
1 parent a2690f1 commit f73a209
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 18 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ async-recursion = "0.3"
async-smtp = "0.4"
check-if-email-exists = "0.8"
env_logger = "0.8"
lazy_static = "1.4"
log = "0.4"
regex = "1.5"
sentry = "0.22"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.8", features = ["macros"] }
Expand Down
13 changes: 9 additions & 4 deletions src/bin/heroku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use reacher_backend::{routes::create_routes, sentry_util::setup_sentry};
use reacher_backend::{
routes::create_routes,
sentry_util::{setup_sentry, CARGO_PKG_VERSION},
};
use std::{env, net::IpAddr};

/// Run a HTTP server using warp.
///
/// # Panics
///
/// If at least one of the environment variables:
/// - RCH_HTTP_HOST
/// is malformed, then the program will panic.
/// The program panics if at least one of the environment variables is
/// malformed:
/// - RCH_HTTP_HOST,
/// - PORT.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
env_logger::init();
Expand All @@ -43,6 +47,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
.unwrap_or(8080);
log::info!(target: "reacher", "Server is listening on {}:{}.", host, port);

log::info!(target: "reacher", "Running Reacher v{}", CARGO_PKG_VERSION);
warp::serve(routes).run((host, port)).await;
Ok(())
}
3 changes: 2 additions & 1 deletion src/routes/check_email/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ async fn check_email(body: EndpointRequest) -> Result<impl warp::Reply, warp::Re
};

// Log on Sentry the `is_reachable` field.
// FIXME We should definitely log this somehwere else than Sentry.
// We should definitely log this somewhere else than Sentry.
// TODO https://github.com/reacherhq/backend/issues/207
sentry_util::metrics(
format!("is_reachable={:?}", value.is_reachable),
retry_option,
Expand Down
55 changes: 43 additions & 12 deletions src/sentry_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,22 @@
//! Helper functions to send events to Sentry.
use crate::routes::check_email::post::RetryOption;
use lazy_static::lazy_static;
use regex::Regex;
use sentry::protocol::{Event, Level, Value};
use std::{collections::BTreeMap, env};

pub const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");

/// Setup logging and Sentry.
pub fn setup_sentry() -> sentry::ClientInitGuard {
log::info!(target: "reacher", "Running Reacher v{}", CARGO_PKG_VERSION);
lazy_static! {
// Regex to extract emails from a string.
static ref RE: Regex =
Regex::new(r"[a-zA-Z0-9._-]+@(?P<domain>[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)")
.unwrap();
}

/// Setup Sentry.
pub fn setup_sentry() -> sentry::ClientInitGuard {
// Use an empty string if we don't have any env variable for sentry. Sentry
// will just silently ignore.
let sentry = sentry::init(env::var("RCH_SENTRY_DSN").unwrap_or_else(|_| "".into()));
Expand All @@ -36,7 +43,7 @@ pub fn setup_sentry() -> sentry::ClientInitGuard {
sentry
}

/// If HEROKU_APP_NAME environment variable is set, add it to the sentry extra
/// If HEROKU_APP_NAME environment variable is set, add it to the sentry `extra`
/// properties.
fn add_heroku_app_name(mut extra: BTreeMap<String, Value>) -> BTreeMap<String, Value> {
if let Ok(heroku_app_name) = env::var("HEROKU_APP_NAME") {
Expand All @@ -48,6 +55,7 @@ fn add_heroku_app_name(mut extra: BTreeMap<String, Value>) -> BTreeMap<String, V

/// Helper function to send an Info event to Sentry. We use these events for
/// analytics purposes (I know, Sentry shouldn't be used for that...).
/// TODO https://github.com/reacherhq/backend/issues/207
pub fn metrics(message: String, retry_option: RetryOption, duration: u128, domain: &str) {
log::info!("Sending info to Sentry: {}", message);

Expand All @@ -62,20 +70,19 @@ pub fn metrics(message: String, retry_option: RetryOption, duration: u128, domai
extra,
level: Level::Info,
message: Some(message),
// FIXME It seams that this doesn't work on Sentry, so I added it in
// the `extra` field above too.
release: Some(CARGO_PKG_VERSION.into()),
..Default::default()
});
}

/// Helper function to send an Error event to Sentry.
pub fn error(message: String, result: Option<&str>, retry_option: Option<RetryOption>) {
log::debug!("Sending error to Sentry: {}", message);
let mut extra = BTreeMap::new();
let redacted_message = redact(message.as_str());
log::debug!("Sending error to Sentry: {}", redacted_message);

let mut extra = BTreeMap::new();
if let Some(result) = result {
extra.insert("CheckEmailOutput".into(), result.into());
extra.insert("CheckEmailOutput".into(), redact(result).into());
}
if let Some(retry_option) = retry_option {
extra.insert("retry_option".into(), retry_option.to_string().into());
Expand All @@ -85,10 +92,34 @@ pub fn error(message: String, result: Option<&str>, retry_option: Option<RetryOp
sentry::capture_event(Event {
extra,
level: Level::Error,
message: Some(message),
// FIXME It seams that this doesn't work on Sentry, so I added it in
// the `extra` field above too.
message: Some(redacted_message),
release: Some(CARGO_PKG_VERSION.into()),
..Default::default()
});
}

/// Function to parse emails inside a text, and replace them with
/// `*@domain.com` for privacy reasons.
fn redact(input: &str) -> String {
let result = RE.replace_all(input, "*@$domain");
result.into()
}

#[cfg(test)]
mod tests {
use super::redact;

#[test]
fn test_redact() {
assert_eq!("*@gmail.com", redact("someone@gmail.com"));
assert_eq!(
"my email is *@gmail.com.",
redact("my email is someone@gmail.com.")
);
assert_eq!(
"my email is *@gmail.com., I repeat, my email is *@gmail.com.",
redact("my email is someone@gmail.com., I repeat, my email is someone@gmail.com.")
);
assert_eq!("someone @ gmail . com", redact("someone @ gmail . com"));
}
}
2 changes: 1 addition & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

Files in this folder are Reacher backend's integration tests. They need the following to work:

- a working internet connection,
- a working internet connection.

0 comments on commit f73a209

Please sign in to comment.