Sandboxable busybox in Go, compiled to WASM via TinyGo. Extensive comparative testing (including fuzzing) against the reference C binary.
Three components:
- Applet library — 57 utilities under
pkg/applets/, each a self-contained package. - Core runtime — shared helpers (
core/,sandbox/,procutil/), sandboxed filesystem viapkg/core/fs/. - Test harness — unit tests, fuzz tests, integration parity matrix, BusyBox reference suite.
go-busybox/
├── cmd/ # entry points (51 standalone + busybox multi-call)
├── pkg/
│ ├── applets/ # 57 applet packages + procutil
│ ├── core/ # fs/, archiveutil/, headtail, textutil
│ ├── integration/ # BusyBox comparison tests
│ ├── sandbox/ # capability-based access control
│ └── testutil/ # FuzzCompare, CaptureStdio, helpers
├── busybox-reference/# reference binary (v1.35.0) + test suite
└── docs/SPEC.md
- Capability-based filesystem: only pre-opened directories accessible
- Network gated:
wget,nc,dig,ssrequire explicit opt-in - Memory isolation via WASM linear memory
- Syscalls limited to WASI preview1
- Go 1.22+, TinyGo 0.34+ (WASM target:
wasm32-wasi) - No CGO dependencies
- BusyBox v1.35.0 as reference
- POSIX compliance where applicable
- Binary size: 4.7MB standard WASM, 2.0MB optimised (
-opt=z -no-debug) - Startup: <10ms cold start
- Memory: <16MB baseline
- 16 applets stubbed under WASI (OS-dependent syscalls); 41 fully functional
- TinyGo WASM build pipeline
- Core runtime with syscall support
- Initial utilities:
echo,cat,ls,cp,mv,rm - Basic test harness
- File:
head,tail,wc,sort,uniq,cut,grep - Directory:
mkdir,rmdir,pwd,find - Text:
sed,tr,diff,awk(via goawk) - Filesystem sandbox
- Shell:
ash— near-complete POSIX shell, 349/349 BusyBox tests - Process:
ps,kill,killall,pidof,pgrep,pkill,nice,renice,nohup,time,timeout,taskset,ionice,setsid,start-stop-daemon,xargs,top,watch,sleep,nproc,free,logname,uptime,users,who,w,whoami - Archive:
tar,gzip,gunzip - Network:
wget,nc,dig,ss
- Comprehensive fuzzing (100 functions, 353 seeds)
- Full Godoc coverage (all packages, all exported symbols)
- BusyBox reference suite 387/387
- Security audit
- Performance optimisation
| Layer | Files | Scope |
|---|---|---|
| Unit tests | 62 *_test.go |
per-applet logic |
| Fuzz tests | 58 *_fuzz_test.go, 100 functions, 353 seeds |
crash/panic detection, differential comparison |
| Integration | pkg/integration/ |
parity matrix vs reference BusyBox |
| Reference suite | busybox-reference/testsuite/ |
387/387 (308 new-style + 79 old-style) |
Every applet has at least one fuzz test. Complex applets (sed, grep, awk, diff, tr, cut, tar, gzip, gunzip) have multiple functions covering:
- Input fuzzing — random data with fixed flags
- Expression/DSL fuzzing — fuzzed sed expressions, grep patterns, awk programmes, find predicates, tr character sets, cut field specs
- Flag combination fuzzing — fixed input with varied flag sets
- Roundtrip fuzzing — gzip→gunzip, tar create→extract, cp source→destination byte-for-byte verification
- Invalid input fuzzing — arbitrary bytes fed to gunzip, tar to verify graceful failure
Safety filters prevent non-termination: sed rejects branch-label loops and excessive regex quantifiers; printf skips non-consuming format strings; all inputs clamped to 2048 bytes.
All applets below pass their respective BusyBox reference tests at 100%:
| Applet | Tests | Source |
|---|---|---|
| ash | 349/349 | BusyBox ash test suite |
| awk | 53/53 | awk.tests |
| sed | 92/92 | sed.tests |
| grep | 44/44 | grep.tests |
| printf | 24/24 | printf.tests |
| cut | 22/22 | cut.tests |
| uniq | 14/14 | uniq.tests |
| cp | 13/13 | cp.tests + old-style |
| diff | 12/12 | diff.tests |
| xargs | 7/7 | xargs.tests |
| sort | 5/5 | sort.tests |
| gunzip | 5/5 | gunzip.tests |
| tar | 3/3 | tar.tests |
| taskset | 3/3 | taskset.tests |
| pidof | 3/3 | pidof.tests |
| find | 2/2 | find.tests |
| head | 2/2 | head.tests |
| tail | 2/2 | tail.tests + old-style |
| tr | 2/2 | tr.tests + old-style |
| wget | 4/4 | old-style |
| wc | 5/5 | old-style |
| cat, echo, gzip, ls, mkdir, mv, pwd, rm, rmdir | — | old-style (all pass) |
Remaining applets (kill, killall, pgrep, pkill, nice, nohup, renice, ps, free, nproc, ionice, setsid, sleep, timeout, time, top, watch, logname, uptime, users, w, who, whoami, dig, nc, ss, start-stop-daemon) have unit tests and fuzz tests but no dedicated BusyBox reference-suite entries.
- Exit codes: BusyBox returns 1 for usage errors where we return 2 — both accepted.
psoutput: whitespace normalised.findoutput: leading./stripped.pwd: compared per temp directory.taskset: PID normalised.wgetstderr: ignored.time/uptime/who/w: timing/session-dependent output excluded from strict comparison.