Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/execution/tests/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,14 @@ export async function loadPyodide(options) {
},
)
.expect("respond to read_dir"),
PythonVfsRpcMethod::Unlink
| PythonVfsRpcMethod::Rmdir
| PythonVfsRpcMethod::Rename => {
panic!(
"unexpected mutating-FS Python RPC in this test: {:?}",
request.method
)
}
PythonVfsRpcMethod::HttpRequest
| PythonVfsRpcMethod::DnsLookup
| PythonVfsRpcMethod::SubprocessRun => {
Expand Down
50 changes: 40 additions & 10 deletions crates/execution/tests/python_prewarm.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use secure_exec_execution::{
CreatePythonContextRequest, PythonExecutionEngine, StartPythonExecutionRequest,
CreatePythonContextRequest, PythonExecutionEngine, PythonExecutionEvent,
StartPythonExecutionRequest,
};
use serde_json::Value;
use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::Duration;
use tempfile::tempdir;

const PYTHON_WARMUP_METRICS_PREFIX: &str = "__AGENTOS_PYTHON_WARMUP_METRICS__:";
Expand All @@ -27,7 +29,7 @@ fn run_python_execution(
cwd: &Path,
code: &str,
) -> (String, String, i32) {
let result = engine
let mut execution = engine
.start_execution(StartPythonExecutionRequest {
guest_runtime: Default::default(),
limits: Default::default(),
Expand All @@ -41,15 +43,43 @@ fn run_python_execution(
)]),
cwd: cwd.to_path_buf(),
})
.expect("start Python execution")
.wait(None)
.expect("wait for Python execution");
.expect("start Python execution");

(
String::from_utf8(result.stdout).expect("stdout utf8"),
String::from_utf8(result.stderr).expect("stderr utf8"),
result.exit_code,
)
// Drive the event loop directly instead of `.wait()`: the Pyodide runner
// now sets up a kernel-VFS-backed site-packages on boot, which emits VFS
// RPCs. This prewarm test has no VFS backend, so reject those RPCs and let
// the runner's best-effort site-packages setup degrade. Module-resolution
// sync RPCs are serviced host-directly.
let mut stdout = Vec::new();
let mut stderr = Vec::new();
loop {
match execution
.poll_event_blocking(Duration::from_secs(60))
.expect("poll Python event")
{
Some(PythonExecutionEvent::Stdout(chunk)) => stdout.extend(chunk),
Some(PythonExecutionEvent::Stderr(chunk)) => stderr.extend(chunk),
Some(PythonExecutionEvent::JavascriptSyncRpcRequest(request)) => {
let serviced = execution
.try_service_standalone_module_sync_rpc(&request)
.expect("service module sync RPC");
assert!(serviced, "unexpected JS sync RPC request: {request:?}");
}
Some(PythonExecutionEvent::VfsRpcRequest(request)) => {
execution
.respond_vfs_rpc_error(request.id, "ENOSYS", "no VFS backend in prewarm test")
.expect("respond to VFS RPC");
}
Some(PythonExecutionEvent::Exited(exit_code)) => {
return (
String::from_utf8(stdout).expect("stdout utf8"),
String::from_utf8(stderr).expect("stderr utf8"),
exit_code,
);
}
None => panic!("timed out waiting for Python execution event"),
}
}
}

