oct.mcp.sandbox module#
Purpose#
Execution Sandbox (Layer 4, outer wrapper) for the oct-mcp
six-layer pipeline. Provides environment sanitisation, working-directory
pinning, timeout enforcement, and output-size limiting for subprocess
execution.
Phase 5C adds pluggable OS-level sandbox backends:
NativeBackend— existing behavior (subprocess + env sanitization). Always available; default on Windows.BubblewrapBackend— Linux only. Wraps every command inbwrap --unshare-net --die-with-parent. Requiresbwrapin PATH.SandboxExecBackend— macOS only. Prependssandbox-exec -p "(deny network*)(allow default)". Requiressandbox-execin PATH.
Use make_sandbox_backend() to select the appropriate backend based
on McpConfig.
Responsibilities#
Strip sensitive environment variables before forking (
PYTHONPATH, common secret-named vars).Pin the subprocess working directory to the validated project root.
Enforce per-tool timeouts and kill the process on expiry.
Cap combined stdout+stderr output to
max_output_bytes.Never use
shell=True.Return a
SandboxResult— never raise for subprocess errors.
Diagnostics#
Domain: MCP Levels:
L1 — errors: process launch failures, unexpected errors L2 — lifecycle: process start, process end, backend selection L3 — command details (argv) L4 — deep trace: per-line output, env keys
Contracts#
shell=Falseis enforced unconditionally in all backends — no caller path can override it.SandboxExecutor.run()never raises. AnyOSErrororsubprocess.SubprocessErroris caught, logged at level 1, and returned as aSandboxResultwith a non-zero exit code.The sanitised environment always preserves
PATH,HOME,LANG,TERM,USER,USERNAME,LOGNAME,VIRTUAL_ENV,CONDA_DEFAULT_ENV, andOCT_MCP_SESSION_ID/OCT_MCP_REQUEST_IDcorrelation IDs.make_sandbox_backend()never raises — it always returns a usable backend (falls back toNativeBackend).
- class oct.mcp.sandbox.BubblewrapBackend[source]#
Bases:
objectLinux bubblewrap backend — network-isolated, die-with-parent.
Wraps every command in
bwrap --unshare-net --die-with-parent. Requiresbwrapto be available in PATH.Network isolation is provided by
--unshare-net. The project root is bind-mounted read-write; system paths are read-only.
- class oct.mcp.sandbox.NativeBackend[source]#
Bases:
objectSubprocess-only backend — existing Phase 5A/5B behavior.
On POSIX, when memory_limit_bytes is non-None and non-zero, applies
resource.setrlimit(RLIMIT_AS, ...)viapreexec_fnto limit virtual address space. On Windows the limit is silently ignored (use Docker for Windows memory enforcement).
- class oct.mcp.sandbox.SandboxBackend(*args, **kwargs)[source]#
Bases:
ProtocolProtocol for OS-level sandbox backends.
Each backend wraps the subprocess call with platform-specific isolation (network namespace, resource limits, etc.). The backend receives a sanitised environment and returns raw
(exit_code, stdout, stderr)— output capping and error handling live inSandboxExecutor.- run(cmd: list[str], env: dict[str, str], cwd: Path, timeout: int, memory_limit_bytes: int | None) tuple[int, str, str][source]#
Run cmd and return
(exit_code, stdout, stderr).- Parameters:
cmd – Command + arguments list. Must not use
shell=True.env – Sanitised environment dict.
cwd – Working directory (validated project root).
timeout – Maximum wall-clock seconds.
memory_limit_bytes – Virtual memory ceiling in bytes, or
Noneto skip. Implementations on POSIX apply this viasetrlimit; on Windows it is silently ignored.
- class oct.mcp.sandbox.SandboxExecBackend[source]#
Bases:
objectmacOS
sandbox-execbackend — network-denied policy.Prepends
sandbox-exec -p "<profile>"to every command. Requiressandbox-execto be available in PATH.The policy denies all network access while allowing everything else (file I/O, process creation, etc.) needed to run
octcommands.
- class oct.mcp.sandbox.SandboxExecutor(backend: SandboxBackend | None = None, memory_limit_mb: int = 0)[source]#
Bases:
objectSandboxed subprocess runner for the MCP execution layer.
Wraps a
SandboxBackendwith environment sanitisation, output capping, and error handling. A singleSandboxExecutorinstance may be reused across multiple tool calls within the same server session.- Parameters:
backend – OS-level isolation backend. Defaults to
NativeBackendwhen not supplied (Phase 5A/5B backward compatibility).memory_limit_mb – Per-subprocess virtual memory limit in megabytes.
0means no limit. Forwarded to the backend as bytes. POSIX only — silently ignored on Windows.
- run(cmd: list[str], cwd: Path, timeout: int, max_output_bytes: int = 1048576) SandboxResult[source]#
Execute cmd in a sanitised environment under cwd.
- Parameters:
cmd – The command and arguments to run. Must not contain shell metacharacters — this is enforced by passing
shell=Falsein every backend.cwd – Working directory, pinned to the validated project root.
timeout – Maximum wall-clock seconds before the process is killed.
max_output_bytes – Combined stdout+stderr cap. When exceeded, output is truncated and
SandboxResult.output_truncatedis set.
- Return type:
SandboxResult— never raises.
- class oct.mcp.sandbox.SandboxResult(exit_code: int, stdout: str, stderr: str, timed_out: bool = False, output_truncated: bool = False, error_message: str = '')[source]#
Bases:
objectResult of a sandboxed subprocess execution.
- exit_code#
The subprocess return code, or
1on launch failure.- Type:
int
- stdout#
Captured standard output (possibly truncated).
- Type:
str
- stderr#
Captured standard error (possibly truncated).
- Type:
str
- timed_out#
Trueif the process was killed due to timeout.- Type:
bool
- output_truncated#
Trueif combined stdout+stderr exceededmax_output_bytes.- Type:
bool
- error_message#
Non-empty if the process could not be launched (e.g. not found on PATH). Distinct from stderr — set on
OSError.- Type:
str
- error_message: str = ''#
- exit_code: int#
- output_truncated: bool = False#
- stderr: str#
- stdout: str#
- timed_out: bool = False#
- oct.mcp.sandbox.make_sandbox_backend(config: object) SandboxBackend[source]#
Select and return the best available
SandboxBackend.- Parameters:
config – A
McpConfiginstance. Reads thesandbox_backendfield ("auto"/"native"/"bubblewrap"/"sandbox_exec") andmemory_limit_mb.- Returns:
A concrete backend instance. On
"auto", picks*
BubblewrapBackendon Linux whenbwrapis in PATH.*
SandboxExecBackendon macOS whensandbox-execis in PATH.*
NativeBackendotherwise (always safe).When a specific backend is forced (e.g.
"bubblewrap") but itsbinary is not found, raises
RuntimeError.Never raises on
"auto"or"native"— always returns ausable backend.