1use std::collections::BTreeMap;
5
6pub const BASE_ALLOWLIST: &[&str] = &[
8 "PATH",
9 "HOME",
10 "USER",
11 "SHELL",
12 "TERM",
13 "LANG",
14 "LC_ALL",
15 "CARGO_HOME",
16 "RUSTUP_HOME",
17 "TMPDIR",
18 "TEMP",
19];
20pub const GIT_IDENTITY_NAME: &str = "heddle ci";
22pub const GIT_IDENTITY_EMAIL: &str = "ci@heddle.invalid";
24
25pub(crate) const VERDICT_ENV_ALLOWLIST: &[&str] = &[];
30
31#[derive(Debug, Clone)]
33pub struct HermeticEnv {
34 git_hermetic: bool,
35 host: BTreeMap<String, String>,
36}
37
38impl HermeticEnv {
39 #[must_use]
41 pub fn new() -> Self {
42 let host = BASE_ALLOWLIST
43 .iter()
44 .filter_map(|name| {
45 std::env::var(name)
46 .ok()
47 .map(|value| ((*name).to_string(), value))
48 })
49 .collect();
50 Self {
51 git_hermetic: true,
52 host,
53 }
54 }
55
56 #[must_use]
58 pub fn with_host(host: BTreeMap<String, String>) -> Self {
59 Self {
60 git_hermetic: true,
61 host,
62 }
63 }
64
65 #[must_use]
67 pub fn git_hermetic(mut self, enabled: bool) -> Self {
68 self.git_hermetic = enabled;
69 self
70 }
71
72 #[must_use]
74 pub fn build(
75 &self,
76 check: &BTreeMap<String, String>,
77 services: &BTreeMap<String, String>,
78 caches: &BTreeMap<String, String>,
79 ) -> BTreeMap<String, String> {
80 let mut output = self.host.clone();
81 if self.git_hermetic {
82 output.insert("GIT_CONFIG_GLOBAL".into(), "/dev/null".into());
83 output.insert("GIT_CONFIG_SYSTEM".into(), "/dev/null".into());
84 output.insert("GIT_AUTHOR_NAME".into(), GIT_IDENTITY_NAME.into());
85 output.insert("GIT_AUTHOR_EMAIL".into(), GIT_IDENTITY_EMAIL.into());
86 output.insert("GIT_COMMITTER_NAME".into(), GIT_IDENTITY_NAME.into());
87 output.insert("GIT_COMMITTER_EMAIL".into(), GIT_IDENTITY_EMAIL.into());
88 }
89 for source in [services, caches, check] {
90 output.extend(
91 source
92 .iter()
93 .map(|(key, value)| (key.clone(), value.clone())),
94 );
95 }
96 output
97 }
98}
99
100impl Default for HermeticEnv {
101 fn default() -> Self {
102 Self::new()
103 }
104}