fn parse_metrics(stderr: &str, phase: &str) -> Value {
Expand Down
9 changes: 4 additions & 5 deletions crates/sidecar/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ use crate::protocol::{
OwnershipScope, ProcessExitedEvent, ProcessKilledResponse, ProcessOutputEvent,
ProcessSnapshotEntry, ProcessSnapshotResponse, ProcessSnapshotStatus, ProcessStartedResponse,
PtyResizedResponse, RequestFrame, ResizePtyRequest, ResponseFrame, ResponsePayload,
SidecarRequestPayload, SignalDispositionAction,
SignalHandlerRegistration, SignalStateResponse, SocketStateEntry, StdinClosedResponse,
StdinWrittenResponse, StreamChannel, VmFetchRequest, VmFetchResponse, WasmPermissionTier,
WriteStdinRequest, ZombieTimerCountResponse,
SidecarRequestPayload, SignalDispositionAction, SignalHandlerRegistration, SignalStateResponse,
SocketStateEntry, StdinClosedResponse, StdinWrittenResponse, StreamChannel, VmFetchRequest,
VmFetchResponse, WasmPermissionTier, WriteStdinRequest, ZombieTimerCountResponse,
};
use crate::service::{
audit_fields, dirname, emit_security_audit_event, emit_structured_event, javascript_error,
Expand Down Expand Up @@ -16810,7 +16809,7 @@ fn install_kernel_stdin_pipe(kernel: &mut SidecarKernel, pid: u32) -> Result<u32
.fd_close(EXECUTION_DRIVER_NAME, pid, read_fd)
.map_err(kernel_error)?;
Ok(write_fd)
}
}

