Rootless sandbox for running untrusted code
Project description
CodeBubble: 🧪 Sandbox Your AI-Generated Code
CodeBubble is a lightweight, rootless sandboxing library for running untrusted code—perfect for taming wild AI-generated programs on Linux.
Why CodeBubble? 🔥
- AI Code Mayhem: AI-generated snippets can contain infinite loops, memory bombs, or malicious operations like
rm -rf /. One rogue program can crash your experiments or compromise your server. - Rootless Bwrap Magic: Leverages Bubblewrap (bwrap) for unprivileged isolation (no
sudorequired), ideal for shared lab or school servers. - Detailed Metrics: Captures exit status, timing, CPU/memory usage, I/O stats, and error classifications in structured Python objects.
- Multi-Language Out-of-the-Box: Ships with Python and C++ executors; extendable to Java, Go, Rust, and more via
BaseExecutor.
📦 Installation
1. Install the Python package
pip install codebubble
2. Install Bubblewrap (bwrap)
With root:
sudo apt-get update
sudo apt install bubblewrap
Without root (build from source):
pip install meson ninja
export PATH="$HOME/.local/bin:$PATH"
git clone https://github.com/containers/bubblewrap.git
cd bubblewrap
meson setup build --prefix=$HOME/.local
meson compile -C build
meson install -C build
~/.local/bin/bwrap --version # check installation
bwrap --version # check installation
🚀 Quick Start
Python Executor
from codebubble.utils import ResourceLimits
from codebubble.sandbox.bwrap import BwrapSandbox, BwrapSandboxConfig
from codebubble.executor.python import PythonExecutor, PythonExecutorConfig
# 1. Configure a bwrap sandbox
workspace = "./py_workspace" # any empty directory
bwrap_path = "bwrap" # or the absolute path to bwrap
sb_config = BwrapSandboxConfig(workspace=workspace, bwrap_path=bwrap_path)
sandbox = BwrapSandbox(sb_config)
# 2. Set up Python executor
py_config = PythonExecutorConfig(interpreter_path='/usr/bin/python3')
executor = PythonExecutor(py_config, sandbox)
# 3. Run untrusted Python code
test_code = """
print("Sandboxed Python ✅")
a, b = map(int, input().split())
s = a + b
print(f"Sum: {s}")
"""
inputs = [
"1 2\n",
"3 4\n",
"5 6\n"
]
limits = ResourceLimits(
time_limit=3, # seconds per input
overall_time_limit=10, # seconds for all inputs
memory_limit=64 * 1024, # KB of RAM
max_input_size=2 * 1024, # KB of input data
max_output_size=2 * 1024 # KB of output data
)
results = executor.run(test_code, inputs, limits)
for r in results:
print(r)
C++ Executor
from codebubble.utils import ResourceLimits
from codebubble.sandbox.bwrap import BwrapSandbox, BwrapSandboxConfig
from codebubble.executor.cpp import CppExecutor, CppExecutorConfig
# 1. Configure a bwrap sandbox
workspace = "./cpp_workspace" # any empty directory
bwrap_path = "bwrap" # or the absolute path to bwrap
sb_config = BwrapSandboxConfig(workspace=workspace, bwrap_path=bwrap_path)
sandbox = BwrapSandbox(sb_config)
# 2. Set up C++ executor with custom compile flags
cpp_config = CppExecutorConfig(
compiler_path='/usr/bin/g++',
compiler_flags=['-std=c++17', '-O2', '-Wall']
)
executor = CppExecutor(cpp_config, sandbox)
# 3. Compile & run untrusted C++ code
test_code = """
#include <iostream>
int main() {
std::cout << "Sandboxed C++ ✅" << std::endl;
int a, b;
std::cin >> a >> b;
int s = a + b;
std::cout << "Sum: " << s << std::endl;
return 0;
}
"""
inputs = [
"1 2\n",
"3 4\n",
"5 6\n"
]
limits = ResourceLimits(
time_limit=3, # seconds per input
overall_time_limit=10, # seconds for all inputs
memory_limit=64 * 1024, # KB of RAM
max_input_size=2 * 1024, # KB of input data
max_output_size=2 * 1024 # KB of output data
)
results = executor.run(test_code, inputs, limits)
for r in results:
print(r)
⚙️ Configuration
ResourceLimits
| Parameter | Description | Default |
|---|---|---|
time_limit |
Seconds per run | 5.0 |
overall_time_limit |
Seconds for all inputs | 30.0 |
memory_limit |
KB of RAM | 256 * 1024 |
max_input_size |
KB of input data | 2 * 1024 |
max_output_size |
KB of output data | 2 * 1024 |
BwrapSandboxConfig
| Parameter | Description | Default |
|---|---|---|
bind_mounts |
Read-only mounts (host,container) |
[('/usr','/usr'),...] |
tmpfs_paths |
Paths mounted as tmpfs | ['/tmp'] |
use_proc |
Mount /proc |
True |
use_dev |
Mount /dev |
True |
env_vars |
Env vars inside sandbox | {'PATH':'/usr/bin'} |
hostname |
Sandbox hostname | 'sandbox' |
fsize_factor |
File-size limit multiplier | 1.1 |
📊 Metrics
ExecutionResult
| Field | Type | Description |
|---|---|---|
full_cmd |
List[str] | The full command executed (including wrappers) |
status |
ExecutionStatus | One of SUCCESS, TIME_LIMIT_EXCEEDED, etc. |
return_code |
int | Process exit code |
stdout |
str | Captured standard output |
stderr |
str | Captured standard error |
compile_time |
float (sec) (optional) | Time spent compiling (if applicable) |
execution_time |
float (sec) (optional) | Wall-clock execution time |
error_info |
str (optional) | Human-readable error description |
time_result |
TimeResult (optional) | Parsed resource metrics object |
TimeResult
| Metric | Type / Unit | Description |
|---|---|---|
elapsed_time |
float (sec) | Wall-clock time |
user_cpu_time / system_cpu_time |
float (sec) | CPU user and system times |
cpu_percentage |
string | CPU usage percentage |
avg_total_mem |
int (KB) | Average total memory usage |
avg_shared_mem / avg_unshared_data / avg_unshared_stack |
int (KB) | Breakdown of shared/unshared memory |
page_reclaims / page_faults |
int | Soft / hard page faults |
swaps |
int | Number of swap operations |
block_input_ops / block_output_ops |
int | Block I/O operations |
ipc_msgs_sent / ipc_msgs_received |
int | IPC messages sent / received |
signals_received |
int | Number of signals received |
voluntary_ctxt_switches / involuntary_ctxt_switches |
int | Context switches |
max_resident_set_size |
int (KB) | Peak resident set size |
exit_status |
int | Command exit code |
💡 How It Works
- Sandbox Creation: Bubblewrap spawns an unprivileged container, mounting only essential directories and isolating the workspace.
- Resource Guards: Uses
prlimitto cap memory/file sizes andtimeoutto enforce execution time limits. - Metric Collection: Wraps the command with
/usr/bin/time -f <format>to gather detailed stats intime_output.txt. - Execution Pipeline:
- Reset: cleanup workspace before each run.
- Prepare: write source files (and compile for C++).
- Run: execute inside the sandbox, capturing
stdout/stderr. - Collect: parse metrics via
parse_time_output()into aTimeResultobject. - Aggregate: build
ExecutionResultwith status, outputs, timings, and metrics.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
codebubble-0.1.0.tar.gz
(13.5 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codebubble-0.1.0.tar.gz.
File metadata
- Download URL: codebubble-0.1.0.tar.gz
- Upload date:
- Size: 13.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0fbe3022a41889dfc9e8d74c3e9de6a4022b1935bfe02b79b6a30f980f2e000e
|
|
| MD5 |
3d28dce2a014744b06b62fcfb6ca745b
|
|
| BLAKE2b-256 |
fcde922bd0c21c6780e2376db34f9832ebdd0b365131f4945914c47b8914ab64
|
File details
Details for the file codebubble-0.1.0-py3-none-any.whl.
File metadata
- Download URL: codebubble-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9cb4faf51d6de397e63be373449764fe18c03a21f42be14d6915c4d93a79769d
|
|
| MD5 |
a929707a387995b799e5fd0a78dc14e0
|
|
| BLAKE2b-256 |
e552bc4ab246f4ec552e533d9d783b1c3b0ab555da4897e205c36b2aa680ef67
|