#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DebugEnvironment {
#[default]
Auto,
Allow,
Deny,
}
const SSL_KEY_LOG_FILE: &str = "SSLKEYLOGFILE";
const CRASH_REPORTER_VARIABLES: &[(&str, &str)] = &[
(
"CEF_CRASH_REPORTER_SERVER_URL",
"redirecting crash report uploads, which carry process memory",
),
(
"CEF_CRASH_REPORTER_RATE_LIMIT_ENABLED",
"overriding the crash report upload rate limit",
),
(
"BREAKPAD_DUMP_LOCATION",
"redirecting where minidumps are written",
),
];
fn refuses_debug_variables(policy: DebugEnvironment, is_dev: bool) -> bool {
match policy {
DebugEnvironment::Auto => !is_dev,
DebugEnvironment::Allow => false,
DebugEnvironment::Deny => true,
}
}
fn is_set(name: &str) -> bool {
std::env::var_os(name).is_some_and(|value| !value.is_empty())
}
pub(crate) fn neutralizes_tls_key_log(policy: DebugEnvironment, is_dev: bool) -> bool {
if !refuses_debug_variables(policy, is_dev) || !is_set(SSL_KEY_LOG_FILE) {
return false;
}
log::warn!(
"ignoring the {SSL_KEY_LOG_FILE} environment variable: it asks Chromium to log the TLS \
session keys that decrypt this application's network traffic. Set \
DebugEnvironment::Allow to honour it."
);
true
}
pub(crate) fn remove_crash_reporter_overrides(policy: DebugEnvironment, is_dev: bool) {
if !refuses_debug_variables(policy, is_dev) {
return;
}
for (name, effect) in CRASH_REPORTER_VARIABLES {
if !is_set(name) {
continue;
}
log::warn!(
"ignoring the {name} environment variable: it asks CEF for {effect}. Set \
DebugEnvironment::Allow to honour it."
);
unsafe { std::env::remove_var(name) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auto_follows_the_build_profile() {
assert!(
!refuses_debug_variables(DebugEnvironment::Auto, true),
"a development build keeps its debugging hooks"
);
assert!(
refuses_debug_variables(DebugEnvironment::Auto, false),
"a shipped build must not hand its TLS keys to whoever sets a variable"
);
}
#[test]
fn explicit_policies_ignore_the_build_profile() {
for is_dev in [false, true] {
assert!(!refuses_debug_variables(DebugEnvironment::Allow, is_dev));
assert!(refuses_debug_variables(DebugEnvironment::Deny, is_dev));
}
}
#[test]
fn a_permissive_policy_never_touches_the_command_line() {
assert!(!neutralizes_tls_key_log(DebugEnvironment::Allow, false));
assert!(!neutralizes_tls_key_log(DebugEnvironment::Auto, true));
}
#[test]
fn every_crash_variable_is_described() {
for (name, effect) in CRASH_REPORTER_VARIABLES {
assert!(!name.is_empty());
assert!(!effect.is_empty());
}
}
#[test]
fn the_tls_variable_is_not_also_removed_from_the_environment() {
assert!(
!CRASH_REPORTER_VARIABLES
.iter()
.any(|(name, _)| *name == SSL_KEY_LOG_FILE)
);
}
}