fn requested_pty_window_size(env: &BTreeMap<String, String>) -> Option<(u16, u16)> {
let cols = env
Expand Down
14 changes: 14 additions & 0 deletions crates/sidecar/tests/architecture_guards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ const FS_ALLOW: &[&str] = &[
"crates/execution/src/javascript.rs",
"crates/execution/src/node_import_cache.rs",
"crates/execution/src/runtime_support.rs",
// Host-side V8 diagnostics: module-trace and sync-RPC latency profilers
// write to an operator-provided file path, and snapshot bootstrap reads the
// userland bundle from PI_SNAPSHOT_BUNDLE_PATH. Host-only, not guest-reachable.
"crates/v8-runtime/src/execution.rs",
"crates/v8-runtime/src/host_call.rs",
"crates/v8-runtime/src/snapshot.rs",
];

/// net: host network access.
Expand Down Expand Up @@ -323,6 +329,14 @@ const ENV_ALLOW: &[&str] = &[
"crates/v8-runtime/src/bridge.rs",
"crates/sidecar/src/execution.rs",
"crates/sidecar/src/plugins/s3_common.rs",
// Host-process startup log-level knob, read before any VM exists.
"crates/sidecar/src/main.rs",
// Host-side V8 diagnostics toggles (module-trace + sync-RPC latency
// profiling + snapshot-bundle path), read at runtime init from operator
// env. Not guest-reachable.
"crates/v8-runtime/src/execution.rs",
"crates/v8-runtime/src/host_call.rs",
"crates/v8-runtime/src/snapshot.rs",
];

fn fs_class() -> BannedClass {
Expand Down
4 changes: 3 additions & 1 deletion crates/sidecar/tests/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use secure_exec_vm_config::{
};
use serde_json::json;

const SIDECAR_FRAME_CAP: usize = 1024 * 1024;
// Must match the production sidecar wire frame cap (wire::DEFAULT_MAX_FRAME_BYTES),
// which is what vm_limits_from_config is called with at runtime (lib.rs/state.rs).
const SIDECAR_FRAME_CAP: usize = 16 * 1024 * 1024;

#[test]
fn defaults_match_struct_default() {
Expand Down
2 changes: 2 additions & 0 deletions crates/sidecar/tests/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2850,6 +2850,7 @@ print(json.dumps(result))
);
}

#[allow(clippy::too_many_arguments)]
fn execute_python_cli(
sidecar: &mut secure_exec_sidecar::NativeSidecar<support::RecordingBridge>,
request_id: RequestId,
Expand Down Expand Up @@ -2885,6 +2886,7 @@ fn execute_python_cli(
}
}

#[allow(clippy::too_many_arguments)]
fn execute_python_cli_with_env(
sidecar: &mut secure_exec_sidecar::NativeSidecar<support::RecordingBridge>,
request_id: RequestId,
Expand Down
22 changes: 13 additions & 9 deletions crates/sidecar/tests/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10344,24 +10344,28 @@ await new Promise(() => {});
};
cleanup_fake_runtime_process(process);
}
fn python_vfs_rpc_paths_are_scoped_to_workspace_root() {
fn python_vfs_rpc_paths_resolve_textually_and_defer_to_kernel_confinement() {
// Root is `/`: any absolute guest path is addressable and textual
// `.`/`..` segments are resolved here; confinement is enforced at the
// kernel/mount layer (openat2 RESOLVE_BENEATH), not by a prefix check.
assert_eq!(
crate::filesystem::normalize_python_vfs_rpc_path("/workspace/./note.txt")
.expect("normalize workspace path"),
String::from("/workspace/note.txt")
);
assert!(
assert_eq!(
crate::filesystem::normalize_python_vfs_rpc_path("/workspace/../etc/passwd")
.is_err(),
"workspace escape should be rejected",
.expect("normalize resolves .. textually"),
String::from("/etc/passwd")
);
assert!(
crate::filesystem::normalize_python_vfs_rpc_path("/etc/passwd").is_err(),
"non-workspace paths should be rejected",
assert_eq!(
crate::filesystem::normalize_python_vfs_rpc_path("/etc/passwd")
.expect("absolute guest paths are addressable"),
String::from("/etc/passwd")
);
assert!(
crate::filesystem::normalize_python_vfs_rpc_path("workspace/note.txt").is_err(),
"relative paths should be rejected",
"relative paths must be rejected",
);
}
fn javascript_fs_sync_rpc_resolves_proc_self_against_the_kernel_process() {
Expand Down Expand Up @@ -17483,7 +17487,7 @@ console.log(JSON.stringify({
command_resolution_rejects_unknown_command();
python_vfs_rpc_requests_proxy_into_the_vm_kernel_filesystem();
javascript_sync_rpc_requests_proxy_into_the_vm_kernel_filesystem();
python_vfs_rpc_paths_are_scoped_to_workspace_root();
python_vfs_rpc_paths_resolve_textually_and_defer_to_kernel_confinement();
javascript_fs_sync_rpc_resolves_proc_self_against_the_kernel_process();
javascript_fd_and_stream_rpc_requests_proxy_into_the_vm_kernel_filesystem();
javascript_mapped_tmp_open_wx_uses_exclusive_create_once();
Expand Down
10 changes: 9 additions & 1 deletion registry/native/crates/commands/sh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@ description = "sh standalone binary for secure-exec VM"
name = "sh"
path = "src/main.rs"

[dependencies]
# `reedline` (brush's interactive line editor) pulls brush-interactive's
# tokio block_in_place / Handle::block_on paths, which don't compile or run on
# single-threaded wasm. The wasm sandbox shell runs scripts, not an interactive
# TTY REPL, so enable reedline only on native targets and keep `minimal` on wasm
# (the pre-#137 behavior that built cleanly for wasm32-wasip1).
[target.'cfg(any(unix, windows))'.dependencies]
brush-shell = { version = "0.3.0", default-features = false, features = ["reedline", "minimal"] }

[target.'cfg(not(any(unix, windows)))'.dependencies]
brush-shell = { version = "0.3.0", default-features = false, features = ["minimal"] }
65 changes: 33 additions & 32 deletions registry/native/patches/crates/fd-lock/0001-wasi-support.patch
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
diff -ruN '--exclude=*.orig' a/src/sys/mod.rs b/src/sys/mod.rs
--- a/src/sys/mod.rs 2026-06-27 14:11:52.511433635 -0700
+++ b/src/sys/mod.rs 2026-06-27 14:11:52.511433635 -0700
@@ -13,6 +13,5 @@
diff -ruN a/src/sys/mod.rs b/src/sys/mod.rs
--- a/src/sys/mod.rs 2026-06-27 16:47:16.865093521 -0700
+++ b/src/sys/mod.rs 2026-06-27 16:47:48.549140973 -0700
@@ -12,6 +12,6 @@
pub(crate) use std::os::windows::io::AsHandle as AsOpenFile;
} else {
mod unsupported;
pub use unsupported::*;
- pub(crate) use std::os::fd::AsFd as AsOpenFile;
- pub use unsupported;
+ pub use unsupported::*;
}
}
diff -ruN '--exclude=*.orig' a/src/sys/unsupported/mod.rs b/src/sys/unsupported/mod.rs
--- a/src/sys/unsupported/mod.rs 2026-06-27 14:11:52.511433635 -0700
+++ b/src/sys/unsupported/mod.rs 2026-06-27 14:11:52.515433634 -0700
diff -ruN a/src/sys/unsupported/mod.rs b/src/sys/unsupported/mod.rs
--- a/src/sys/unsupported/mod.rs 2026-06-27 16:47:16.865093521 -0700
+++ b/src/sys/unsupported/mod.rs 2026-06-27 16:47:48.549140973 -0700
@@ -2,8 +2,10 @@
mod rw_lock;
mod write_guard;
Expand All @@ -24,31 +25,31 @@ diff -ruN '--exclude=*.orig' a/src/sys/unsupported/mod.rs b/src/sys/unsupported/
+pub(crate) trait AsOpenFile {}
+
+impl<T> AsOpenFile for T {}
diff -ruN '--exclude=*.orig' a/src/sys/unsupported/read_guard.rs b/src/sys/unsupported/read_guard.rs
--- a/src/sys/unsupported/read_guard.rs 2026-06-27 14:11:52.511433635 -0700
+++ b/src/sys/unsupported/read_guard.rs 2026-06-27 14:11:52.515433634 -0700
diff -ruN a/src/sys/unsupported/read_guard.rs b/src/sys/unsupported/read_guard.rs
--- a/src/sys/unsupported/read_guard.rs 2026-06-27 16:47:16.865093521 -0700
+++ b/src/sys/unsupported/read_guard.rs 2026-06-27 16:47:48.553140979 -0700
@@ -1,31 +1,28 @@
use std::ops;
-use std::os::fd::AsFd;
-use std::os::unix::io::AsRawFd;

-use super::RwLock;
+use super::{AsOpenFile, RwLock};

#[derive(Debug)]
-pub struct RwLockReadGuard<'lock, T: AsFd> {
-pub struct RwLockReadGuard<'lock, T: AsRawFd> {
+pub struct RwLockReadGuard<'lock, T: AsOpenFile> {
lock: &'lock RwLock<T>,
}

-impl<'lock, T: AsFd> RwLockReadGuard<'lock, T> {
-impl<'lock, T: AsRawFd> RwLockReadGuard<'lock, T> {
+impl<'lock, T: AsOpenFile> RwLockReadGuard<'lock, T> {
pub(crate) fn new(lock: &'lock RwLock<T>) -> Self {
- panic!("target unsupported")
+ Self { lock }
}
}

-impl<T: AsFd> ops::Deref for RwLockReadGuard<'_, T> {
-impl<T: AsRawFd> ops::Deref for RwLockReadGuard<'_, T> {
+impl<T: AsOpenFile> ops::Deref for RwLockReadGuard<'_, T> {
type Target = T;

Expand All @@ -59,32 +60,32 @@ diff -ruN '--exclude=*.orig' a/src/sys/unsupported/read_guard.rs b/src/sys/unsup
}
}

-impl<T: AsFd> Drop for RwLockReadGuard<'_, T> {
-impl<T: AsRawFd> Drop for RwLockReadGuard<'_, T> {
+impl<T: AsOpenFile> Drop for RwLockReadGuard<'_, T> {
#[inline]
- fn drop(&mut self) {
- panic!("target unsupported")
- }
+ fn drop(&mut self) {}
}
diff -ruN '--exclude=*.orig' a/src/sys/unsupported/rw_lock.rs b/src/sys/unsupported/rw_lock.rs
--- a/src/sys/unsupported/rw_lock.rs 2026-06-27 14:11:52.511433635 -0700
+++ b/src/sys/unsupported/rw_lock.rs 2026-06-27 14:11:52.515433634 -0700
diff -ruN a/src/sys/unsupported/rw_lock.rs b/src/sys/unsupported/rw_lock.rs
--- a/src/sys/unsupported/rw_lock.rs 2026-06-27 16:47:16.865093521 -0700
+++ b/src/sys/unsupported/rw_lock.rs 2026-06-27 16:47:48.553140979 -0700
@@ -1,37 +1,36 @@
-use std::io::{self, Error, ErrorKind};
-use std::os::fd::AsFd;
-use std::os::unix::io::AsRawFd;
+use std::io::{self, Error};

-use super::{RwLockReadGuard, RwLockWriteGuard};
+use super::{AsOpenFile, RwLockReadGuard, RwLockWriteGuard};

#[derive(Debug)]
-pub struct RwLock<T: AsFd> {
-pub struct RwLock<T: AsRawFd> {
+pub struct RwLock<T: AsOpenFile> {
pub(crate) inner: T,
}

-impl<T: AsFd> RwLock<T> {
-impl<T: AsRawFd> RwLock<T> {
+impl<T: AsOpenFile> RwLock<T> {
#[inline]
pub fn new(inner: T) -> Self {
Expand Down Expand Up @@ -125,31 +126,31 @@ diff -ruN '--exclude=*.orig' a/src/sys/unsupported/rw_lock.rs b/src/sys/unsuppor
+ self.inner
}
}
diff -ruN '--exclude=*.orig' a/src/sys/unsupported/write_guard.rs b/src/sys/unsupported/write_guard.rs
--- a/src/sys/unsupported/write_guard.rs 2026-06-27 14:11:52.511433635 -0700
+++ b/src/sys/unsupported/write_guard.rs 2026-06-27 14:11:52.515433634 -0700
diff -ruN a/src/sys/unsupported/write_guard.rs b/src/sys/unsupported/write_guard.rs
--- a/src/sys/unsupported/write_guard.rs 2026-06-27 16:47:16.865093521 -0700
+++ b/src/sys/unsupported/write_guard.rs 2026-06-27 16:47:48.553140979 -0700
@@ -1,38 +1,35 @@
use std::ops;
-use std::os::fd::AsFd;
-use std::os::unix::io::AsRawFd;

-use super::RwLock;
+use super::{AsOpenFile, RwLock};

#[derive(Debug)]
-pub struct RwLockWriteGuard<'lock, T: AsFd> {
-pub struct RwLockWriteGuard<'lock, T: AsRawFd> {
+pub struct RwLockWriteGuard<'lock, T: AsOpenFile> {
lock: &'lock mut RwLock<T>,
}

-impl<'lock, T: AsFd> RwLockWriteGuard<'lock, T> {
-impl<'lock, T: AsRawFd> RwLockWriteGuard<'lock, T> {
+impl<'lock, T: AsOpenFile> RwLockWriteGuard<'lock, T> {
pub(crate) fn new(lock: &'lock mut RwLock<T>) -> Self {
- panic!("target unsupported")
+ Self { lock }
}
}

-impl<T: AsFd> ops::Deref for RwLockWriteGuard<'_, T> {
-impl<T: AsRawFd> ops::Deref for RwLockWriteGuard<'_, T> {
+impl<T: AsOpenFile> ops::Deref for RwLockWriteGuard<'_, T> {
type Target = T;

Expand All @@ -160,7 +161,7 @@ diff -ruN '--exclude=*.orig' a/src/sys/unsupported/write_guard.rs b/src/sys/unsu
}
}

-impl<T: AsFd> ops::DerefMut for RwLockWriteGuard<'_, T> {
-impl<T: AsRawFd> ops::DerefMut for RwLockWriteGuard<'_, T> {
+impl<T: AsOpenFile> ops::DerefMut for RwLockWriteGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
Expand All @@ -169,7 +170,7 @@ diff -ruN '--exclude=*.orig' a/src/sys/unsupported/write_guard.rs b/src/sys/unsu
}
}

-impl<T: AsFd> Drop for RwLockWriteGuard<'_, T> {
-impl<T: AsRawFd> Drop for RwLockWriteGuard<'_, T> {
+impl<T: AsOpenFile> Drop for RwLockWriteGuard<'_, T> {
#[inline]
- fn drop(&mut self) {
Expand Down
Loading
Loading