lean_ctx/core/config/
risk.rs1pub struct ConfigRisk {
16 pub note: &'static str,
18}
19
20#[must_use]
25pub fn classify(key: &str) -> Option<ConfigRisk> {
26 let note = match key {
27 "path_jail" => {
28 "Path jail confines agent file access to the project root. Disabling it lets tools read and write any path on this machine."
29 }
30 "shell_security" => {
31 "Shell gating blocks dangerous commands via an allowlist. Lowering it (warn/off) lets the agent run any command."
32 }
33 "sandbox_level" => {
34 "Sandbox level governs how strictly tool execution is contained. Lowering it reduces isolation."
35 }
36 "secret_detection.enabled" => {
37 "Secret detection masks API keys and .env values before they reach the LLM. Disabling it can leak credentials to the provider."
38 }
39 "secret_detection.redact" => {
40 "Secret redaction masks detected secrets. Disabling it sends them verbatim to the provider."
41 }
42 "boundary_policy" => {
43 "Boundary policy controls what context is allowed to leave this machine. Relaxing it widens data egress."
44 }
45 "proxy_require_token" => {
46 "Proxy token policy controls whether provider API keys can authenticate local proxy requests without the lean-ctx Bearer token."
47 }
48 "proxy_loopback_open" => {
49 "Disables ALL proxy authentication on loopback binds. Any local process can access the proxy without a token."
50 }
51 "proxy.anthropic_upstream"
52 | "proxy.openai_upstream"
53 | "proxy.chatgpt_upstream"
54 | "proxy.gemini_upstream" => {
55 "Redirects provider traffic to a custom upstream — every request and API key for this provider will flow through it."
56 }
57 _ => return None,
58 };
59 Some(ConfigRisk { note })
60}
61
62#[must_use]
64pub fn is_consequential(key: &str) -> bool {
65 classify(key).is_some()
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn security_keys_are_consequential() {
74 for key in [
75 "path_jail",
76 "shell_security",
77 "sandbox_level",
78 "secret_detection.enabled",
79 "secret_detection.redact",
80 "boundary_policy",
81 "proxy_require_token",
82 "proxy_loopback_open",
83 "proxy.openai_upstream",
84 "proxy.chatgpt_upstream",
85 "proxy.anthropic_upstream",
86 "proxy.gemini_upstream",
87 ] {
88 assert!(is_consequential(key), "{key} should be consequential");
89 assert!(!classify(key).unwrap().note.is_empty());
90 }
91 }
92
93 #[test]
94 fn routine_keys_are_not_consequential() {
95 for key in [
96 "theme",
97 "max_ram_percent",
98 "compression_level",
99 "proxy.port",
100 "proxy.effort",
101 "bm25_max_cache_mb",
102 ] {
103 assert!(!is_consequential(key), "{key} should be routine");
104 }
105 }
106}