mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
sandboxing: migrate cwd inputs to PathUri (#27816)
## Why Sandbox cwd values can cross app-server and exec-server host boundaries. They should retain URI semantics until the receiving host validates them instead of being interpreted early as native paths. ## What - Carry `PathUri` through filesystem sandbox contexts, sandbox commands, and transform inputs. - Convert command and policy cwd once in `SandboxManager::transform`, then keep launch requests native. - Preserve sandbox cwd over remote filesystem transport and reject non-native URIs without fallback. - Cache paired native/URI turn-environment cwd values during migration, with immutable access to keep them synchronized. - Extend existing protocol, forwarding, transform, and core runtime tests.
This commit is contained in:
committed by
GitHub
Unverified
parent
84520225b9
commit
52a50aec70
@@ -15,6 +15,7 @@ use codex_sandboxing::SandboxTransformRequest;
|
||||
use codex_sandboxing::SandboxablePreference;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_absolute_path::canonicalize_preserving_symlinks;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
@@ -39,6 +40,12 @@ const FS_HELPER_BAZEL_BWRAP_ENV_ALLOWLIST: &[&str] = &[
|
||||
"TEST_WORKSPACE",
|
||||
];
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct SandboxCwd {
|
||||
uri: PathUri,
|
||||
native: AbsolutePathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FileSystemSandboxRunner {
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
@@ -65,7 +72,11 @@ impl FileSystemSandboxRunner {
|
||||
} else {
|
||||
helper_read_roots(&self.runtime_paths)
|
||||
};
|
||||
add_helper_runtime_permissions(&mut file_system_policy, &helper_read_roots, cwd.as_path());
|
||||
add_helper_runtime_permissions(
|
||||
&mut file_system_policy,
|
||||
&helper_read_roots,
|
||||
cwd.native.as_path(),
|
||||
);
|
||||
normalize_file_system_policy_root_aliases(&mut file_system_policy);
|
||||
let network_policy = NetworkSandboxPolicy::Restricted;
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
@@ -73,7 +84,7 @@ impl FileSystemSandboxRunner {
|
||||
&file_system_policy,
|
||||
network_policy,
|
||||
);
|
||||
let command = self.sandbox_exec_request(&permission_profile, &cwd, sandbox)?;
|
||||
let command = self.sandbox_exec_request(&permission_profile, &cwd.uri, sandbox)?;
|
||||
let request_json = serde_json::to_vec(&request).map_err(json_error)?;
|
||||
run_command(command, request_json).await
|
||||
}
|
||||
@@ -81,7 +92,7 @@ impl FileSystemSandboxRunner {
|
||||
fn sandbox_exec_request(
|
||||
&self,
|
||||
permission_profile: &PermissionProfile,
|
||||
cwd: &AbsolutePathBuf,
|
||||
cwd: &PathUri,
|
||||
sandbox_context: &FileSystemSandboxContext,
|
||||
) -> Result<SandboxExecRequest, JSONRPCErrorError> {
|
||||
let helper = &self.runtime_paths.codex_self_exe;
|
||||
@@ -108,7 +119,7 @@ impl FileSystemSandboxRunner {
|
||||
sandbox,
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
sandbox_policy_cwd: cwd.as_path(),
|
||||
sandbox_policy_cwd: cwd,
|
||||
codex_linux_sandbox_exe: self.runtime_paths.codex_linux_sandbox_exe.as_deref(),
|
||||
use_legacy_landlock: sandbox_context.use_legacy_landlock,
|
||||
windows_sandbox_level: sandbox_context.windows_sandbox_level,
|
||||
@@ -118,9 +129,12 @@ impl FileSystemSandboxRunner {
|
||||
}
|
||||
}
|
||||
|
||||
fn sandbox_cwd(sandbox: &FileSystemSandboxContext) -> Result<AbsolutePathBuf, JSONRPCErrorError> {
|
||||
if let Some(cwd) = &sandbox.cwd {
|
||||
return Ok(cwd.clone());
|
||||
fn sandbox_cwd(sandbox: &FileSystemSandboxContext) -> Result<SandboxCwd, JSONRPCErrorError> {
|
||||
if let Some(uri) = &sandbox.cwd {
|
||||
return Ok(SandboxCwd {
|
||||
native: native_sandbox_cwd(uri)?,
|
||||
uri: uri.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
if sandbox.has_cwd_dependent_permissions() {
|
||||
@@ -129,9 +143,22 @@ fn sandbox_cwd(sandbox: &FileSystemSandboxContext) -> Result<AbsolutePathBuf, JS
|
||||
));
|
||||
}
|
||||
|
||||
let cwd = current_sandbox_cwd().map_err(io_error)?;
|
||||
AbsolutePathBuf::from_absolute_path(cwd.as_path())
|
||||
.map_err(|err| invalid_request(format!("current directory is not absolute: {err}")))
|
||||
let native = AbsolutePathBuf::from_absolute_path(current_sandbox_cwd().map_err(io_error)?)
|
||||
.map_err(|err| invalid_request(format!("current directory is not absolute: {err}")))?;
|
||||
let uri = PathUri::from_abs_path(&native).map_err(|err| {
|
||||
invalid_request(format!(
|
||||
"current directory cannot be represented as a file URI: {err}"
|
||||
))
|
||||
})?;
|
||||
Ok(SandboxCwd { uri, native })
|
||||
}
|
||||
|
||||
fn native_sandbox_cwd(cwd: &PathUri) -> Result<AbsolutePathBuf, JSONRPCErrorError> {
|
||||
cwd.to_abs_path().map_err(|err| {
|
||||
invalid_request(format!(
|
||||
"file system sandbox cwd is not native to this exec-server host: {err}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn helper_read_roots(runtime_paths: &ExecServerRuntimePaths) -> Vec<AbsolutePathBuf> {
|
||||
@@ -327,11 +354,13 @@ mod tests {
|
||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
|
||||
use super::FileSystemSandboxRunner;
|
||||
use super::SandboxCwd;
|
||||
use super::add_helper_runtime_permissions;
|
||||
use super::helper_env;
|
||||
use super::helper_env_from_vars;
|
||||
@@ -489,9 +518,10 @@ mod tests {
|
||||
ExecServerRuntimePaths::new(codex_self_exe.clone(), Some(codex_self_exe))
|
||||
.expect("runtime paths");
|
||||
let runner = FileSystemSandboxRunner::new(runtime_paths);
|
||||
let cwd = AbsolutePathBuf::current_dir().expect("cwd");
|
||||
let native_cwd = AbsolutePathBuf::current_dir().expect("cwd");
|
||||
let cwd = PathUri::from_abs_path(&native_cwd).expect("cwd URI");
|
||||
let file_system_policy =
|
||||
restricted_policy(vec![path_entry(cwd.clone(), FileSystemAccessMode::Write)]);
|
||||
restricted_policy(vec![path_entry(native_cwd, FileSystemAccessMode::Write)]);
|
||||
let network_policy = NetworkSandboxPolicy::Restricted;
|
||||
let permission_profile =
|
||||
PermissionProfile::from_runtime_permissions(&file_system_policy, network_policy);
|
||||
@@ -506,15 +536,42 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn sandbox_cwd_uses_context_cwd() {
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path())
|
||||
let native_cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path())
|
||||
.expect("absolute cwd");
|
||||
let cwd = PathUri::from_abs_path(&native_cwd).expect("cwd URI");
|
||||
let policy = restricted_policy(vec![special_entry(
|
||||
FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
FileSystemAccessMode::Write,
|
||||
)]);
|
||||
let sandbox_context = sandbox_context_with_cwd(&policy, cwd.clone());
|
||||
|
||||
assert_eq!(sandbox_cwd(&sandbox_context).expect("sandbox cwd"), cwd);
|
||||
assert_eq!(
|
||||
sandbox_cwd(&sandbox_context).expect("sandbox cwd"),
|
||||
SandboxCwd {
|
||||
uri: cwd,
|
||||
native: native_cwd
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sandbox_cwd_rejects_non_native_context_cwd_without_fallback() {
|
||||
let cwd = non_native_cwd();
|
||||
let policy = restricted_policy(vec![special_entry(
|
||||
FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
FileSystemAccessMode::Write,
|
||||
)]);
|
||||
let sandbox_context = sandbox_context_with_cwd(&policy, cwd);
|
||||
|
||||
let err = sandbox_cwd(&sandbox_context).expect_err("non-native cwd should be rejected");
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
crate::rpc::invalid_request(
|
||||
"file system sandbox cwd is not native to this exec-server host: file URI contains an invalid absolute path"
|
||||
.to_string()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -595,7 +652,7 @@ mod tests {
|
||||
|
||||
fn sandbox_context_with_cwd(
|
||||
policy: &FileSystemSandboxPolicy,
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd: PathUri,
|
||||
) -> crate::FileSystemSandboxContext {
|
||||
crate::FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
PermissionProfile::from_runtime_permissions(policy, NetworkSandboxPolicy::Restricted),
|
||||
@@ -603,6 +660,15 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
fn non_native_cwd() -> PathUri {
|
||||
#[cfg(unix)]
|
||||
let uri = "file://server/share/checkout";
|
||||
#[cfg(windows)]
|
||||
let uri = "file:///usr/local/checkout";
|
||||
|
||||
PathUri::parse(uri).expect("non-native cwd URI")
|
||||
}
|
||||
|
||||
fn path_entry(path: AbsolutePathBuf, access: FileSystemAccessMode) -> FileSystemSandboxEntry {
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path },
|
||||
|
||||
@@ -450,6 +450,8 @@ mod base64_bytes {
|
||||
mod tests {
|
||||
use super::FsReadFileParams;
|
||||
use super::HttpRequestParams;
|
||||
use crate::FileSystemSandboxContext;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -458,14 +460,22 @@ mod tests {
|
||||
let legacy_path = std::env::current_dir()
|
||||
.expect("current directory")
|
||||
.join("legacy-file.txt");
|
||||
let legacy_cwd = std::env::current_dir().expect("current directory");
|
||||
let expected_sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
PermissionProfile::default(),
|
||||
PathUri::from_path(&legacy_cwd).expect("cwd URI"),
|
||||
);
|
||||
let mut legacy_sandbox =
|
||||
serde_json::to_value(&expected_sandbox).expect("sandbox should serialize");
|
||||
legacy_sandbox["cwd"] = serde_json::json!(legacy_cwd.to_string_lossy());
|
||||
let params: FsReadFileParams = serde_json::from_value(serde_json::json!({
|
||||
"path": legacy_path.to_string_lossy(),
|
||||
"sandbox": null,
|
||||
"sandbox": legacy_sandbox,
|
||||
}))
|
||||
.expect("legacy absolute path should deserialize");
|
||||
let expected = FsReadFileParams {
|
||||
path: PathUri::from_path(legacy_path).expect("path URI"),
|
||||
sandbox: None,
|
||||
sandbox: Some(expected_sandbox.clone()),
|
||||
};
|
||||
|
||||
assert_eq!(params, expected);
|
||||
@@ -473,7 +483,8 @@ mod tests {
|
||||
serde_json::to_value(params).expect("params should serialize"),
|
||||
serde_json::json!({
|
||||
"path": expected.path.to_string(),
|
||||
"sandbox": null,
|
||||
"sandbox": serde_json::to_value(expected_sandbox)
|
||||
.expect("sandbox should serialize"),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -321,6 +321,7 @@ mod tests {
|
||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
@@ -337,7 +338,7 @@ mod tests {
|
||||
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted);
|
||||
let sandbox_context = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
permissions,
|
||||
absolute_test_path("host-checkout"),
|
||||
path_uri("host-checkout"),
|
||||
);
|
||||
|
||||
let remote_context =
|
||||
@@ -356,7 +357,7 @@ mod tests {
|
||||
}]);
|
||||
let permissions =
|
||||
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted);
|
||||
let cwd = absolute_test_path("host-checkout");
|
||||
let cwd = path_uri("host-checkout");
|
||||
let sandbox_context =
|
||||
FileSystemSandboxContext::from_permission_profile_with_cwd(permissions, cwd.clone());
|
||||
|
||||
@@ -400,4 +401,8 @@ mod tests {
|
||||
let path = std::env::temp_dir().join(name);
|
||||
AbsolutePathBuf::from_absolute_path(&path).expect("absolute path")
|
||||
}
|
||||
|
||||
fn path_uri(name: &str) -> PathUri {
|
||||
PathUri::from_abs_path(&absolute_test_path(name)).expect("path URI")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
use codex_app_server_protocol::JSONRPCMessage;
|
||||
use codex_app_server_protocol::JSONRPCResponse;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use futures::SinkExt;
|
||||
use futures::StreamExt;
|
||||
@@ -25,9 +32,9 @@ use crate::protocol::INITIALIZED_METHOD;
|
||||
use crate::protocol::InitializeResponse;
|
||||
|
||||
#[tokio::test]
|
||||
async fn remote_file_system_sends_path_uris_without_native_conversion() {
|
||||
let (websocket_url, captured_paths, server) =
|
||||
record_read_file_paths(/*expected_requests*/ 2).await;
|
||||
async fn remote_file_system_sends_path_and_sandbox_cwd_uris_without_native_conversion() {
|
||||
let (websocket_url, captured_params, server) =
|
||||
record_read_file_params(/*expected_requests*/ 2).await;
|
||||
let file_system = RemoteFileSystem::new(LazyRemoteExecServerClient::new(
|
||||
ExecServerTransportParams::websocket_url(websocket_url),
|
||||
));
|
||||
@@ -35,33 +42,54 @@ async fn remote_file_system_sends_path_uris_without_native_conversion() {
|
||||
PathUri::parse("file:///C:/Users/Alice/src/main.rs").expect("valid drive URI"),
|
||||
PathUri::parse("file://server/share/src/main.rs").expect("valid UNC URI"),
|
||||
];
|
||||
let sandbox_cwd = non_native_cwd();
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}]);
|
||||
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
|
||||
sandbox_cwd,
|
||||
);
|
||||
|
||||
for path in &paths {
|
||||
assert_eq!(
|
||||
file_system
|
||||
.read_file(path, /*sandbox*/ None)
|
||||
.read_file(path, Some(&sandbox))
|
||||
.await
|
||||
.expect("remote read should succeed"),
|
||||
Vec::<u8>::new()
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(captured_paths.await.expect("captured paths"), paths);
|
||||
let expected_params = paths
|
||||
.into_iter()
|
||||
.map(|path| FsReadFileParams {
|
||||
path,
|
||||
sandbox: Some(sandbox.clone()),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
captured_params.await.expect("captured params"),
|
||||
expected_params
|
||||
);
|
||||
server.await.expect("recording server should succeed");
|
||||
}
|
||||
|
||||
async fn record_read_file_paths(
|
||||
async fn record_read_file_params(
|
||||
expected_requests: usize,
|
||||
) -> (
|
||||
String,
|
||||
oneshot::Receiver<Vec<PathUri>>,
|
||||
oneshot::Receiver<Vec<FsReadFileParams>>,
|
||||
tokio::task::JoinHandle<()>,
|
||||
) {
|
||||
let listener = TcpListener::bind("127.0.0.1:0")
|
||||
.await
|
||||
.expect("listener should bind");
|
||||
let websocket_url = format!("ws://{}", listener.local_addr().expect("listener address"));
|
||||
let (captured_paths_tx, captured_paths_rx) = oneshot::channel();
|
||||
let (captured_params_tx, captured_params_rx) = oneshot::channel();
|
||||
let server = tokio::spawn(async move {
|
||||
let (stream, _) = listener.accept().await.expect("listener should accept");
|
||||
let mut websocket = accept_async(stream)
|
||||
@@ -69,7 +97,7 @@ async fn record_read_file_paths(
|
||||
.expect("websocket handshake should succeed");
|
||||
complete_websocket_initialize(&mut websocket).await;
|
||||
|
||||
let mut captured_paths = Vec::with_capacity(expected_requests);
|
||||
let mut captured_params = Vec::with_capacity(expected_requests);
|
||||
for _ in 0..expected_requests {
|
||||
let request = match read_jsonrpc_websocket(&mut websocket).await {
|
||||
JSONRPCMessage::Request(request) if request.method == FS_READ_FILE_METHOD => {
|
||||
@@ -80,7 +108,7 @@ async fn record_read_file_paths(
|
||||
let params: FsReadFileParams =
|
||||
serde_json::from_value(request.params.expect("fs/readFile params should exist"))
|
||||
.expect("fs/readFile params should deserialize");
|
||||
captured_paths.push(params.path);
|
||||
captured_params.push(params);
|
||||
write_jsonrpc_websocket(
|
||||
&mut websocket,
|
||||
JSONRPCMessage::Response(JSONRPCResponse {
|
||||
@@ -93,12 +121,21 @@ async fn record_read_file_paths(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
captured_paths_tx
|
||||
.send(captured_paths)
|
||||
.expect("captured paths receiver should stay open");
|
||||
captured_params_tx
|
||||
.send(captured_params)
|
||||
.expect("captured params receiver should stay open");
|
||||
});
|
||||
|
||||
(websocket_url, captured_paths_rx, server)
|
||||
(websocket_url, captured_params_rx, server)
|
||||
}
|
||||
|
||||
fn non_native_cwd() -> PathUri {
|
||||
#[cfg(unix)]
|
||||
let uri = "file://server/share/checkout";
|
||||
#[cfg(windows)]
|
||||
let uri = "file:///usr/local/checkout";
|
||||
|
||||
PathUri::parse(uri).expect("non-native cwd URI")
|
||||
}
|
||||
|
||||
async fn complete_websocket_initialize(websocket: &mut WebSocketStream<TcpStream>) {
|
||||
|
||||
@@ -189,7 +189,6 @@ fn map_fs_error(err: io::Error) -> JSONRPCErrorError {
|
||||
mod tests {
|
||||
use codex_protocol::protocol::NetworkAccess;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -207,8 +206,14 @@ mod tests {
|
||||
)
|
||||
.expect("runtime paths");
|
||||
let handler = FileSystemHandler::new(runtime_paths);
|
||||
let sandbox_cwd =
|
||||
AbsolutePathBuf::from_absolute_path(temp_dir.path()).expect("absolute tempdir");
|
||||
let sandbox_cwd = PathUri::from_path(temp_dir.path()).expect("tempdir URI");
|
||||
let sandbox_context = |sandbox_policy| {
|
||||
FileSystemSandboxContext::from_legacy_sandbox_policy(
|
||||
sandbox_policy,
|
||||
sandbox_cwd.clone(),
|
||||
)
|
||||
.expect("sandbox context")
|
||||
};
|
||||
|
||||
for (file_name, sandbox_policy) in [
|
||||
("danger.txt", SandboxPolicy::DangerFullAccess),
|
||||
@@ -225,10 +230,7 @@ mod tests {
|
||||
.write_file(FsWriteFileParams {
|
||||
path: path.clone(),
|
||||
data_base64: STANDARD.encode("ok"),
|
||||
sandbox: Some(FileSystemSandboxContext::from_legacy_sandbox_policy(
|
||||
sandbox_policy.clone(),
|
||||
sandbox_cwd.clone(),
|
||||
)),
|
||||
sandbox: Some(sandbox_context(sandbox_policy.clone())),
|
||||
})
|
||||
.await
|
||||
.expect("write file");
|
||||
@@ -236,10 +238,7 @@ mod tests {
|
||||
let canonicalized = handler
|
||||
.canonicalize(FsCanonicalizeParams {
|
||||
path: path.clone(),
|
||||
sandbox: Some(FileSystemSandboxContext::from_legacy_sandbox_policy(
|
||||
sandbox_policy.clone(),
|
||||
sandbox_cwd.clone(),
|
||||
)),
|
||||
sandbox: Some(sandbox_context(sandbox_policy.clone())),
|
||||
})
|
||||
.await
|
||||
.expect("canonicalize file");
|
||||
@@ -254,10 +253,7 @@ mod tests {
|
||||
let response = handler
|
||||
.read_file(FsReadFileParams {
|
||||
path,
|
||||
sandbox: Some(FileSystemSandboxContext::from_legacy_sandbox_policy(
|
||||
sandbox_policy,
|
||||
sandbox_cwd.clone(),
|
||||
)),
|
||||
sandbox: Some(sandbox_context(sandbox_policy)),
|
||||
})
|
||||
.await
|
||||
.expect("read file");
|
||||
|
||||
Reference in New Issue
Block a user