1use std::collections::BTreeMap;
5use std::ffi::OsString;
6use std::path::PathBuf;
7use std::process::ExitStatus;
8
9use crate::internal::app_adapter::{launcher, LaunchRequest, ResolutionStrategy, ResolvedProgram};
10
11use crate::error::{Error, Result};
12
13pub use crate::internal::app_adapter::IntegrationType;
14
15pub struct SecureProcess {
29 program: PathBuf,
30 args: Vec<OsString>,
31 secret_env: BTreeMap<String, String>,
32 env_additions: BTreeMap<String, String>,
33 env_removals: Vec<String>,
34 scrub_patterns: Vec<String>,
35}
36
37impl std::fmt::Debug for SecureProcess {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("SecureProcess")
40 .field("program", &self.program)
41 .field("args", &self.args)
42 .field("env_additions", &self.env_additions)
43 .field("env_removals", &self.env_removals)
44 .field("scrub_patterns", &self.scrub_patterns)
45 .finish()
47 }
48}
49
50impl SecureProcess {
51 pub fn new(program: impl Into<PathBuf>) -> Self {
52 Self {
53 program: program.into(),
54 args: Vec::new(),
55 secret_env: BTreeMap::new(),
56 env_additions: BTreeMap::new(),
57 env_removals: Vec::new(),
58 scrub_patterns: Vec::new(),
59 }
60 }
61
62 pub fn arg(mut self, a: impl Into<OsString>) -> Self {
63 self.args.push(a.into());
64 self
65 }
66
67 pub fn args(mut self, args: impl IntoIterator<Item = impl Into<OsString>>) -> Self {
68 self.args.extend(args.into_iter().map(Into::into));
69 self
70 }
71
72 pub fn secret_env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
75 self.secret_env.insert(key.into(), value.into());
76 self
77 }
78
79 pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
81 self.env_additions.insert(key.into(), value.into());
82 self
83 }
84
85 pub fn env_remove(mut self, key: impl Into<String>) -> Self {
87 self.env_removals.push(key.into());
88 self
89 }
90
91 pub fn scrub(mut self, pattern: impl Into<String>) -> Self {
94 self.scrub_patterns.push(pattern.into());
95 self
96 }
97
98 pub fn run(self) -> Result<ExitStatus> {
100 let mut env_overrides: BTreeMap<String, String> = BTreeMap::new();
101 for (k, v) in self.secret_env {
102 env_overrides.insert(k, v);
103 }
104 for (k, v) in self.env_additions {
105 env_overrides.insert(k, v);
106 }
107
108 let request = LaunchRequest {
109 program: ResolvedProgram {
110 path: self.program,
111 fixed_args: Vec::new(),
112 strategy: ResolutionStrategy::ExplicitPath,
113 shell_hint: None,
114 },
115 args: self
116 .args
117 .into_iter()
118 .map(|s| s.to_string_lossy().into_owned())
119 .collect(),
120 env_overrides,
121 env_removals: self.env_removals,
122 env_scrub_patterns: self.scrub_patterns,
123 };
124
125 launcher::run(request).map_err(|e| Error::KeyOperation {
126 operation: "exec".into(),
127 detail: e.to_string(),
128 })
129 }
130
131 #[allow(clippy::needless_return, unreachable_code)]
137 pub fn exec(self) -> Result<std::convert::Infallible> {
138 #[cfg(unix)]
139 {
140 use std::os::unix::process::CommandExt;
141 let mut cmd = std::process::Command::new(&self.program);
142 cmd.args(&self.args);
143 for (k, v) in &self.secret_env {
144 cmd.env(k, v);
145 }
146 for (k, v) in &self.env_additions {
147 cmd.env(k, v);
148 }
149 for k in &self.env_removals {
150 cmd.env_remove(k);
151 }
152 let err = cmd.exec();
153 return Err(Error::KeyOperation {
154 operation: "exec".into(),
155 detail: err.to_string(),
156 });
157 }
158 #[cfg(not(unix))]
159 {
160 let status = self.run()?;
161 let code = status.code().unwrap_or(1);
162 std::process::exit(code);
163 }
164 }
165}
166
167pub struct TempSecretFile {
174 #[cfg(target_os = "linux")]
175 _inner: TempSecretInner,
176 #[cfg(not(target_os = "linux"))]
177 _inner: crate::internal::app_adapter::TempConfig,
178 path_str: String,
179}
180
181#[cfg(target_os = "linux")]
183#[allow(dead_code)]
184enum TempSecretInner {
185 Memfd(crate::internal::app_adapter::MemfdConfig),
186 Fallback(crate::internal::app_adapter::TempConfig),
187}
188
189impl std::fmt::Debug for TempSecretFile {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 f.debug_struct("TempSecretFile")
192 .field("path", &self.path_str)
193 .finish()
194 }
195}
196
197impl TempSecretFile {
198 pub fn create(content: &str) -> Result<Self> {
200 Self::create_bytes(content.as_bytes())
201 }
202
203 pub fn create_bytes(content: &[u8]) -> Result<Self> {
205 #[cfg(target_os = "linux")]
206 {
207 match crate::internal::app_adapter::create_memfd_config(
208 "enclave-secret",
209 "secret",
210 content,
211 ) {
212 Ok(memfd) => {
213 let path_str = memfd.path().to_string_lossy().into_owned();
214 Ok(Self {
215 _inner: TempSecretInner::Memfd(memfd),
216 path_str,
217 })
218 }
219 Err(_) => {
220 let tc = crate::internal::app_adapter::TempConfig::write(
222 "enclave-secret",
223 "secret",
224 content,
225 )
226 .map_err(|e| Error::KeyOperation {
227 operation: "temp_secret".into(),
228 detail: e.to_string(),
229 })?;
230 let path_str = tc.path().to_string_lossy().into_owned();
231 Ok(Self {
232 _inner: TempSecretInner::Fallback(tc),
233 path_str,
234 })
235 }
236 }
237 }
238 #[cfg(not(target_os = "linux"))]
239 {
240 let tc = crate::internal::app_adapter::TempConfig::write(
241 "enclave-secret",
242 "secret",
243 content,
244 )
245 .map_err(|e| Error::KeyOperation {
246 operation: "temp_secret".into(),
247 detail: e.to_string(),
248 })?;
249 let path_str = tc.path().to_string_lossy().into_owned();
250 Ok(Self {
251 _inner: tc,
252 path_str,
253 })
254 }
255 }
256
257 pub fn path(&self) -> &str {
259 &self.path_str
260 }
261}