From 8e606f36ac3deedd9f9cf0029786357407e7a430 Mon Sep 17 00:00:00 2001 From: Florent Benoit Date: Fri, 18 Sep 2026 12:43:51 +0200 Subject: [PATCH] fix(sandbox-backend): raise control frame limit to 2 MiB The default macOS system CA bundle (~334 KB) inflates ~3.3x under serde's JSON byte-array encoding, pushing StartAgent past the 1 MiB MAX_CONTROL_FRAME_BYTES limit and failing every VM-backed sandbox create. Add a test that round-trips a 400 KB CA bundle through the frame encoder to pin the invariant. Closes #3453 Signed-off-by: Florent Benoit --- .../src/boundary_protocol.rs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/openshell-sandbox-backend/src/boundary_protocol.rs b/crates/openshell-sandbox-backend/src/boundary_protocol.rs index 05542fb2bc..4fa57b7afb 100644 --- a/crates/openshell-sandbox-backend/src/boundary_protocol.rs +++ b/crates/openshell-sandbox-backend/src/boundary_protocol.rs @@ -32,7 +32,7 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest as _, Sha256}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -pub const MAX_CONTROL_FRAME_BYTES: usize = 1024 * 1024; +pub const MAX_CONTROL_FRAME_BYTES: usize = 2 * 1024 * 1024; pub const STREAM_STDIN: u8 = 0; pub const STREAM_STDOUT: u8 = 1; pub const STREAM_STDERR: u8 = 2; @@ -1330,6 +1330,35 @@ mod tests { )); } + #[test] + fn start_agent_with_large_ca_bundle_fits_in_frame_limit() { + let request = RequestEnvelope::new(Request::StartAgent { + sandbox_id: "sandbox-1".to_string(), + spec: AgentSpecWire { + program: "/bin/true".to_string(), + args: Vec::new(), + workdir: None, + timeout_secs: 5, + interactive: false, + }, + policy: Box::new(SandboxPolicyWire::from(SandboxPolicy { + version: 1, + filesystem: FilesystemPolicy::default(), + network: NetworkPolicy::default(), + landlock: LandlockPolicy::default(), + process: ProcessPolicy::default(), + })), + ca_cert: Some(vec![0xAB; 16 * 1024]), + ca_bundle: Some(vec![0xCD; 400 * 1024]), + provider_env_revision: 0, + provider_env: std::collections::HashMap::new(), + }) + .expect("request envelope"); + let frame = encode_frame(&request).expect("large CA bundle must fit in frame limit"); + let decoded: RequestEnvelope = decode_frame(&frame).expect("round-trip"); + assert_eq!(decoded, request); + } + #[test] fn rejects_declared_oversize() { let oversized = u32::try_from(MAX_CONTROL_FRAME_BYTES + 1).expect("test size fits u32");