1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
//! Typed, immutable **launch authority** — the OCAP-off / full-access switches
//! resolved **once** near startup and then FROZEN for the process. The typed
//! `--workspace-access` choice has no environment twin and retains confinement.
//!
//! # Why this exists (`noninteractive-launch-policy` closure)
//!
//! The authority switches (`--disable-ocap`/`--yolo`, `--full-access`,
//! `--unsafe-host-exec`) have env twins (`NEWT_DISABLE_OCAP`,
//! `NEWT_FULL_ACCESS`, `NEWT_UNSAFE_HOST_EXEC`) so a wrapper/pod can assert them.
//! Historically deep libraries decided authority by reading those env vars
//! *live* (`std::env::var(...)`) at the point of use — so a value that appeared
//! **after** startup (an inherited var, or a hostile tool that managed to set
//! one mid-session) could *widen* the running process's authority. That is a
//! confused-deputy: authority is supposed to be a startup decision, not an
//! ambient signal a later actor can flip.
//!
//! This module makes authority a **value**, not an ambient read:
//!
//! - [`LaunchAuthority::from_env`] is the **only** place that reads the three
//! authority env vars. A widening switch reads fail-closed (the value must be
//! exactly `"1"`).
//! - [`freeze`] records the resolved authority once, near startup (the CLI
//! entrypoints call it after they translate flags to env twins). First freeze
//! wins; a later call can never widen it.
//! - [`current`] returns the frozen value. Deep libraries call this instead of
//! `std::env::var`, so a later-appearing env var cannot change authority.
//! - The switches are private bits with getters only; the sole combinator
//! ([`LaunchAuthority::meet`]) can *attenuate* (clear bits), never widen.
//! - [`LaunchAuthority::WORKSPACE_ACCESS`] is an explicit CLI startup choice:
//! commands are admitted inside the workspace, with no host bypass.
//!
//! The ratchet that keeps it honest is a source-inventory gate (`ocap_check.py`):
//! `std::env::var("NEWT_DISABLE_OCAP" | "NEWT_FULL_ACCESS" | "NEWT_UNSAFE_HOST_EXEC")`
//! may appear **only** in this file. Any other deep ambient authority read
//! re-opens the deviation.
/// The ambient authority switches and typed workspace choice, as one immutable value. `Copy` so it
/// is returned and threaded freely; the bits are private and there is
/// deliberately no setter that *widens* — construction is via [`from_env`] (the
/// startup resolver), the [`CONFINED`] default, or an attenuating [`meet`].
///
/// [`from_env`]: LaunchAuthority::from_env
/// [`CONFINED`]: LaunchAuthority::CONFINED
/// [`meet`]: LaunchAuthority::meet
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct LaunchAuthority {
/// `--disable-ocap` / `--yolo` (`NEWT_DISABLE_OCAP=1`): run `run_command`
/// UNCONFINED on the host shell instead of the confined engine.
ocap_disabled: bool,
/// `--full-access` (`NEWT_FULL_ACCESS=1`): build the session policy from the
/// `full_access` preset (`Caveats::top()`) regardless of config.
full_access: bool,
/// `--unsafe-host-exec` (`NEWT_UNSAFE_HOST_EXEC=1`): the explicit opt-in that
/// lets a headless lane select the OCAP-off host-exec path.
unsafe_host_exec: bool,
/// Explicit directory-scoped startup choice; never inferred from the env.
workspace_access: bool,
}
impl LaunchAuthority {
/// Fully confined — every switch off. The safe default (same as
/// `LaunchAuthority::default()`), and what a process with no authority flags
/// resolves to.
pub const CONFINED: Self = Self {
ocap_disabled: false,
full_access: false,
unsafe_host_exec: false,
workspace_access: false,
};
/// Run commands with filesystem access confined to the selected workspace.
/// The configured network scope remains independent. This explicit startup
/// choice clears inherited global-access and unconfined-execution switches.
pub const WORKSPACE_ACCESS: Self = Self {
workspace_access: true,
..Self::CONFINED
};
/// Read exactly `"1"` from the env twin — a widening switch is fail-closed,
/// so anything else (including `"true"`) leaves it off.
fn env_switch(key: &str) -> bool {
std::env::var(key).is_ok_and(|v| v == "1")
}
/// Resolve the launch authority from the process environment. **This is the
/// only function in the workspace that reads the authority env vars**; every
/// other consumer goes through [`current`]. Callers translate their flags to
/// the env twins first (the compatibility input), then [`freeze`] the result.
#[must_use]
pub fn from_env() -> Self {
Self {
ocap_disabled: Self::env_switch("NEWT_DISABLE_OCAP"),
full_access: Self::env_switch("NEWT_FULL_ACCESS"),
unsafe_host_exec: Self::env_switch("NEWT_UNSAFE_HOST_EXEC"),
workspace_access: false,
}
}
/// `--disable-ocap` / `--yolo` asserted for this process.
#[must_use]
pub const fn ocap_disabled(self) -> bool {
self.ocap_disabled
}
/// `--full-access` asserted for this process.
#[must_use]
pub const fn full_access(self) -> bool {
self.full_access
}
/// `--unsafe-host-exec` asserted for this process.
#[must_use]
pub const fn unsafe_host_exec(self) -> bool {
self.unsafe_host_exec
}
/// Full command and file authority inside the selected workspace, while
/// retaining the native filesystem fence and independent network scope.
#[must_use]
pub const fn workspace_access(self) -> bool {
self.workspace_access
}
/// Attenuate: the per-bit `meet` (logical AND) of two authorities. A switch
/// is on in the result only if it was on in BOTH — so `meet` can drop
/// authority but never add it. This is how a later context lowers authority
/// without any path to widen it.
#[must_use]
pub const fn meet(self, other: Self) -> Self {
Self {
ocap_disabled: self.ocap_disabled && other.ocap_disabled,
full_access: self.full_access && other.full_access,
unsafe_host_exec: self.unsafe_host_exec && other.unsafe_host_exec,
workspace_access: self.workspace_access && other.workspace_access,
}
}
}
// The frozen store is a process-global `OnceLock` in production; in test builds
// it is a **thread-local** so a freeze in one test cannot bleed into a
// concurrent test in the same binary (e.g. the `disable_ocap_tests` that drive
// `ocap_disabled()` via their env guards). The freeze/current logic is
// otherwise identical, and both are "first freeze wins".
#[cfg(not(test))]
static PROCESS_FROZEN: std::sync::OnceLock<LaunchAuthority> = std::sync::OnceLock::new();
#[cfg(test)]
thread_local! {
static PROCESS_FROZEN: std::cell::Cell<Option<LaunchAuthority>> =
const { std::cell::Cell::new(None) };
}
/// Freeze the launch authority for the process. Call this once, near startup,
/// AFTER the entrypoint has translated its authority flags to the env twins.
/// **First freeze wins**: a later call (or a later env mutation) can never widen
/// the running process's authority (a `OnceLock` ignores subsequent sets).
pub fn freeze(authority: LaunchAuthority) {
#[cfg(not(test))]
{
PROCESS_FROZEN.set(authority).ok();
}
#[cfg(test)]
{
PROCESS_FROZEN.with(|c| {
if c.get().is_none() {
c.set(Some(authority));
}
});
}
}
/// The launch authority for this process. Deep libraries call this to decide
/// authority instead of reading `std::env::var`.
///
/// If an entrypoint has [`freeze`]d (every production entrypoint does, right
/// after it translates its authority flags to the env twins), this returns that
/// frozen value and a later env mutation is ignored — the invariant. If nothing
/// has frozen yet it resolves live from env, which is only the pre-freeze
/// startup window in production and the normal state in a test that does not
/// freeze (so existing env-driven tests keep working; the frozen path is proven
/// by `frozen_authority_ignores_later_env_mutation`). It never lazily freezes —
/// that would let the *first* read in a long-lived test process pin the value
/// for every later test in the same binary.
#[must_use]
pub fn current() -> LaunchAuthority {
#[cfg(not(test))]
{
PROCESS_FROZEN
.get()
.copied()
.unwrap_or_else(LaunchAuthority::from_env)
}
#[cfg(test)]
{
PROCESS_FROZEN
.with(std::cell::Cell::get)
.unwrap_or_else(LaunchAuthority::from_env)
}
}
/// Clear the thread-local frozen authority (test-only) so the freeze-path tests
/// run in isolation.
#[cfg(test)]
pub(crate) fn reset_for_test() {
PROCESS_FROZEN.with(|c| c.set(None));
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Mutex, MutexGuard};
/// Serializes the tests here that touch the shared env twins + the frozen
/// global, so the process-wide state can't race across the parallel runner.
static SERIAL: Mutex<()> = Mutex::new(());
fn serial() -> MutexGuard<'static, ()> {
SERIAL.lock().unwrap_or_else(|p| p.into_inner())
}
/// RAII env override restoring the previous value on drop (even on panic).
struct EnvVar {
key: &'static str,
prev: Option<String>,
}
impl EnvVar {
fn set(key: &'static str, val: &str) -> Self {
let prev = std::env::var(key).ok();
unsafe { std::env::set_var(key, val) };
Self { key, prev }
}
fn unset(key: &'static str) -> Self {
let prev = std::env::var(key).ok();
unsafe { std::env::remove_var(key) };
Self { key, prev }
}
}
impl Drop for EnvVar {
fn drop(&mut self) {
match &self.prev {
Some(v) => unsafe { std::env::set_var(self.key, v) },
None => unsafe { std::env::remove_var(self.key) },
}
}
}
#[test]
fn from_env_reads_exactly_one_fail_closed() {
let _g = serial();
let _a = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let _b = EnvVar::set("NEWT_FULL_ACCESS", "true"); // not "1" ⇒ fail-closed
let _c = EnvVar::unset("NEWT_UNSAFE_HOST_EXEC");
let a = LaunchAuthority::from_env();
assert!(a.ocap_disabled(), "exactly \"1\" enables");
assert!(
!a.full_access(),
"a widening switch reads fail-closed unless == \"1\""
);
assert!(!a.unsafe_host_exec());
}
#[test]
fn meet_can_only_attenuate_never_widen() {
let full = LaunchAuthority {
ocap_disabled: true,
full_access: true,
unsafe_host_exec: true,
workspace_access: true,
};
// meeting with CONFINED clears everything…
assert_eq!(
full.meet(LaunchAuthority::CONFINED),
LaunchAuthority::CONFINED
);
// …and a confined base can never be widened by meeting with `full`.
assert_eq!(
LaunchAuthority::CONFINED.meet(full),
LaunchAuthority::CONFINED
);
}
#[test]
fn workspace_choice_retains_confinement_and_freezes_once() {
let _g = serial();
reset_for_test();
let choice = LaunchAuthority::WORKSPACE_ACCESS;
assert!(choice.workspace_access());
assert!(!choice.full_access());
assert!(!choice.ocap_disabled());
assert!(!choice.unsafe_host_exec());
assert_eq!(
choice.meet(LaunchAuthority::CONFINED),
LaunchAuthority::CONFINED
);
freeze(choice);
let _global = EnvVar::set("NEWT_FULL_ACCESS", "1");
let _bypass = EnvVar::set("NEWT_DISABLE_OCAP", "1");
freeze(LaunchAuthority::from_env());
assert_eq!(
current(),
choice,
"later global authority cannot replace the directory choice"
);
reset_for_test();
}
/// THE adversarial invariant: construct a confined authority, FREEZE it,
/// then set every authority env var — `current()` must stay confined. This
/// is the "authority cannot widen from a later environment mutation" bound.
#[test]
fn frozen_authority_ignores_later_env_mutation() {
let _g = serial();
reset_for_test();
// Startup resolves confined (no switches).
let _off1 = EnvVar::unset("NEWT_DISABLE_OCAP");
let _off2 = EnvVar::unset("NEWT_FULL_ACCESS");
let _off3 = EnvVar::unset("NEWT_UNSAFE_HOST_EXEC");
freeze(LaunchAuthority::from_env());
assert_eq!(current(), LaunchAuthority::CONFINED, "froze confined");
// A later actor sets every switch — the frozen value must not move.
let _on1 = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let _on2 = EnvVar::set("NEWT_FULL_ACCESS", "1");
let _on3 = EnvVar::set("NEWT_UNSAFE_HOST_EXEC", "1");
let now = current();
assert!(
!now.ocap_disabled(),
"later NEWT_DISABLE_OCAP must not widen a frozen confined authority"
);
assert!(!now.full_access(), "later NEWT_FULL_ACCESS must not widen");
assert!(
!now.unsafe_host_exec(),
"later NEWT_UNSAFE_HOST_EXEC must not widen"
);
reset_for_test();
}
#[test]
fn freeze_is_first_wins_a_second_freeze_cannot_widen() {
let _g = serial();
reset_for_test();
freeze(LaunchAuthority::CONFINED);
freeze(LaunchAuthority {
ocap_disabled: true,
full_access: true,
unsafe_host_exec: true,
workspace_access: true,
});
assert_eq!(
current(),
LaunchAuthority::CONFINED,
"the first (confined) freeze wins"
);
reset_for_test();
}
}