Skip to main content

lilo_rm_core/
path_shaped_envs.rs

1//! Curated environment variables whose values Claude Code treats as paths.
2//!
3//! This list is intentionally explicit. Docker preflight uses it to reject
4//! path values that would be unreadable inside the container namespace.
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub enum PathValueShape {
8    Single,
9    ColonList,
10}
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct PathShapedEnv {
14    pub key: &'static str,
15    pub doc_comment: &'static str,
16    pub value_shape: PathValueShape,
17}
18
19impl PathShapedEnv {
20    pub fn path_values<'a>(&self, value: &'a str) -> Vec<&'a str> {
21        match self.value_shape {
22            PathValueShape::Single => vec![value],
23            PathValueShape::ColonList if value.is_empty() => vec![value],
24            PathValueShape::ColonList => value.split(':').collect(),
25        }
26    }
27}
28
29pub fn claude_path_shaped_env(key: &str) -> Option<&'static PathShapedEnv> {
30    CLAUDE_PATH_SHAPED_ENVS
31        .iter()
32        .find(|entry| entry.key == key)
33}
34
35pub const CLAUDE_PATH_SHAPED_ENVS: &[PathShapedEnv] = &[
36    // `CLAUDE_BG_AUTH_SNAPSHOT_PATH`: Claude Code source names this auth
37    // snapshot file path, which must be readable where the runtime starts.
38    PathShapedEnv {
39        key: "CLAUDE_BG_AUTH_SNAPSHOT_PATH",
40        doc_comment: "Auth snapshot file path. Source: Claude Code source string.",
41        value_shape: PathValueShape::Single,
42    },
43    // `CLAUDE_CODE_CLIENT_CERT`: documented mTLS client certificate file.
44    PathShapedEnv {
45        key: "CLAUDE_CODE_CLIENT_CERT",
46        doc_comment: "mTLS client certificate file. Source: Claude Code environment variable documentation.",
47        value_shape: PathValueShape::Single,
48    },
49    // `CLAUDE_CODE_CLIENT_KEY`: documented mTLS private key file.
50    PathShapedEnv {
51        key: "CLAUDE_CODE_CLIENT_KEY",
52        doc_comment: "mTLS client private key file. Source: Claude Code environment variable documentation.",
53        value_shape: PathValueShape::Single,
54    },
55    // `CLAUDE_CODE_DEBUG_LOGS_DIR`: documented debug log file path.
56    PathShapedEnv {
57        key: "CLAUDE_CODE_DEBUG_LOGS_DIR",
58        doc_comment: "Debug log file path. Source: Claude Code environment variable documentation.",
59        value_shape: PathValueShape::Single,
60    },
61    // `CLAUDE_CODE_GIT_BASH_PATH`: documented Git Bash executable path.
62    PathShapedEnv {
63        key: "CLAUDE_CODE_GIT_BASH_PATH",
64        doc_comment: "Git Bash executable path. Source: Claude Code environment variable documentation.",
65        value_shape: PathValueShape::Single,
66    },
67    // `CLAUDE_CODE_PLUGIN_CACHE_DIR`: documented plugin parent directory.
68    PathShapedEnv {
69        key: "CLAUDE_CODE_PLUGIN_CACHE_DIR",
70        doc_comment: "Plugin parent directory. Source: Claude Code environment variable documentation.",
71        value_shape: PathValueShape::Single,
72    },
73    // `CLAUDE_CODE_PLUGIN_SEED_DIR`: documented seed directory list.
74    PathShapedEnv {
75        key: "CLAUDE_CODE_PLUGIN_SEED_DIR",
76        doc_comment: "Plugin seed directory list. Source: Claude Code environment variable documentation.",
77        value_shape: PathValueShape::ColonList,
78    },
79    // `CLAUDE_CODE_SHELL_PREFIX`: documented command prefix path for shell
80    // commands, hooks, and MCP server startup.
81    PathShapedEnv {
82        key: "CLAUDE_CODE_SHELL_PREFIX",
83        doc_comment: "Shell command prefix path. Source: Claude Code environment variable documentation.",
84        value_shape: PathValueShape::Single,
85    },
86    // `CLAUDE_CODE_TMPDIR`: documented temp directory root.
87    PathShapedEnv {
88        key: "CLAUDE_CODE_TMPDIR",
89        doc_comment: "Claude Code temp directory root. Source: Claude Code environment variable documentation.",
90        value_shape: PathValueShape::Single,
91    },
92    // `CLAUDE_CONFIG_DIR`: documented configuration directory.
93    PathShapedEnv {
94        key: "CLAUDE_CONFIG_DIR",
95        doc_comment: "Configuration directory containing settings, credentials, history, and plugins. Source: Claude Code environment variable documentation.",
96        value_shape: PathValueShape::Single,
97    },
98    // `CLAUDE_ENV_FILE`: documented shell script path sourced for Bash calls.
99    PathShapedEnv {
100        key: "CLAUDE_ENV_FILE",
101        doc_comment: "Shell script path sourced before Bash commands. Source: Claude Code environment variable documentation.",
102        value_shape: PathValueShape::Single,
103    },
104    // `CLAUDE_PLUGIN_ROOT`: Claude Code source names this plugin root
105    // directory, which must exist inside the runtime namespace.
106    PathShapedEnv {
107        key: "CLAUDE_PLUGIN_ROOT",
108        doc_comment: "Plugin root directory. Source: Claude Code source string.",
109        value_shape: PathValueShape::Single,
110    },
111    // `CLAUDE_PROJECT_DIR`: conventional Claude project directory used by
112    // hooks and subprocesses.
113    PathShapedEnv {
114        key: "CLAUDE_PROJECT_DIR",
115        doc_comment: "Project directory for hooks and subprocesses. Source: Claude Code source string and convention.",
116        value_shape: PathValueShape::Single,
117    },
118    // `CLAUDE_SECURESTORAGE_CONFIG_DIR`: Claude Code source names this
119    // secure storage configuration directory.
120    PathShapedEnv {
121        key: "CLAUDE_SECURESTORAGE_CONFIG_DIR",
122        doc_comment: "Secure storage configuration directory. Source: Claude Code source string.",
123        value_shape: PathValueShape::Single,
124    },
125    // `CLAUDE_SKILL_DIR`: Claude Code source names this skills directory.
126    PathShapedEnv {
127        key: "CLAUDE_SKILL_DIR",
128        doc_comment: "Skills directory. Source: Claude Code source string.",
129        value_shape: PathValueShape::Single,
130    },
131    // `CLAUDE_TMPDIR`: Claude Code source names this temp directory root.
132    PathShapedEnv {
133        key: "CLAUDE_TMPDIR",
134        doc_comment: "Claude temp directory root. Source: Claude Code source string.",
135        value_shape: PathValueShape::Single,
136    },
137];
138
139#[cfg(test)]
140mod tests {
141    use std::collections::BTreeSet;
142
143    use super::*;
144
145    #[test]
146    fn claude_path_shaped_envs_have_frozen_cardinality() {
147        assert_eq!(CLAUDE_PATH_SHAPED_ENVS.len(), 16);
148    }
149
150    #[test]
151    fn claude_path_shaped_envs_have_doc_comments() {
152        for entry in CLAUDE_PATH_SHAPED_ENVS {
153            assert!(
154                !entry.doc_comment.trim().is_empty(),
155                "{} is missing an entry doc comment",
156                entry.key
157            );
158        }
159    }
160
161    #[test]
162    fn claude_path_shaped_envs_are_unique_claude_keys() {
163        let mut seen = BTreeSet::new();
164
165        for entry in CLAUDE_PATH_SHAPED_ENVS {
166            assert!(entry.key.starts_with("CLAUDE_"), "{}", entry.key);
167            assert!(seen.insert(entry.key), "{} is duplicated", entry.key);
168        }
169    }
170
171    #[test]
172    fn colon_lists_preserve_each_declared_path() {
173        let entry = claude_path_shaped_env("CLAUDE_CODE_PLUGIN_SEED_DIR").expect("entry");
174
175        assert_eq!(
176            entry.path_values("/seed/a:/seed/b"),
177            vec!["/seed/a", "/seed/b"]
178        );
179    }
180}