Option C Tools
Loading...
Searching...
No Matches
oct.mcp.sandbox Namespace Reference

Classes

class  SandboxResult
class  SandboxBackend
class  NativeBackend
class  BubblewrapBackend
class  SandboxExecBackend
class  SandboxExecutor

Functions

None _dbg (*args, **kwargs)
dict[str, str] _sanitise_env (Path project_root)
SandboxBackend make_sandbox_backend (object config)

Variables

frozenset _SENSITIVE_ENV_EXACT
tuple _SENSITIVE_ENV_SUBSTRINGS
frozenset _SAFE_ENV_KEYS

Detailed Description

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*:

* :class:`NativeBackend` — existing behavior (subprocess + env
  sanitization). Always available; default on Windows.
* :class:`BubblewrapBackend` — Linux only. Wraps every command in
  ``bwrap --unshare-net --die-with-parent``. Requires ``bwrap`` in PATH.
* :class:`SandboxExecBackend` — macOS only. Prepends
  ``sandbox-exec -p "(deny network*)(allow default)"``. Requires
  ``sandbox-exec`` in PATH.

Use :func:`make_sandbox_backend` to select the appropriate backend based
on :class:`~oct.mcp.config.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 :class:`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=False`` is enforced unconditionally in all backends — no
  caller path can override it.
- :meth:`SandboxExecutor.run` never raises. Any ``OSError`` or
  ``subprocess.SubprocessError`` is caught, logged at level 1, and
  returned as a ``SandboxResult`` with a non-zero exit code.
- The sanitised environment always preserves ``PATH``, ``HOME``,
  ``LANG``, ``TERM``, ``USER``, ``USERNAME``, ``LOGNAME``,
  ``VIRTUAL_ENV``, ``CONDA_DEFAULT_ENV``, and
  ``OCT_MCP_SESSION_ID`` / ``OCT_MCP_REQUEST_ID`` correlation IDs.
- :func:`make_sandbox_backend` never raises — it always returns a
  usable backend (falls back to :class:`NativeBackend`).

Function Documentation

◆ _dbg()

None oct.mcp.sandbox._dbg ( * args,
** kwargs )
protected

Definition at line 73 of file sandbox.py.

Here is the caller graph for this function:

◆ _sanitise_env()

dict[str, str] oct.mcp.sandbox._sanitise_env ( Path project_root)
protected
Build a sanitised environment for subprocess execution.

Starts from ``os.environ``, preserves :data:`_SAFE_ENV_KEYS`,
strips :data:`_SENSITIVE_ENV_EXACT` exact keys and anything whose
key contains a :data:`_SENSITIVE_ENV_SUBSTRINGS` token (case-
insensitive), and adds ``OCT_MCP_CWD`` so the subprocess knows
its sandboxed working directory.

Returns
-------
A new ``dict[str, str]`` — never modifies ``os.environ``.

Definition at line 135 of file sandbox.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ make_sandbox_backend()

SandboxBackend oct.mcp.sandbox.make_sandbox_backend ( object config)
Select and return the best available :class:`SandboxBackend`.

Parameters
----------
config
    A :class:`~oct.mcp.config.McpConfig` instance. Reads the
    ``sandbox_backend`` field (``"auto"`` / ``"native"`` /
    ``"bubblewrap"`` / ``"sandbox_exec"``) and ``memory_limit_mb``.

Returns
-------
A concrete backend instance. On ``"auto"``, picks:

* :class:`BubblewrapBackend` on Linux when ``bwrap`` is in PATH.
* :class:`SandboxExecBackend` on macOS when ``sandbox-exec`` is in PATH.
* :class:`NativeBackend` otherwise (always safe).

When a *specific* backend is forced (e.g. ``"bubblewrap"``) but its
binary is not found, raises :exc:`RuntimeError`.

Never raises on ``"auto"`` or ``"native"`` — always returns a
usable backend.

Definition at line 406 of file sandbox.py.

Here is the call graph for this function:

Variable Documentation

◆ _SAFE_ENV_KEYS

frozenset oct.mcp.sandbox._SAFE_ENV_KEYS
protected
Initial value:
1= frozenset({
2 "PATH",
3 "HOME",
4 "LANG",
5 "LC_ALL",
6 "LC_CTYPE",
7 "TERM",
8 "COLORTERM",
9 "USER",
10 "USERNAME",
11 "LOGNAME",
12 "USERPROFILE", # Windows home directory
13 "SYSTEMROOT", # Windows system root
14 "VIRTUAL_ENV",
15 "CONDA_DEFAULT_ENV",
16 "CONDA_PREFIX",
17 "OCT_MCP_SESSION_ID",
18 "OCT_MCP_REQUEST_ID",
19 # Diagnostics env vars
20 "OCT_DBG_LEVEL",
21 "OCT_DBG_FILE",
22})

Definition at line 106 of file sandbox.py.

◆ _SENSITIVE_ENV_EXACT

frozenset oct.mcp.sandbox._SENSITIVE_ENV_EXACT
protected
Initial value:
1= frozenset({
2 "PYTHONPATH",
3})

Definition at line 87 of file sandbox.py.

◆ _SENSITIVE_ENV_SUBSTRINGS

tuple oct.mcp.sandbox._SENSITIVE_ENV_SUBSTRINGS
protected
Initial value:
1= (
2 "api_key", "apikey",
3 "password", "passwd", "pwd",
4 "secret",
5 "token",
6 "credentials",
7 "private_key",
8 "auth",
9 "access_key",
10)

Definition at line 93 of file sandbox.py.