Skip to content

Latest commit

 

History

History
173 lines (139 loc) · 12.7 KB

File metadata and controls

173 lines (139 loc) · 12.7 KB
type Limitations
title Known Limitations
description Intentional gaps, partial features, and Bash and POSIX compatibility stance.
tags
bashkit
limitations
compatibility

Limitations

Status

Living document (updated as limitations are added/lifted)

Summary

The negative spec: what Bashkit deliberately does NOT do (and why), plus known partial implementations. Absences can't be recovered from code, so they're recorded here; everything positive is generated or tested instead:

  • Builtin inventory: generated builtins.json (just regen-builtins, drift-checked by builtins-drift.yml)
  • Test counts / pass rates: CI (spec_tests::bash_spec_tests suite); spec cases in crates/bashkit/tests/spec_cases/
  • Resource limit defaults: crates/bashkit/src/limits.rs
  • Hook/binding API surface: rustdoc + binding type stubs

Intentional-limitation IDs (L-<AREA>-<NNN>) are stable: code comments and docs reference them (like TM-* threat IDs). Never renumber; mark lifted limitations as removed in the PR that lifts them. limitations_doc_format in crates/bashkit/tests/integration/ lints the table format and ID uniqueness; evidence cells naming l_* tests must resolve to functions in limitations_evidence_tests.rs (also linted). stance marks rows that are positions rather than testable behaviors.

Intentional Limitations

By design, these conflict with the sandboxed, virtual, stateless execution model. Evidence is a threat-model ID, a test, or stance (untestable position).

ID Limitation Why Evidence
L-PROC-001 exec does not replace the process; exec cmd runs cmd then stops execution (fd redirects work) True process replace would break sandbox containment TM-ESC-005
L-PROC-002 No job control (bg, fg, jobs) Requires process state; interactive-only feature l_proc_002_no_job_control
L-PROC-003 No process spawning; external commands run as builtins Core sandbox model: no fork/exec escape surface l_proc_003_no_process_spawning
L-FS-001 Symlinks stored but never followed in path resolution (ln -s works, read_link() returns targets, traversal blocked) Prevents symlink loops and link-based sandbox escapes TM-DOS-011
L-FS-002 No file permission enforcement in the VFS Single-tenant virtual FS; permissions would be theater l_fs_002_no_permission_enforcement
L-FS-003 On Windows, RealFs::symlink() validates the target but creates an empty host file rather than a symlink/reparse point; pre-existing host symlinks and junctions remain readable subject to containment checks Windows requires choosing file-vs-directory link semantics and may require link privileges; the portable VFS symlink contract does not carry that host metadata TM-ESC-033
L-NET-001 No raw network sockets; HTTP only via curl/wget/http builtins Allowlist-mediated egress is the only network surface l_net_001_no_raw_sockets
L-NET-002 No DNS resolution; hosts must appear in the allowlist Resolution would bypass allowlist intent l_net_002_default_deny_no_resolution
L-SIG-001 trap stores INT/TERM handlers but no signal delivery in virtual mode (EXIT, ERR fire) No host signals exist inside the sandbox l_sig_001_signal_traps_not_delivered
L-SSH-001 CA-signed SSH host certificates are rejected under strict_host_key_checking (default), even when the public key they wrap is a configured trusted key; configure the host's public key directly No CA trust store exists to validate the signature chain, validity window, principals or critical options; matching the embedded key would grant trust never actually verified TM-SSH-006, test_strict_rejects_certificate_even_when_inner_key_is_trusted
L-WASM-001 Removed: JS-host timers now drive sleep, builtin timeout, execution limits, and tool timeoutMs gloo-timers bridges the host event-loop clock without threads or cross-origin isolation Browser Package
L-WASM-002 Browser build: executeSync() cannot run async custom builtins (fails with a clear message); use execute() Single-threaded event loop can't settle a JS Promise without yielding crates/bashkit-wasm/__test__/bashkit-wasm.test.mjs
L-WASM-003 Browser build: background jobs (cmd &) run synchronously and awk file redirects drive the VFS inline; no work runs on a separate thread wasm32-unknown-unknown is single-threaded, std::thread::spawn/tokio::spawn are unavailable; safe because the in-memory VFS never suspends crates/bashkit-wasm/__test__/bashkit-wasm.test.mjs
L-CAPI-001 C ABI v1 excludes callbacks, custom builtins, streaming, async cancellation, host mounts, transport hooks, snapshots, scripted tools, and external filesystem providers These require explicit reentrancy, callback lifetime, and dynamic-library unload contracts C API, stance
L-STREAM-001 Shell words, variables, command substitution, script source, text-oriented builtins, and JSON tool responses cannot represent arbitrary bytes. Command substitution removes NUL; other text boundaries decode invalid UTF-8 with replacement. Use StreamData, binding byte fields, redirects, or byte-oriented builtins for exact data Bash variables and the parser are text domains; JSON strings are Unicode byte_stream_tests, Architecture

Design Rationale

Stateless execution model: scripts run in isolated, stateless contexts; each command completes before the next begins. Prevents resource leaks from orphaned work, simplifies limit enforcement, keeps agent runs deterministic. (& background execution + wait are supported within an exec call.)

bash/sh as virtual re-invocation: bash script.sh / bash -c / bash -n re-enter the Bashkit interpreter, same virtual environment, shared state and limits, never an external process. bash --version reports Bashkit. Security analysis: TM-ESC-015 in threat-model.md.

POSIX Compliance Stance

Target: IEEE 1003.1-2024 Shell Command Language.

Category Status Notes
Reserved words, special parameters Full All 16 / all 8
Special built-in utilities Substantial 14/15; exec partial (L-PROC-001); times returns zeros; trap per L-SIG-001
Quoting, redirections, compound commands, functions Full
Word expansions Substantial Most expansions supported
Pipelines and lists Full |, &&, ||, ;, &+wait, !

