Process execution guard for agentic systems
Project description
proc_jail
Process execution guard for agentic systems.
proc_jail provides a safe wrapper around process spawning, enforcing deterministic bounds on process execution to prevent command injection, unauthorized binary execution, and resource abuse.
Features
- No shell interpretation: Commands use argv-style execution, not shell strings
- Allowlist-only: Explicit enumeration of permitted binaries and flags
- Fail closed: Any error or ambiguity results in denial
- Resource limits: Timeout, stdout/stderr byte limits
- Double-dash injection: Automatic
--insertion to prevent flag injection - Python and Rust APIs: Native bindings for both languages
Quick Start (Python)
pip install proc_jail
from proc_jail import ProcPolicyBuilder, ProcRequest, ArgRules
# Define a policy
policy = (
ProcPolicyBuilder()
.allow_bin("/usr/bin/grep")
.arg_rules("/usr/bin/grep",
ArgRules()
.allowed_flags(["-n", "-i", "-l", "-c"])
.max_flags(4)
.max_positionals(10)
.inject_double_dash())
.timeout(30)
.build()
)
# Create and execute a request
request = ProcRequest("/usr/bin/grep", ["-n", "pattern", "file.txt"])
output = policy.prepare(request).spawn_sync()
print(output.stdout_string())
Quick Start (Rust)
[dependencies]
proc_jail = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
use proc_jail::{ProcPolicy, ProcRequest, ArgRules, InjectDoubleDash};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let policy = ProcPolicy::builder()
.allow_bin("/usr/bin/grep")
.arg_rules("/usr/bin/grep", ArgRules::new()
.allowed_flags(&["-n", "-i", "-l", "-c"])
.max_flags(4)
.max_positionals(10)
.inject_double_dash(InjectDoubleDash::AfterFlags))
.timeout(Duration::from_secs(30))
.build()?;
let request = ProcRequest::new(
"/usr/bin/grep",
vec!["-n".into(), "pattern".into(), "file.txt".into()],
);
let output = policy.prepare(request)?.spawn().await?;
println!("{}", output.stdout_string());
Ok(())
}
Why proc_jail?
Traditional process spawning is dangerous in agentic systems:
# VULNERABLE: Shell injection
subprocess.run(f"grep '{query}' file.txt", shell=True)
# Attacker sets: query = "x'; rm -rf / #"
# Executes: grep 'x'; rm -rf / #' file.txt
With proc_jail, the same attack becomes harmless:
request = ProcRequest("/usr/bin/grep", [query, "file.txt"])
output = policy.prepare(request).spawn_sync()
# If query = "x'; rm -rf / #"
# Executes: grep "x'; rm -rf / #" file.txt
# The injection is just a literal string, not interpreted
Policies
Binary Allowlist
Only explicitly allowed binaries can be executed:
ProcPolicyBuilder()
.allow_bin("/usr/bin/grep")
.allow_bin("/usr/bin/jq")
# ...
Argument Rules
Every binary requires explicit argument rules:
.arg_rules("/usr/bin/grep",
ArgRules()
.allowed_flags(["-n", "-i", "-l"])
.max_flags(3)
.max_positionals(10)
.inject_double_dash())
Subcommand Pinning
Pin allowed subcommands for tools like git:
.arg_rules("/usr/bin/git",
ArgRules()
.subcommand("status")
.allowed_flags(["--porcelain", "-s"])
.max_flags(2)
.max_positionals(0))
Risky Binary Detection
Shells, interpreters, and privilege escalation tools are blocked by default:
# Even if allowed, bash is denied by default
policy.prepare(ProcRequest("/bin/bash", [])) # Error: BinRiskyDenied
# Opt-in with explicit acknowledgment
ProcPolicyBuilder()
.allow_risky_binaries()
# ...
Environment Control
By default, no environment variables are passed. Dangerous variables (LD_PRELOAD, PYTHONPATH, etc.) are always stripped.
Resource Limits
ProcPolicyBuilder()
.timeout(30) # seconds
.max_stdout(10485760) # 10 MB
.max_stderr(1048576) # 1 MB
Platform Support
Unix only (Linux, macOS). Windows is not supported because CreateProcess passes arguments as a single string that each program parses differently, making injection prevention impossible to guarantee. See docs/windows.md for details.
Related Projects
| Project | Description | PyPI/Crates.io |
|---|---|---|
| path_jail | Path traversal prevention | |
| url_jail | SSRF-safe URL validation | |
| safe_unzip | Zip Slip and zip bomb prevention | |
| tenuo | Capability-based authorization for AI agents |
Integration with Tenuo
proc_jail provides the execution layer, while Tenuo provides cryptographic authorization. Together they offer defense in depth:
from proc_jail import ProcPolicyBuilder, ProcRequest, ArgRules
# proc_jail handles the safe execution
# Tenuo handles the authorization (who can call which tools)
def execute_grep(user_query: str, target_file: str) -> str:
"""Execute grep with proc_jail protection."""
policy = (
ProcPolicyBuilder()
.allow_bin("/usr/bin/grep")
.arg_rules("/usr/bin/grep",
ArgRules()
.allowed_flags(["-n", "-i"])
.max_positionals(10)
.inject_double_dash())
.timeout(30)
.build()
)
# Even if user_query = "'; rm -rf / #", it's treated as a literal string
request = ProcRequest("/usr/bin/grep", ["-n", user_query, target_file])
output = policy.prepare(request).spawn_sync()
return output.stdout_string()
Why both?
- Tenuo: Cryptographic proof the agent is authorized for this tool
- proc_jail: Prevents command injection even if the agent is compromised
Documentation
- SECURITY.md - Security properties, limitations, threat model
- CHANGELOG.md - Version history
- docs/windows.md - Why Windows is not supported
- docs/risky-binaries.md - Blocked binary categories
- docs/design-decisions.md - Rationale for key decisions
Repository Structure
proc_jail/
├── src/ # Rust library source
├── tests/ # Rust integration tests
├── docs/ # Documentation
├── python/ # Python bindings (PyO3)
│ ├── src/ # Rust binding code
│ └── proc_jail/ # Python package
└── ...
Development
# Build Rust library
cargo build
# Run tests
cargo test
# Build Python bindings
cd python
pip install maturin
maturin develop
License
MIT OR Apache-2.0
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 Distributions
Built Distributions
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 proc_jail-0.1.0-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: proc_jail-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 501.1 kB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dad2efa9baac144d15e55f01896156c477d28aa66212a4a23b5a9bf8db1bacfb
|
|
| MD5 |
91006e7e52e509e0586753234b3146ca
|
|
| BLAKE2b-256 |
e5951b31ca5f3bc7eb2aaaae904e70e48ab99f22a6250d1e4aff7e71d5029ecc
|
File details
Details for the file proc_jail-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: proc_jail-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 564.6 kB
- Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e90611a4f6d5fa5fc8b78b7123c120fd717cc7804807197ab4d7fddd39452532
|
|
| MD5 |
3abb272350a9eb00b835fb6f9ebda525
|
|
| BLAKE2b-256 |
164b5430be3aa336b316becb4e8c74db4dd1d9e64c68cfc973c965bcc0997dcf
|