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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ serde_json = "1"
# JSON processing (jq) - verified embeddable
jaq-core = "2"
jaq-std = "2"
jaq-json = { version = "1", features = ["serde_json"] }

# Text search (grep) - verified supports search_slice() for in-memory
grep = "0.3"
Expand Down
129 changes: 129 additions & 0 deletions KNOWN_LIMITATIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Known Limitations

BashKit is a sandboxed bash interpreter designed for AI agents. It prioritizes safety and simplicity over full POSIX/bash compliance. This document tracks known limitations.

## Spec Test Coverage

Current compatibility: **78.3%** (83/106 tests passing)

| Category | Passed | Total | Notes |
|----------|--------|-------|-------|
| Echo | 8 | 10 | -n flag, empty echo edge case |
| Variables | 19 | 20 | $? after `false` |
| Control Flow | - | - | Skipped (timeout investigation) |
| Functions | 10 | 14 | return, local scope, recursion |
| Arithmetic | 12 | 22 | Comparison ops, ternary, bitwise |
| Arrays | 8 | 14 | +=, element length, loops |
| Globs | 4 | 7 | Brackets, recursive, brace |
| Pipes/Redirects | 10 | 13 | Heredoc vars, stderr |
| Command Subst | 12 | 14 | Exit code, backticks |
| AWK | 17 | 19 | gsub regex, split |
| Grep | 12 | 15 | -w, -o, -l stdin |
| Sed | 13 | 17 | -i flag, multiple commands |
| JQ | 20 | 21 | -r flag |

## Shell Features

### Not Implemented

| Feature | Priority | Notes |
|---------|----------|-------|
| `set -e` (errexit) | High | Critical for scripts |
| Process substitution `<(cmd)` | Medium | Used in advanced scripts |
| Coprocesses `coproc` | Low | Rarely used |
| Extended globs `@()` `!()` | Medium | Requires `shopt -s extglob` |
| Associative arrays `declare -A` | Medium | Bash 4+ feature |
| `[[ =~ ]]` regex matching | Medium | Bash extension |
| Backtick substitution | Low | Deprecated, use `$()` |
| Brace expansion `{a,b,c}` | Medium | Common pattern |
| `trap` signal handling | High | Error handling |
| `getopts` | Medium | Option parsing |
| `alias` | Low | Interactive feature |
| History expansion | Out of scope | Interactive only |
| Job control (bg/fg/jobs) | Out of scope | Requires process control |

### Partially Implemented

| Feature | What Works | What's Missing |
|---------|------------|----------------|
| `local` | Declaration | Proper scoping in nested functions |
| `return` | Basic usage | Return value propagation |
| Arithmetic | Basic ops | Comparison, ternary, bitwise |
| Heredocs | Basic | Variable expansion inside |
| Arrays | Indexing, `[@]` | `+=` append, `${!arr[@]}` |
| `echo -n` | Flag parsed | Trailing newline handling |

## Builtins

### Implemented
`echo`, `printf`, `cat`, `cd`, `pwd`, `true`, `false`, `exit`, `test`, `[`, `export`, `set`, `unset`, `local`, `source`, `read`, `grep`, `sed`, `awk`, `jq`

### Not Implemented
`cp`, `mv`, `rm`, `mkdir`, `rmdir`, `ls`, `touch`, `chmod`, `chown`, `ln`, `head`, `tail`, `sort`, `uniq`, `wc`, `tr`, `cut`, `tee`, `xargs`, `find`, `type`, `which`, `command`, `hash`, `declare`, `typeset`, `readonly`, `shift`, `wait`, `kill`, `eval`, `exec`

## Text Processing

### AWK Limitations
- Regex literals in function args: `gsub(/pattern/, replacement)`
- Array assignment in split: `split($0, arr, ":")`
- Complex regex patterns

