| Crates.io | proc_jail |
| lib.rs | proc_jail |
| version | 0.1.0 |
| created_at | 2026-01-12 01:19:01.202353+00 |
| updated_at | 2026-01-12 01:19:01.202353+00 |
| description | Process execution guard for agentic systems |
| homepage | |
| repository | https://github.com/tenuo-ai/proc_jail |
| max_upload_size | |
| id | 2036826 |
| size | 197,698 |
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.
-- insertion to prevent flag injectionpip 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())
[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(())
}
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
Only explicitly allowed binaries can be executed:
ProcPolicyBuilder()
.allow_bin("/usr/bin/grep")
.allow_bin("/usr/bin/jq")
# ...
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())
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))
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()
# ...
By default, no environment variables are passed. Dangerous variables (LD_PRELOAD, PYTHONPATH, etc.) are always stripped.
ProcPolicyBuilder()
.timeout(30) # seconds
.max_stdout(10485760) # 10 MB
.max_stderr(1048576) # 1 MB
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.
| 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 |
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?
proc_jail/
├── src/ # Rust library source
├── tests/ # Rust integration tests
├── docs/ # Documentation
├── python/ # Python bindings (PyO3)
│ ├── src/ # Rust binding code
│ └── proc_jail/ # Python package
└── ...
# Build Rust library
cargo build
# Run tests
cargo test
# Build Python bindings
cd python
pip install maturin
maturin develop
MIT OR Apache-2.0