use super::*;
#[cfg(unix)]
use newt_core::agentic::execute_tool;
#[cfg(unix)]
use newt_core::caveats::{Caveats, CountBound, Scope};
#[test]
fn tui_permissions_exec_clamp_is_an_always_on_floor_without_posture() {
use newt_core::caveats::{Scope, ScopeExt as _};
let configured_exec: Scope<String> = Scope::only(["cargo".to_string(), "git".to_string()]);
let floor = exec_floor_from(&configured_exec, false).expect(
"a configured [tui.permissions] exec clamp must be an always-on floor \
even without a /posture — on the pre-#774 code this was None, so an \
out-of-clamp command ran unconfined under --disable-ocap",
);
assert!(
!floor.permits(&"rm".to_string()),
"an out-of-clamp command must be denied by the always-on floor"
);
assert!(floor.permits(&"cargo".to_string()));
assert!(floor.permits(&"git".to_string()));
}
#[test]
fn exec_floor_none_only_when_unrestricted_and_no_posture_floor() {
use newt_core::caveats::Scope;
assert!(exec_floor_from(&Scope::<String>::All, false).is_none());
assert!(exec_floor_from(&Scope::<String>::All, true).is_some());
assert!(exec_floor_from(&Scope::only(["git".to_string()]), true).is_some());
}
pub(crate) struct EnvVar {
key: &'static str,
saved: Option<String>,
}
impl EnvVar {
pub(crate) fn set(key: &'static str, value: &str) -> Self {
let saved = std::env::var(key).ok();
std::env::set_var(key, value);
Self { key, saved }
}
pub(crate) fn unset(key: &'static str) -> Self {
let saved = std::env::var(key).ok();
std::env::remove_var(key);
Self { key, saved }
}
}
impl Drop for EnvVar {
fn drop(&mut self) {
match self.saved.take() {
Some(v) => std::env::set_var(self.key, v),
None => std::env::remove_var(self.key),
}
}
}
#[test]
fn banner_names_the_flag_and_the_consequence() {
let banner = ocap_disabled_banner();
assert!(banner.contains("⚠ ocap DISABLED"), "got: {banner}");
assert!(banner.contains("--disable-ocap"), "got: {banner}");
assert!(
banner.contains("permitted commands may run unconfined"),
"got: {banner}"
);
assert!(
banner.contains("active exec floors can force confinement or denial"),
"got: {banner}"
);
}
#[serial_test::serial(real_fs)]
#[test]
fn ocap_disabled_record_is_the_issue_shape_and_appends() {
let rec = ocap_disabled_record("conv-297");
assert_eq!(rec.conversation_id, "conv-297");
assert_eq!(rec.tool, "run_command");
assert_eq!(rec.kind, "exec");
assert_eq!(rec.target, "*");
assert_eq!(rec.decision, "ocap-disabled");
assert_eq!(rec.scope, "session");
let dir = tempfile::TempDir::new().unwrap();
let path = dir.path().join("permission-log.jsonl");
newt_core::permission_journal::append_record(&path, rec.clone()).unwrap();
let body = std::fs::read_to_string(&path).unwrap();
let lines = newt_core::permission_journal::read_jsonl(&body);
assert_eq!(lines.len(), 1);
assert_eq!(newt_core::permission_journal::records(&lines)[0], rec);
}
#[test]
fn full_access_banner_names_the_flag_and_the_consequence() {
let banner = full_access_banner();
assert!(banner.contains("⚠ FULL ACCESS"), "got: {banner}");
assert!(banner.contains("--full-access"), "got: {banner}");
assert!(banner.contains("exact MCP net grants"), "got: {banner}");
assert!(banner.contains("full AMBIENT authority"), "got: {banner}");
assert!(
banner.contains("Object-Capability authority restrictions"),
"got: {banner}"
);
}
#[serial_test::serial(real_fs)]
#[test]
fn full_access_record_is_the_session_shape_and_appends() {
let rec = full_access_record("conv-full-access");
assert_eq!(rec.conversation_id, "conv-full-access");
assert_eq!(rec.tool, "session");
assert_eq!(rec.kind, "exec");
assert_eq!(rec.target, "*");
assert_eq!(rec.decision, "full-access");
assert_eq!(rec.scope, "session");
let dir = tempfile::TempDir::new().unwrap();
let path = dir.path().join("permission-log.jsonl");
newt_core::permission_journal::append_record(&path, rec.clone()).unwrap();
let body = std::fs::read_to_string(&path).unwrap();
let lines = newt_core::permission_journal::read_jsonl(&body);
assert_eq!(lines.len(), 1);
assert_eq!(newt_core::permission_journal::records(&lines)[0], rec);
}
#[test]
fn full_access_env_overrides_configured_preset_in_policy_for() {
use newt_core::caveats::{Caveats as Cav, Scope};
let _g = crate::test_env_guard::env_write_guard();
let tui = newt_core::TuiConfig::default();
{
let _off = EnvVar::unset("NEWT_FULL_ACCESS");
let base = policy_for(Some(tui.clone()), "/ws");
assert!(
matches!(base.exec, Scope::Only(_)),
"override absent ⇒ the configured workspace_dev allowlist rules"
);
}
let _on = EnvVar::set("NEWT_FULL_ACCESS", "1");
assert_eq!(
policy_for(Some(tui), "/ws"),
Cav::top(),
"override asserted ⇒ the full_access preset, bit-for-bit"
);
assert_eq!(
policy_for(None, "/ws"),
Cav::top(),
"the explicit flag overrides even the absent-config read-only default"
);
}
#[cfg(unix)]
fn caveats_no_exec(ws: &std::path::Path) -> Caveats {
Caveats {
fs_read: Scope::only([ws.to_string_lossy().into_owned()]),
fs_write: Scope::only([ws.to_string_lossy().into_owned()]),
exec: Scope::none(),
net: Scope::none(),
max_calls: CountBound::Unlimited,
valid_for_generation: Scope::All,
}
}
#[cfg(unix)]
#[serial_test::serial(real_fs)]
#[tokio::test]
async fn yolo_runs_exec_unconfined_but_keeps_the_fs_fence() {
let _env = crate::test_env_guard::env_write_guard_async().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "echo yolo-through" }),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut Mcp::empty(),
None,
None,
None,
None, None,
None,
None, None, None, None, None, None, None, )
.await;
assert_eq!(out, "yolo-through\n");
let escape = "/definitely-outside-the-fence/escape.txt";
let out = execute_tool(
"write_file",
&serde_json::json!({ "path": escape, "content": "nope" }),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut Mcp::empty(),
None,
None,
None,
None, None,
None,
None, None, None, None, None, None, None, )
.await;
assert!(
out.starts_with(&format!(
"capability denied: fs_write does not permit '{escape}'"
)),
"got: {out}"
);
assert!(out.contains("request_permissions"), "got: {out}");
assert!(!std::path::Path::new(escape).exists());
}
#[cfg(unix)]
#[serial_test::serial(real_fs)]
#[tokio::test]
async fn yolo_exec_never_prompts_but_fs_prompting_still_works() {
let _env = crate::test_env_guard::env_write_guard_async().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let mut state = PermissionPromptState::default();
let outside = tempfile::TempDir::new().unwrap();
let secret = outside.path().join("secret.txt");
std::fs::write(&secret, "gated contents").unwrap();
let mut gate = PromptPermissionGate {
pending_command_retries: Default::default(),
ask_surface: None,
#[cfg(feature = "rich-tui")]
open_panel: None,
state: &mut state,
base: caveats.clone(),
key_path: None,
conversation_id: "conv-297".to_string(),
log_path: None,
denials_path: None,
config_path: None,
preset_clamp: None,
delegation: None,
danger: danger::DangerTable::builtin(),
color: false,
verbose: false,
authorization_prompts_enabled: true,
web_decision_timeout: std::time::Duration::from_secs(2),
cancel: None,
exit: None,
ask_human:
|_w: &newt_core::tty::PromptWindow,
_interaction: &newt_core::interaction_surface::SurfaceInteraction| {
PromptChoice::AllowOnce
},
};
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "echo no-prompt" }),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut Mcp::empty(),
None,
None,
None,
None, Some(&mut gate),
None,
None, None, None, None, None, None, None, )
.await;
assert_eq!(out, "no-prompt\n");
assert!(
state.decisions.is_empty(),
"exec under yolo must never reach the gate, got: {:?}",
state.decisions
);
let mut gate = PromptPermissionGate {
pending_command_retries: Default::default(),
ask_surface: None,
#[cfg(feature = "rich-tui")]
open_panel: None,
state: &mut state,
base: caveats.clone(),
key_path: None,
conversation_id: "conv-297".to_string(),
log_path: None,
denials_path: None,
config_path: None,
preset_clamp: None,
delegation: None,
danger: danger::DangerTable::builtin(),
color: false,
verbose: false,
authorization_prompts_enabled: true,
web_decision_timeout: std::time::Duration::from_secs(2),
cancel: None,
exit: None,
ask_human:
|_w: &newt_core::tty::PromptWindow,
_interaction: &newt_core::interaction_surface::SurfaceInteraction| {
PromptChoice::AllowOnce
},
};
let out = execute_tool(
"read_file",
&serde_json::json!({ "path": secret.to_string_lossy() }),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut Mcp::empty(),
None,
None,
None,
None, Some(&mut gate),
None,
None, None, None, None, None, None, None, )
.await;
assert_eq!(out, "gated contents");
assert_eq!(state.decisions.len(), 1, "the fs denial prompted once");
assert_eq!(state.decisions[0].kind, "fs_read");
}
#[cfg(unix)]
#[serial_test::serial(real_fs)]
#[tokio::test]
async fn floor_wins_over_disable_ocap_at_the_tui_seam() {
let _env = crate::test_env_guard::env_write_guard_async().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let base = caveats_no_exec(ws.path());
let clamp = newt_core::NamedPermissionPreset {
readonly: true,
..Default::default()
}
.clamp();
let effective = base.meet(&clamp);
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "echo should-not-run" }),
&ws.path().to_string_lossy(),
false,
20,
&effective,
&mut Mcp::empty(),
None,
None,
None,
None, None,
Some(&clamp.exec),
None, None, None, None, None, None, None, )
.await;
assert_ne!(out, "should-not-run\n", "the floor must block --yolo");
assert!(
out.contains("capability denied"),
"fell to the confined dispatch and was denied: {out}"
);
}