chore: sync current workspace changes
Desktop CI Build / Build macOS ARM64 DMG (push) Has been cancelled
Desktop CI Build / Build Windows x64 (push) Has been cancelled

This commit is contained in:
boos
2026-07-14 03:21:10 +08:00
parent 1512fa23ce
commit 557914fea8
745 changed files with 124917 additions and 11222 deletions
+48 -14
View File
@@ -9,16 +9,16 @@
#![cfg(unix)]
use boosapi::services::agent_workbench::bash_sandbox::{build_bwrap_args, SandboxedBashTool};
use boosapi::services::agent_workbench::bash_sandbox::{
build_bwrap_args, sandbox_available, SandboxedBashTool,
};
use boosapi::services::agent_workbench::permission::PermissionMode;
use open_agent_sdk::types::{Tool, ToolUseContext};
use serde_json::json;
use std::path::{Path, PathBuf};
fn bwrap_available() -> bool {
["/usr/bin/bwrap", "/bin/bwrap", "/usr/local/bin/bwrap"]
.iter()
.any(|p| Path::new(p).is_file())
sandbox_available()
}
fn tmp_session() -> PathBuf {
@@ -45,6 +45,7 @@ fn build_args_pin_session_dir_and_isolation() {
assert!(j.contains("--chdir /srv/sess/abc"));
assert!(args.iter().any(|a| a == "--unshare-all"));
assert!(!j.contains("/etc"));
assert!(!j.contains("/opt"));
}
#[tokio::test]
@@ -71,9 +72,18 @@ async fn default_mode_blocks_listing_root() {
let tool = SandboxedBashTool::new(tmp_session(), PermissionMode::Default);
let (out, _) = run(&tool, "ls /").await;
// /etc, /root, /home must be absent from the sandbox root.
assert!(!out.contains("etc"), "sandbox root must not expose /etc: {out}");
assert!(!out.contains("root"), "sandbox root must not expose /root: {out}");
assert!(!out.contains("home"), "sandbox root must not expose /home: {out}");
assert!(
!out.contains("etc"),
"sandbox root must not expose /etc: {out}"
);
assert!(
!out.contains("root"),
"sandbox root must not expose /root: {out}"
);
assert!(
!out.contains("home"),
"sandbox root must not expose /home: {out}"
);
}
#[tokio::test]
@@ -97,7 +107,10 @@ async fn default_mode_blocks_network() {
}
let tool = SandboxedBashTool::new(tmp_session(), PermissionMode::Default);
let (out, _) = run(&tool, "getent hosts example.com || echo NET-BLOCKED").await;
assert!(out.contains("NET-BLOCKED"), "network should be unshared: {out}");
assert!(
out.contains("NET-BLOCKED"),
"network should be unshared: {out}"
);
}
#[tokio::test]
@@ -116,10 +129,31 @@ async fn default_mode_allows_work_inside_sandbox() {
}
#[tokio::test]
async fn full_access_mode_is_unconfined() {
// FullAccess is the explicitly-dangerous mode: it should NOT be sandboxed,
// so it can read the host (this documents the intended escape hatch).
let tool = SandboxedBashTool::new(tmp_session(), PermissionMode::FullAccess);
let (out, _) = run(&tool, "test -e /etc && echo HOST-VISIBLE || echo no").await;
assert!(out.contains("HOST-VISIBLE"), "full access should see host: {out}");
async fn output_flood_is_bounded() {
if !bwrap_available() {
eprintln!("SKIP: sandbox runtime not available");
return;
}
let tool = SandboxedBashTool::new(tmp_session(), PermissionMode::Default);
let (out, _) = run(&tool, "yes x | head -c 250000").await;
assert!(out.len() < 101_000, "captured output exceeded hard cap");
assert!(out.contains("output truncated"));
}
#[tokio::test]
async fn full_access_mode_remains_confined() {
if !bwrap_available() {
eprintln!("SKIP: sandbox runtime not available");
return;
}
let tool = SandboxedBashTool::new(tmp_session(), PermissionMode::FullAccess);
let (out, _) = run(
&tool,
"test -e /etc/passwd && echo HOST-VISIBLE || echo BLOCKED",
)
.await;
assert!(
out.contains("BLOCKED"),
"full access must remain confined: {out}"
);
}
+7
View File
@@ -63,3 +63,10 @@ fn ordinary_new_file_still_allowed() {
let sb = tmp_sandbox();
assert!(validate_path_within_cwd("notes/today.md", &sb).is_ok());
}
#[test]
fn absolute_glob_pattern_is_rejected() {
let sb = tmp_sandbox();
let input = json!({ "pattern": "/etc/**" });
assert!(check_tool_paths("Glob", &input, &sb).is_err());
}
+8 -7
View File
@@ -22,7 +22,7 @@
#![cfg(unix)]
use boosapi::services::agent_workbench::bash_sandbox::SandboxedBashTool;
use boosapi::services::agent_workbench::bash_sandbox::{sandbox_available, SandboxedBashTool};
use boosapi::services::agent_workbench::permission::PermissionMode;
use open_agent_sdk::types::{Tool, ToolUseContext};
use serde_json::json;
@@ -30,14 +30,11 @@ use std::path::Path;
use std::path::PathBuf;
fn bwrap_available() -> bool {
["/usr/bin/bwrap", "/bin/bwrap", "/usr/local/bin/bwrap"]
.iter()
.any(|p| Path::new(p).is_file())
sandbox_available()
}
fn tmp_session() -> PathBuf {
let base =
std::env::temp_dir().join(format!("agent_toctou_{}", uuid::Uuid::new_v4().simple()));
let base = std::env::temp_dir().join(format!("agent_toctou_{}", uuid::Uuid::new_v4().simple()));
std::fs::create_dir_all(&base).unwrap();
base.canonicalize().unwrap()
}
@@ -60,7 +57,11 @@ async fn backgrounded_process_does_not_outlive_bash_call() {
let cmd = "( for i in $(seq 1 200); do echo x >> racer_marker; sleep 0.01; done ) & \
echo backgrounded";
let res = tool.call(json!({ "command": cmd }), &ctx).await.unwrap();
assert!(!res.is_error, "spawn call should succeed: {}", res.get_text());
assert!(
!res.is_error,
"spawn call should succeed: {}",
res.get_text()
);
let marker = sd.join("racer_marker");
let count_now = std::fs::read_to_string(&marker)