Sandboxing
ipybox uses Anthropic's sandbox-runtime to isolate code execution. When enabled, the IPython kernel runs with restricted filesystem and network access.
Default sandbox
Enable sandboxing with sandbox=True.
async with CodeExecutor(sandbox=True) as executor:
result = await executor.execute("print('hello world')")
assert result.text == "hello world"
code = """
import requests
try:
requests.get('https://example.org')
except Exception as e:
print(e)
"""
# Default sandbox config blocks internet access
result = await executor.execute(code)
assert "Failed to resolve 'example.org'" in result.text
The default sandbox configuration allows:
- Reading all files except
.env - Writing to the current directory and subdirectories (plus IPython directories)
- Local network access to the tool execution server
{
"enableWeakerNestedSandbox": false,
"filesystem": {
"denyRead": [".env"],
"allowWrite": [".", "~/Library/Jupyter", "~/.ipython"],
"denyWrite": []
},
"network": {
"allowedDomains": [],
"deniedDomains": [],
"allowLocalBinding": true
}
}
Internet access is blocked as demonstrated in the example above. See the sandbox-runtime documentation for all configuration options.
Custom sandbox
To allow access to example.org, provide a custom sandbox configuration file:
{
"enableWeakerNestedSandbox": false,
"filesystem": {
"denyRead": [".env"],
"allowWrite": [".", "~/Library/Jupyter", "~/.ipython"],
"denyWrite": []
},
"network": {
"allowedDomains": ["example.org"],
"deniedDomains": [],
"allowLocalBinding": true
}
}
and pass it as sandbox_config argument:
code = """
import requests
result = requests.get('https://example.org')
print(result.text)
"""
async with CodeExecutor(
sandbox=True,
sandbox_config=Path("examples/sandbox-kernel.json"),
log_level="WARNING",
) as executor:
result = await executor.execute(code)
assert "Example Domain" in result.text
Sandboxing MCP servers
stdio MCP servers like the filesystem MCP server can be configured to run in a sandbox using srt as command:
server_params = {
"command": "srt",
"args": [
"--settings",
"examples/sandbox-mcp.json",
"npx",
"-y",
"@modelcontextprotocol/server-filesystem",
".",
],
}
The sandbox configuration is:
{
"enableWeakerNestedSandbox": false,
"filesystem": {
"denyRead": [".env"],
"allowWrite": [".", "~/.npm"],
"denyWrite": []
},
"network": {
"allowedDomains": ["registry.npmjs.org"],
"deniedDomains": [],
"allowLocalBinding": true
}
}
The server itself is configured with permissions to access all files in the current directory ("."), but the sandbox additionally blocks read access to .env. The sandbox also allows access to registry.npmjs.org for downloading the server package via npx, and ~/.npm for the local npm cache.
await generate_mcp_sources("filesystem", server_params, Path("mcptools"))
list_dir_code = """
from mcptools.filesystem.list_directory import run, Params
result = run(Params(path="."))
print(result.content)
"""
read_env_code = """
from mcptools.filesystem.read_file import run, Params
result = run(Params(path=".env"))
print(result.content)
"""
async with CodeExecutor(sandbox=True) as executor:
# allowed by MCP server and sandbox
result = await executor.execute(list_dir_code)
assert "README.md" in result.text
try:
# allowed by MCP server but blocked by sandbox
result = await executor.execute(read_env_code)
assert False, "Read access to .env not blocked"
except CodeExecutionError as e:
assert "operation not permitted" in str(e)
Info
MCP server sandboxing is independent of kernel sandboxing and usually not needed when using trusted servers, but provides an additional security layer when needed.