Skip to main content

whycodes_core/
secret_env.rs

1//! Strip provider credentials from child-process environments.
2//!
3//! Host `bash` / plugin / MCP children inherit the process env by default.
4//! API keys and refresh tokens must not ride along unless a spawn explicitly
5//! re-injects them.
6
7use std::process::Command;
8
9/// Exact names always stripped, even when they do not match `*_API_KEY`.
10const SECRET_ENV_EXACT: &[&str] = &[
11    "GITHUB_TOKEN",
12    "GH_TOKEN",
13    "AWS_SECRET_ACCESS_KEY",
14    "AWS_SESSION_TOKEN",
15    "OPENAI_API_KEY",
16    "ANTHROPIC_API_KEY",
17];
18
19/// True when `name` is a known secret the child must not inherit.
20pub fn is_secret_env_name(name: &str) -> bool {
21    let n = name.to_ascii_uppercase();
22    if SECRET_ENV_EXACT.iter().any(|s| n == *s) {
23        return true;
24    }
25    if n.ends_with("_API_KEY") {
26        return true;
27    }
28    n.starts_with("WHYCODES_")
29        && (n.ends_with("_TOKEN") || n.ends_with("_KEY") || n.ends_with("_SECRET"))
30}
31
32/// Names currently set in this process that [`is_secret_env_name`] matches.
33pub fn secret_env_names_present() -> Vec<String> {
34    let mut out = Vec::new();
35    for (k, _) in std::env::vars_os() {
36        let name = k.to_string_lossy();
37        if is_secret_env_name(&name) {
38            out.push(name.into_owned());
39        }
40    }
41    out
42}
43
44/// Call `remove` for every known exact name, then any extra matching names
45/// currently set in this process (`*_API_KEY`, `WHYCODES_*_TOKEN`, …).
46/// Duplicate names are passed twice; `env_remove` is idempotent.
47pub fn strip_secret_env_vars(mut remove: impl FnMut(&str)) {
48    for name in SECRET_ENV_EXACT {
49        remove(name);
50    }
51    for name in secret_env_names_present() {
52        remove(&name);
53    }
54}
55
56/// Drop known secrets from a `std::process::Command` before spawn.
57pub fn strip_std_command_secrets(cmd: &mut Command) {
58    strip_secret_env_vars(|name| {
59        cmd.env_remove(name);
60    });
61}