### Sed Limitations
- Case insensitive flag `/i`
- Multiple commands in single invocation
- Append/insert commands (`a\`, `i\`)
- In-place editing (`-i`)

### Grep Limitations
- Word boundary `-w`
- Only matching `-o`
- Stdin filename with `-l`

### JQ Limitations
- Raw output `-r` flag
- Pretty printing (outputs compact JSON)

## Parser Limitations

- Single-quoted strings are completely literal (correct behavior)
- Some complex nested structures may timeout
- Very long pipelines may cause stack issues

## Filesystem

- Virtual filesystem only (InMemoryFs, OverlayFs, MountableFs)
- No real filesystem access by default
- Symlinks stored but not followed
- No file permissions enforcement

## Network

- HTTP only (via `curl` builtin when enabled)
- URL allowlist required
- No raw sockets
- No DNS resolution (host must be in allowlist)

## Resource Limits

Default limits (configurable):
- Commands: 10,000
- Loop iterations: 100,000
- Function depth: 100
- Output size: 10MB

## Comparison with Real Bash

Run comparison tests:
```bash
cargo test --test spec_tests -- bash_comparison_tests --ignored
```

This runs each spec test against both BashKit and real bash, reporting differences.

## Contributing

To add a known limitation:
1. Add a spec test that demonstrates the limitation
2. Mark the test with `### skip: reason`
3. Update this document
4. Optionally file an issue for tracking
105 changes: 95 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,108 @@
# rust-template
# BashKit

<!-- TODO: Add project description -->
Sandboxed bash interpreter for multi-tenant environments. Written in Rust.

## Overview
## Features

<!-- TODO: Add overview -->
- **Sandboxed execution** - No real filesystem access by default
- **Virtual filesystem** - InMemoryFs, OverlayFs, MountableFs
- **Resource limits** - Command count, loop iterations, function depth
- **Network allowlist** - Control HTTP access per-domain
- **MCP server mode** - Model Context Protocol integration
- **Async-first** - Built on tokio

## Quick Start

```rust
use bashkit::Bash;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut bash = Bash::new();
let result = bash.exec("echo hello world").await?;
println!("{}", result.stdout); // "hello world\n"
Ok(())
}
```

## Built-in Commands

| Category | Commands |
|----------|----------|
| Core | `echo`, `printf`, `cat`, `read` |
| Navigation | `cd`, `pwd` |
| Flow control | `true`, `false`, `exit`, `test`, `[` |
| Variables | `export`, `set`, `unset`, `local`, `source` |
| Text processing | `grep`, `sed`, `awk`, `jq` |

## Shell Features

- Variables and parameter expansion (`$VAR`, `${VAR:-default}`, `${#VAR}`)
- Command substitution (`$(cmd)`)
- Arithmetic expansion (`$((1 + 2))`)
- Pipelines and redirections (`|`, `>`, `>>`, `<`, `<<<`)
- Control flow (`if`/`elif`/`else`, `for`, `while`, `case`)
- Functions (POSIX and bash-style)
- Arrays (`arr=(a b c)`, `${arr[@]}`, `${#arr[@]}`)
- Glob expansion (`*`, `?`)
- Here documents (`<<EOF`)

## Configuration

```rust
use bashkit::{Bash, ExecutionLimits, InMemoryFs};
use std::sync::Arc;

let limits = ExecutionLimits::new()
.max_commands(1000)
.max_loop_iterations(10000)
.max_function_depth(100);

let mut bash = Bash::builder()
.fs(Arc::new(InMemoryFs::new()))
.env("HOME", "/home/user")
.cwd("/home/user")
.limits(limits)
.build();
```

## Virtual Filesystem

```rust
use bashkit::{InMemoryFs, OverlayFs, MountableFs, FileSystem};
use std::sync::Arc;

// Layer filesystems
let base = Arc::new(InMemoryFs::new());
let overlay = Arc::new(OverlayFs::new(base));

// Mount points
let mut mountable = MountableFs::new(Arc::new(InMemoryFs::new()));
mountable.mount("/data", Arc::new(InMemoryFs::new()));
```

## CLI Usage

```bash
cargo build
cargo run
# Run a script
bashkit-cli run script.sh

# Interactive REPL
bashkit-cli repl

# MCP server mode
bashkit-cli mcp
```

## Documentation
## Development

- [AGENTS.md](./AGENTS.md) - Agent instructions
- [CONTRIBUTING.md](./CONTRIBUTING.md) - Contribution guide
```bash
just build # Build project
just test # Run tests
just check # fmt + clippy + test
just pre-pr # Pre-PR checks
```

## License

<!-- TODO: Add license -->
MIT
5 changes: 5 additions & 0 deletions crates/bashkit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ reqwest = { workspace = true, optional = true }
# URL parsing
url = "2"

# JSON processing (jq)
jaq-core = { workspace = true }
jaq-std = { workspace = true }
jaq-json = { workspace = true }

[features]
default = []
network = ["reqwest"]
Expand Down
Loading