Shell Features

Not Yet Implemented

Feature Priority Notes
History expansion Out of scope Interactive only
<> read-write redirect Low Parser rejects the operator; ScriptAnalysis therefore has no read-write mode (readwrite_redirect_opens_file, skipped)

Partially Implemented

Feature What Works What's Missing
Prefix env assignments VAR=val cmd temporarily sets env for cmd Array prefix assignments not in env
local Declaration Proper scoping in nested functions
return Basic usage Return value propagation
time Reserved-word pipelines; -p, --, GNU -f/-o/-a/-v; elapsed time, exit status, and Bashkit counters Host user/system CPU, RSS, and other process metrics are reported as unavailable, never fabricated
timeout Basic usage -k kill timeout
bash/sh -c, -n, -e, -x, -u, -f, -o option, script files, stdin, --version, --help Login shell

Builtins

Inventory is generated, see status/builtins.json and the builtins spec. No wholly unimplemented builtins are currently tracked; partial boundaries follow.

ID Tool Limitation Evidence
L-DATE-001 date TZ accepts bundled IANA identifiers/aliases only. POSIX rule strings and :zoneinfo paths are unsupported and intentionally resolve to UTC because the sandbox has no trusted host zoneinfo filesystem. GNU nanosecond formatting supports the useful %N/%3N/%6N/%9N forms, not other widths date_timezone_tests

Text Processing

What each tool does is covered by its spec tests (all unskipped tests pass in CI); only divergences and boundaries are recorded here.

ID Tool Limitation Evidence
L-AWK-001 awk Some complex regex patterns unsupported (engine shared with sed/grep, size-limited) stance
L-JQ-001 jq Alternative //: jaq errors on .foo applied to null instead of returning null (upstream jaq divergence) 1 skipped spec test
L-JQ-002 jq Regex natives compile the pattern per filter invocation; mapping test/match/split over many inputs can repeat compilation because jaq's native callback has no per-run cache state regex_compat.rs::re_native
L-YQ-001 yq Expressions are Bashkit jq expressions; mikefarah/yq-only node, comment, style, anchor, tag, filename, and eval-all operators are not implemented stance
L-YQ-002 yq YAML conversion follows YAML 1.1, deterministically sorts mapping keys at the JSON-value boundary, drops comments/style/anchors, and rejects aliases, custom tags, non-string mapping keys, and non-finite numbers rather than expanding graphs or silently corrupting data yaml_aliases_are_rejected_before_expansion, yaml_tags_and_non_string_keys_fail_closed, yaml_duplicate_keys_and_lossy_numbers_fail_closed, inplace_update_is_atomic_and_suppresses_stdout
L-YQ-003 yq Input/output conversion supports YAML and JSON only; mikefarah/yq's XML, CSV, TOML, properties, HCL, Lua, and INI formats are not exposed through yq stance
L-GREP-001 grep --color/--colour, --line-buffered accepted as no-ops l_grep_001_noop_flags
L-CURL-001 curl Spec-test coverage for methods/headers/auth/redirects not ported (needs http_client + allowlist in harness); payload behavior has integration and real-curl differential coverage stance
L-CURL-002 curl/wget Unknown options are ignored for compatibility, not rejected (real curl/wget error); deliberate leniency curl.rs
L-STR-001 strings Accepts dash-prefixed filenames (e.g. -data.bin), so only a lone unknown short option (-Q) is rejected as invalid; GNU rejects -data.bin too strings.rs

Safety boundaries (enforced, not bugs): printf width/precision caps, output buffer caps, getline file-cache cap, shared regex size limit, runtime regex cache cap (64 entries and 1 MB retained pattern text per evaluator), curl/wget timeouts clamped to [1, 600] s, multipart field-name sanitization, redirect handling hardened against credential leaks, and curl's aggregate data/multipart request body capped at 10 MB. Repeated mixed curl -d/--data, --data-raw, --data-binary, and --data-urlencode parts retain command-line order; -G/--get moves their joined payload to the query string. The jq input boundary alone accepts literal U+0000..U+001F controls inside JSON strings; controls outside strings and malformed quoting remain invalid. The json builtin, serde defaults, tool JSON contracts, --argjson, and --jsonargs stay strict. See jq Input Compatibility. Archive compression is intentionally in-process and limited to gzip and bzip2; GNU tar selectors for xz, lzip, lzma, compress, and zstd remain unsupported.

CLI

Divergences in the bashkit binary's one-shot (-c / script) mode. Positional parameters, stdin forwarding, and streaming output all work; what remains is how stdin is obtained.

ID Limitation Why Evidence
L-CLI-002 Host stdin is read to EOF before execution, not lazily when a command asks for it, and only when stdin is not a terminal. cmd | bashkit -c 'echo hi' waits for the writer to finish even though the script never reads; --no-stdin opts out The interpreter takes its stdin as a value up front (ExecOptions::stdin); lazy reads would need a reader-backed fd 0 in the sandbox. Capped at 10 MiB and bounded by the selected execution timeout crates/bashkit-cli/tests/cli_oneshot.rs

Parser

  • Single-quoted strings are completely literal (correct behavior)
  • Some complex nested structures may hit the parser timeout
  • Very long pipelines may cause stack issues
  • Bounded by configurable limits: timeout, fuel, input size, AST depth

Lifting a Limitation / Adding One

  1. Add a spec test demonstrating it, marked ### skip: reason (or an expected-fail differential test)
  2. Add a row here, with an L-* ID if it's an intentional decision
  3. When lifting: un-skip the test, delete the row, update referencing code comments in the same PR