Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bashkit-eval/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# BashKit Eval
# Bashkit Eval

LLM evaluation harness for bashkit tool usage. Measures how well models use bashkit's bash tool in agentic workloads.

Expand Down
6 changes: 3 additions & 3 deletions crates/bashkit/docs/threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,8 @@ Python `pathlib.Path` operations are bridged to Bashkit's virtual filesystem.
| Shell escape (TM-PY-004) | `os.system()` | Monty has no os.system/subprocess | MITIGATED |
| Real FS access (TM-PY-005) | `open()` | Monty has no open() builtin | MITIGATED |
| Error info leak (TM-PY-006) | Errors go to stdout | Errors go to stderr, not stdout | MITIGATED |
| Real FS read (TM-PY-015) | `Path.read_text()` | VFS bridge reads only from BashKit VFS | MITIGATED |
| Real FS write (TM-PY-016) | `Path.write_text()` | VFS bridge writes only to BashKit VFS | MITIGATED |
| Real FS read (TM-PY-015) | `Path.read_text()` | VFS bridge reads only from Bashkit VFS | MITIGATED |
| Real FS write (TM-PY-016) | `Path.write_text()` | VFS bridge writes only to Bashkit VFS | MITIGATED |
| Path traversal (TM-PY-017) | `../../etc/passwd` | VFS path normalization | MITIGATED |
| Bash/Python VFS isolation (TM-PY-018) | Cross-tenant access | Shared VFS by design; no cross-tenant | MITIGATED |
| Crash on missing file (TM-PY-019) | Missing file panic | FileNotFoundError raised, not panic | MITIGATED |
Expand All @@ -581,7 +581,7 @@ Python `pathlib.Path` operations are bridged to Bashkit's virtual filesystem.
**Architecture:**

```text
Python code → Monty VM → OsCall pause → BashKit VFS bridge → resume
Python code → Monty VM → OsCall pause → Bashkit VFS bridge → resume
```

