use std::collections::HashMap;
use crate::error::Error;
pub(crate) const INJECT_ENV_VARS: &[&str] = &[
"LD_PRELOAD",
"LD_PRELOAD_32",
"LD_PRELOAD_64",
"LD_AUDIT",
"DYLD_INSERT_LIBRARIES",
];
pub(crate) const SUSPICIOUS_ENV_VARS: &[&str] = &[
"LD_LIBRARY_PATH",
"LD_DEBUG",
"LD_DEBUG_OUTPUT",
"LD_DYNAMIC_WEAK",
"LD_ORIGIN_PATH",
"LD_PROFILE",
"LD_SHOW_AUXV",
"DYLD_LIBRARY_PATH",
"DYLD_FRAMEWORK_PATH",
"DYLD_FALLBACK_LIBRARY_PATH",
"PYTHONPATH",
"BASH_ENV",
"ENV",
"NODE_OPTIONS",
"RUBYOPT",
"PERL5LIB",
"JAVA_TOOL_OPTIONS",
"GLIBC_TUNABLES",
"GCONV_PATH",
"LOCPATH",
"PERL5OPT",
"JDK_JAVA_OPTIONS",
"_JAVA_OPTIONS",
];
#[derive(Debug, PartialEq)]
pub enum EnvironmentThreatLevel {
Safe,
Degraded(String),
Hostile(String),
}
#[must_use]
pub fn assess_environment() -> EnvironmentThreatLevel {
let mut hostile = Vec::new();
for var in INJECT_ENV_VARS {
if std::env::var_os(var).is_some() {
hostile.push(*var);
}
}
if !hostile.is_empty() {
return EnvironmentThreatLevel::Hostile(format!(
"direct injection variables detected: {}",
hostile.join(", ")
));
}
let mut degraded = Vec::new();
for var in SUSPICIOUS_ENV_VARS {
if std::env::var_os(var).is_some() {
degraded.push(*var);
}
}
if !degraded.is_empty() {
return EnvironmentThreatLevel::Degraded(format!(
"suspicious environment variables set: {}",
degraded.join(", ")
));
}
EnvironmentThreatLevel::Safe
}
#[must_use]
pub fn assess_env_signals(_ctx: &super::DetectorContext) -> Vec<super::Signal> {
let mut signals = Vec::new();
for var in INJECT_ENV_VARS {
if std::env::var_os(var).is_some() {
signals.push(super::Signal::new(
super::SignalId::scoped("env.preload", var),
super::Category::EnvironmentInjection,
super::Severity::Hostile,
"dynamic-loader injection variable set",
format!("`{var}` is set in our parent environment — every child we exec will load attacker-controlled code"),
"unset the variable before invoking envseal, or run from a clean shell",
));
}
}
for var in SUSPICIOUS_ENV_VARS {
if std::env::var_os(var).is_some() {
signals.push(super::Signal::new(
super::SignalId::scoped("env.suspicious_loader_var", var),
super::Category::EnvironmentInjection,
super::Severity::Degraded,
"suspicious loader configuration variable set",
format!("`{var}` modifies library resolution"),
"review whether this var is intentional in this shell",
));
}
}
signals
}
pub fn enforce_env_policy(config: &crate::security_config::SecurityConfig) -> Result<(), Error> {
let ctx = super::DetectorContext::ambient();
let signals = assess_env_signals(&ctx);
super::emit_signals_inline(signals, config)
}
#[must_use]
pub fn sanitized_env() -> HashMap<String, String> {
let blocked: std::collections::HashSet<&str> = INJECT_ENV_VARS
.iter()
.chain(SUSPICIOUS_ENV_VARS.iter())
.copied()
.collect();
std::env::vars()
.filter(|(key, _)| !blocked.contains(key.as_str()))
.collect()
}