Monty runs directly in the host process. Resource limits (memory, allocations,
Expand Down
6 changes: 3 additions & 3 deletions crates/bashkit/examples/python_scripts.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
//! Python Scripts Example
//!
//! Demonstrates running Python code inside BashKit's virtual environment using the
//! Demonstrates running Python code inside Bashkit's virtual environment using the
//! embedded Monty interpreter. Python runs entirely in-memory with
//! resource limits. Python pathlib.Path operations are bridged to
//! BashKit's virtual filesystem.
//! Bashkit's virtual filesystem.
//!
//! Run with: cargo run --features python --example python_scripts

use bashkit::Bash;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("=== BashKit Python Integration ===\n");
println!("=== Bashkit Python Integration ===\n");

let mut bash = Bash::builder()
.python()
Expand Down
4 changes: 2 additions & 2 deletions crates/bashkit/examples/typescript_scripts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! TypeScript Scripts Example
//!
//! Demonstrates running TypeScript code inside BashKit's virtual environment
//! Demonstrates running TypeScript code inside Bashkit's virtual environment
//! using the embedded ZapCode interpreter. TypeScript runs entirely in-memory
//! with resource limits. VFS operations are bridged via external function
//! suspend/resume.
Expand All @@ -11,7 +11,7 @@ use bashkit::Bash;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("=== BashKit TypeScript Integration ===\n");
println!("=== Bashkit TypeScript Integration ===\n");

let mut bash = Bash::builder().typescript().build();

Expand Down
12 changes: 6 additions & 6 deletions crates/bashkit/src/builtins/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! # Overview
//!
//! Virtual Python execution with resource limits and VFS access.
//! Python `pathlib.Path` operations are bridged to BashKit's virtual filesystem
//! Python `pathlib.Path` operations are bridged to Bashkit's virtual filesystem
//! via Monty's OsCall pause/resume mechanism. No real filesystem or network access.
//!
//! Supports: `python -c "code"`, `python script.py`, stdin piping.
Expand Down Expand Up @@ -181,7 +181,7 @@ impl std::fmt::Debug for PythonExternalFns {
/// The python/python3 builtin command.
///
/// Executes Python code using the embedded Monty interpreter (pydantic/monty).
/// Python `pathlib.Path` operations are bridged to BashKit's VFS — files
/// Python `pathlib.Path` operations are bridged to Bashkit's VFS — files
/// created by bash (`cat > file`) are readable from Python, and vice versa.
///
/// # Usage
Expand Down Expand Up @@ -456,7 +456,7 @@ impl Builtin for Python {
/// Execute Python code via Monty with resource limits and VFS bridging.
///
/// Uses Monty's start/resume API: execution pauses at filesystem operations
/// (OsCall), we bridge them to BashKit's VFS, then resume.
/// (OsCall), we bridge them to Bashkit's VFS, then resume.
async fn run_python(
code: &str,
filename: &str,
Expand Down Expand Up @@ -598,7 +598,7 @@ async fn run_python(
}

// ---------------------------------------------------------------------------
// VFS bridging: Monty OsCall → BashKit FileSystem
// VFS bridging: Monty OsCall → Bashkit FileSystem
// ---------------------------------------------------------------------------

/// Dispatch a Monty OsCall to the appropriate VFS operation.
Expand Down Expand Up @@ -768,7 +768,7 @@ async fn handle_os_call(
}
}
OsFunction::Resolve | OsFunction::Absolute => {
// No symlink resolution in BashKit VFS; just return absolute path
// No symlink resolution in Bashkit VFS; just return absolute path
ExtFunctionResult::Return(MontyObject::Path(path.to_string_lossy().to_string()))
}
// Getenv/GetEnviron handled above
Expand Down Expand Up @@ -797,7 +797,7 @@ fn resolve_python_path(path_str: &str, cwd: &Path) -> PathBuf {
}
}

/// Map a BashKit VFS error to a Python exception via ExtFunctionResult.
/// Map a Bashkit VFS error to a Python exception via ExtFunctionResult.
fn map_vfs_error(e: crate::Error, path: &Path) -> ExtFunctionResult {
let msg = e.to_string();
let path_str = path.display().to_string();
Expand Down
4 changes: 2 additions & 2 deletions crates/bashkit/src/builtins/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ impl Builtin for TypeScript {
/// Execute TypeScript code via ZapCode with resource limits and VFS bridging.
///
/// Uses ZapCode's start/resume API: execution suspends at external function calls
/// (VFS operations), we bridge them to BashKit's VFS, then resume.
/// (VFS operations), we bridge them to Bashkit's VFS, then resume.
async fn run_typescript(
code: &str,
fs: Arc<dyn FileSystem>,
Expand Down Expand Up @@ -769,7 +769,7 @@ async fn handle_external_call(
}

// ---------------------------------------------------------------------------
// VFS bridging: ZapCode external functions → BashKit FileSystem
// VFS bridging: ZapCode external functions → Bashkit FileSystem
// ---------------------------------------------------------------------------

/// Extract a path string from the first arg, resolve relative to cwd.
Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit/src/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ const PLACEHOLDER_PREFIX: &str = "bk_placeholder_";
/// # Security
///
/// The placeholder acts like a bearer capability inside scripts: if a script
/// sends the placeholder value to a URL matching a credential rule, BashKit
/// sends the placeholder value to a URL matching a credential rule, Bashkit
/// injects the real credential-owned header. The token therefore must come
/// from a security-grade RNG so it cannot be predicted, brute-forced, or
/// reproduced from observed program state.
Expand Down
8 changes: 4 additions & 4 deletions crates/bashkit/tests/threat_model_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ mod python_security {
assert_ne!(result.exit_code, 0);
}

/// TM-PY-014: Python with BashKit resource limits
/// TM-PY-014: Python with Bashkit resource limits
#[tokio::test]
async fn threat_python_respects_bash_limits() {
let limits = ExecutionLimits::new().max_commands(5);
Expand All @@ -1448,7 +1448,7 @@ mod python_security {

// --- VFS Security Tests ---

/// TM-PY-015: Python VFS reads only from BashKit's virtual filesystem
/// TM-PY-015: Python VFS reads only from Bashkit's virtual filesystem
#[tokio::test]
async fn threat_python_vfs_no_real_fs() {
let mut bash = bash_with_python();
Expand Down Expand Up @@ -1540,7 +1540,7 @@ mod python_security {
);
}

/// TM-PY-020: Python VFS operations respect BashKit sandbox boundaries
/// TM-PY-020: Python VFS operations respect Bashkit sandbox boundaries
#[tokio::test]
async fn threat_python_vfs_no_network() {
let mut bash = bash_with_python();
Expand Down Expand Up @@ -4172,7 +4172,7 @@ mod typescript_security {
assert_ne!(result.exit_code, 0, "Stack overflow should not succeed");
}

/// TM-TS-005: TypeScript VFS reads only from BashKit VFS
/// TM-TS-005: TypeScript VFS reads only from Bashkit VFS
#[tokio::test]
async fn threat_ts_vfs_no_real_fs() {
let mut bash = bash_with_ts();
Expand Down
4 changes: 2 additions & 2 deletions crates/bashkit/tests/typescript_security_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ mod whitebox_vfs_security {
.await
.unwrap();
// Verify file doesn't exist on real host (we're in VFS)
// The bash `test -f` in BashKit also operates on VFS, so this
// The bash `test -f` in Bashkit also operates on VFS, so this
// verifies the write went to VFS, not a real assertion about host fs
let r = bash.exec("cat /tmp/ts_escape_test").await.unwrap();
assert_eq!(r.exit_code, 0);
Expand Down Expand Up @@ -415,7 +415,7 @@ mod whitebox_error_handling {
mod whitebox_bash_integration {
use super::*;

/// TM-TS-018: TypeScript respects BashKit command limits
/// TM-TS-018: TypeScript respects Bashkit command limits
#[tokio::test]
async fn threat_ts_respects_bash_limits() {
let limits = ExecutionLimits::new().max_commands(5);
Expand Down
20 changes: 10 additions & 10 deletions specs/python-builtin.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Implemented (experimental)

## Decision

BashKit provides sandboxed Python execution via `python` and `python3` builtins,
Bashkit provides sandboxed Python execution via `python` and `python3` builtins,
powered by the [Monty](https://github.com/pydantic/monty) embedded Python
interpreter written in Rust.

Expand Down Expand Up @@ -82,7 +82,7 @@ python3 -V

### Resource Limits

Monty enforces its own resource limits independent of BashKit's shell limits.
Monty enforces its own resource limits independent of Bashkit's shell limits.
All limits are configurable via `PythonLimits`:

| Limit | Default | Builder Method | Purpose |
Expand Down Expand Up @@ -143,7 +143,7 @@ Monty implements a subset of Python 3.12:

### VFS Bridging

Python `pathlib.Path` operations are bridged to BashKit's virtual filesystem
Python `pathlib.Path` operations are bridged to Bashkit's virtual filesystem
via Monty's OsCall pause/resume mechanism. This enables Python code to read
and write files that are shared with the bash environment.

Expand Down Expand Up @@ -181,16 +181,16 @@ python3 -c "import os; print(os.getenv('HOME'))"

**Architecture:**
```
Python code → Monty VM → OsCall(ReadText, path) → BashKit VFS → resume
Python code → Monty VM → OsCall(ReadText, path) → Bashkit VFS → resume
```

Monty pauses execution at filesystem operations, yields an `OsCall` event
with the operation type and arguments, BashKit bridges it to the VFS, and
with the operation type and arguments, Bashkit bridges it to the VFS, and
resumes execution with the result (or a Python exception).

> **Note:** Monty 0.0.10+ includes native filesystem mounting (`MountTable`,
> `MountDir`, `MountMode`) that can handle file operations directly against
> host directories. BashKit uses the OsCall bridge instead because our VFS is
> host directories. Bashkit uses the OsCall bridge instead because our VFS is
> in-memory and may not be backed by host directories. The native mount system
> is suited for real-filesystem use cases where Monty is used standalone.

Expand Down Expand Up @@ -277,11 +277,11 @@ by-design consistent with all other builtins. Use single quotes to prevent
expansion: `python3 -c 'print("hello")'`.

#### Threat: Resource exhaustion
Monty enforces independent resource limits. Even if BashKit's shell limits
Monty enforces independent resource limits. Even if Bashkit's shell limits
are generous, Python code cannot exceed Monty's allocation/time/memory caps.

#### Threat: Sandbox escape via filesystem
All `pathlib.Path` operations go through BashKit's virtual filesystem.
All `pathlib.Path` operations go through Bashkit's virtual filesystem.
Python code cannot access the real host filesystem. `/etc/passwd` in Python
reads from VFS (where it doesn't exist), not the host.

Expand Down Expand Up @@ -312,13 +312,13 @@ a hint to `help()` and `system_prompt()` documenting its limitations:

> python/python3: Embedded Python (Monty). Stdlib: math, pathlib, os.getenv, sys, typing. File I/O via pathlib.Path only (no open()). No HTTP/network. No classes. No third-party imports.

Regex module `re` is intentionally disabled in BashKit due to catastrophic
Regex module `re` is intentionally disabled in Bashkit due to catastrophic
backtracking DoS risk in untrusted code execution.

This uses the general `Builtin::llm_hint()` mechanism — any builtin can provide
hints that are automatically deduplicated and included in LLM-facing documentation.

### Integration with BashKit
### Integration with Bashkit

- `python`/`python3` both map to the same builtin
- Works in pipelines: `echo "data" | python3 -c "import sys; ..."`
Expand Down
Loading
Loading