From 93e624ad2c2a07ad0b86a4f6d9c15e4026a2cbc3 Mon Sep 17 00:00:00 2001 From: XBetter <32125213+1998x-stack@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:09:22 +0800 Subject: [PATCH 1/5] ci(sandbox): install bwrap and probe actual Linux namespace isolation --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85f78a6..22bde42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,3 +25,20 @@ jobs: run: python -m compileall -q tiny_harness tests - name: Run tests run: python -m pytest -q tests --ignore=tests/test_integration.py + + sandbox-smoke: + name: Linux bubblewrap probe + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install sandbox test dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y bubblewrap util-linux + python -m pip install -e ".[dev]" + - name: Check actual kernel namespace support and run isolation tests + run: | + python -m pytest -q tests/test_shell_bwrap.py tests/test_sandbox_resource_limits.py -rs From a0613b8652b4c7655897a24869f1fea09c717746 Mon Sep 17 00:00:00 2001 From: XBetter <32125213+1998x-stack@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:09:40 +0800 Subject: [PATCH 2/5] docs(sandbox): spell out Linux bwrap mount and resource limitations --- docs/SHELL_SANDBOX.md | 87 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docs/SHELL_SANDBOX.md diff --git a/docs/SHELL_SANDBOX.md b/docs/SHELL_SANDBOX.md new file mode 100644 index 0000000..8cc6951 --- /dev/null +++ b/docs/SHELL_SANDBOX.md @@ -0,0 +1,87 @@ +# Linux Shell Isolation / Bubblewrap + +The built-in Shell skill supports an **optional**, Linux-only `bubblewrap` +execution mode. It is opt-in and does not change other tool handlers or +the default unsandboxed behavior. + +## Setup + +Install `bubblewrap` and util-linux `prlimit` from the operating system's +package manager, and verify the host permits unprivileged user namespaces. + +```bash +tiny-harness \ + --workspace /path/to/disposable-workspace \ + --provider deepseek \ + --skills files,shell \ + --shell-sandbox bubblewrap \ + --sandbox-cpu-seconds 30 \ + --sandbox-memory-mb 1024 \ + --sandbox-file-mb 32 +``` + +Shell retains the usual HITL approvals. Disabling HITL still requires a +separate explicit `--allow-unsafe-shell` option; the sandbox is not a +substitute for user authorization. + +## Namespace and filesystem contract + +- `--unshare-all` isolates namespaces, including network and PID. + There is **no** `--share-net` fallback. +- `--tmpfs /` starts with an empty root. Selected system binary and library + directories (`/usr`, `/bin`, `/lib`, etc.) are mounted **read-only**. + No blanket `--ro-bind / /` or `--ro-bind /etc /etc` is used. +- Only the explicitly configured workspace is writable as + `/workspace`. Relative `cwd` is mapped inside that mount after + canonicalization. `/tmp` is a private tmpfs. +- The environment is cleared; only PATH, HOME, TMPDIR and LANG are set + inside the sandbox. Outside API keys are not forwarded. +- The host `bwrap` executable and `prlimit` are resolved from the system + binary directories, not from the working directory or arbitrary PATH. + +## Resource constraints + +The `prlimit` wrapper imposes `RLIMIT_CPU`, `RLIMIT_AS`, and +`RLIMIT_FSIZE`. Defaults: CPU 30 seconds, virtual address space 1024 MiB, +file size 32 MiB. They are inherited by child processes. + +**Important:** These limits are *per process* and are **not aggregate cgroup +CPU or memory quotas**. The executor's existing timeout limits wall-clock +duration; its output cap limits retained output. `RLIMIT_AS` caps virtual +address space, which is not the same as RSS. The sandbox also does not limit +total disk writes across multiple files or processes. + +## Unsupported cases / failure + +If the OS is not Linux, the sandbox binaries are unavailable, the workspace +is invalid, the Linux kernel blocks namespace creation, or the sandbox +launcher returns an error, the operation fails. There is **no automatic +retry as an unsandboxed shell**. + +The legacy direct `run_command()` Python helper remains unsandboxed; +the backend applies only to the Shell skill's registered +`run_command_async` handler. + +## Limitations and validation + +- Workspace contents and symlinks remain untrusted. The mount boundary + does not protect credentials already stored **inside** the workspace. +- This does not sandbox custom third-party Python tool handlers, and it + does not claim to protect against kernel vulnerabilities or bind-mount + time-of-check/time-of-use races. +- Binary compatibility depends on which trusted system directories are + exposed. Programs requiring host services, sockets, device access, + arbitrary `/opt` paths, or networking may fail intentionally. +- The CI Python test matrix checks launcher construction, explicit opt-in, + missing-binary fail-closed behavior, and resource flags. The separate + **Linux bubblewrap probe** job installs bwrap and attempts an actual + isolation smoke test. That test explicitly **SKIPs** when the runner + cannot create unprivileged user namespaces. A green CI result with a + skip does not establish successful real isolation; inspect job logs. + +### Production follow-up + +A production multi-tenant deployment still needs cgroup v2 aggregate quotas, +verified seccomp/AppArmor/SELinux policy, process identity isolation, +safe bind-mount handling, independent credentials, and dedicated +platform-specific integration tests. From 1322264dfce55fd12cb2cccd5e7db69cfeae8aec Mon Sep 17 00:00:00 2001 From: XBetter <32125213+1998x-stack@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:09:58 +0800 Subject: [PATCH 3/5] docs(sandbox): mark optional isolation, real-smoke status and per-process limit caveats --- docs/security.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/security.md b/docs/security.md index 70765a1..238de8b 100644 --- a/docs/security.md +++ b/docs/security.md @@ -9,12 +9,13 @@ tiny-harness 是可执行工具的 Agent harness,**不是安全沙箱**。模 - HITL 默认审核 mutation/destructive/dangerous;destructive/dangerous 不可使用会话级永久批准。回调异常按拒绝处理。 - `ToolDef` 未声明风险时默认 mutation;未知 risk_level 在注册阶段拒绝;safe 风险等级仍执行文件路径守卫。 - `--no-hitl` 下内置 Shell 工具默认不可执行;使用 `--no-hitl --allow-unsafe-shell` 才能显式启用(不等于沙箱)。 +- 可选 `shell_sandbox="bubblewrap"` 为 Linux 内置 async Shell 提供 mount/network/PID namespace 约束,启动失败不自动降级;`prlimit` CPU/AS/FSIZE 限制是逐进程限制,非 cgroup 总配额。参见 [SHELL_SANDBOX.md](SHELL_SANDBOX.md)。 - built-in Shell skill 的 async handler 运行于指定 cwd;POSIX 上超时/取消时终止进程组;不继承 `*_API_KEY` 等未在 allow-list 的环境变量。 - 会话快照使用原子替换与受限文件权限(POSIX `0600`)。 ## 未提供的隔离能力 -- **Shell 没有操作系统级文件系统隔离**:即使 cwd 在 workspace,命令仍可能访问机器上任何该进程有权限访问的路径;显式设置 `--no-hitl --allow-unsafe-shell` 后,模型可以无需审批执行 Shell。 +- **默认 `shell_sandbox=off` 时 Shell 没有操作系统级文件系统隔离**:即使 cwd 在 workspace,命令仍可能访问机器上该进程有权限访问的路径。开启 bubblewrap 只是选配 Linux namespace 隔离,尚未在所有目标环境做端到端验证。 - Shell 可能访问网络、用户凭据文件、Git 配置及 SSH agent;环境变量白名单不能覆盖全部隐式凭据路径。 - 历史同步 `run_command` 辅助函数保留用于兼容,并未获得与 registered async Shell 等价的进程组取消保障。 - Path guard 的检查和 handler 的实际打开文件不是一个不可分割的 OS 操作;工作区内容可被外部进程并发替换时仍有 TOCTOU 风险。 From 31addf4ea8fc844d29c129ed0aa84d49251487bf Mon Sep 17 00:00:00 2001 From: XBetter <32125213+1998x-stack@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:10:01 +0800 Subject: [PATCH 4/5] docs(sandbox): mark optional isolation, real-smoke status and per-process limit caveats --- docs/OPTIMIZATION_STATUS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/OPTIMIZATION_STATUS.md b/docs/OPTIMIZATION_STATUS.md index 7b77563..269d399 100644 --- a/docs/OPTIMIZATION_STATUS.md +++ b/docs/OPTIMIZATION_STATUS.md @@ -26,12 +26,14 @@ | Phase 1 | [#19](https://github.com/1998x-stack/tiny-harness/pull/19) | OpenAI/Anthropic 流式请求在首次输出前重试 429/5xx/部分网络错误,Retry-After 上限;输出后禁重试 | PR CI 通过;真实 API 冒烟仍待执行 | | Phase 4 | [#21](https://github.com/1998x-stack/tiny-harness/pull/21) | ToolDef 显式 path_args、所有声明路径规范化、第三方兼容字段全部检查 | PR CI 通过;仍非 OS 沙箱 | | Phase 4 | [#22](https://github.com/1998x-stack/tiny-harness/pull/22) | Draft 2020-12 JSON Schema(嵌套/组合/数组),注册时检查 Schema,拒绝远程引用 | PR CI 通过;引入 jsonschema 运行时依赖 | +| Phase 5 | [#24](https://github.com/1998x-stack/tiny-harness/pull/24) | Linux Bubblewrap 可选 shell namespace + workspace-only bind, 缺失时 fail-closed | Python CI 通过,实际 namespace smoke 最初被跳过 | +| Phase 5 | [#25](https://github.com/1998x-stack/tiny-harness/pull/25) | prlimit CPU/虚拟内存/单文件大小逐进程资源约束 | Python CI 通过,非 cgroup 总配额 | ## 待完成事项(不应标记为已修复) ### P1:继续按独立 PR 完成 -1. **真正的执行沙箱**:Shell 的 cwd、HITL 和环境过滤均不是 filesystem/network sandbox。评估 Linux bubblewrap/container;对 Windows/macOS 另定明确边界。**禁止**宣传当前 Shell 已沙箱化。 +1. **OS 沙箱验证及增强**:已实现可选 Linux Bubblewrap namespace 和 prlimit;尚需真实机器上的隔离测试、cgroup 总资源配额、系统级加固和 Windows/macOS 后端。默认 off 仍是不隔离的 Shell,**禁止**把 opt-in Bubblewrap 描述为完整生产沙箱。 2. **ToolDef 参数级策略**:基础风险枚举与 no-HITL Shell 显式 opt-in 已完成;后续引入细粒度 allow/ask/deny、按命令参数审批策略,扩展其他危险工具的无 HITL 拒绝边界。 3. **Provider 全面契约**:Anthropic 自定义 endpoint、截断判断和首次输出前的有限流式重试已完成;仍需统一非流式/流式重试、DeepSeek thinking 透传、HTTP 客户端连接复用及真实 API 冒烟。 4. **上下文窗口**:已提供可配置窗口与启发式压缩;仍需 provider 真实 usage 回收、tokenizer 精确统计、可审计摘要和更精细的窗口分层治理。 From 109660e26eb7c8d7a88d85d158eee3dd632f1a03 Mon Sep 17 00:00:00 2001 From: XBetter <32125213+1998x-stack@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:10:04 +0800 Subject: [PATCH 5/5] docs(sandbox): mark optional isolation, real-smoke status and per-process limit caveats --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 09de0bd..eb9bb15 100644 --- a/README.md +++ b/README.md @@ -313,7 +313,15 @@ ANTHROPIC_API_KEY="sk-ant-..." pytest tests/test_integration.py -v -m integratio The configured `context_window` reserves `max_tokens` for model output. When input history grows too large, the harness compacts oversized tool outputs and can discard complete older turns, preserving the latest user turn and tool-call/result pairing. This budget currently uses a heuristic estimate, not model-specific tokenizers. -The built-in Shell skill requires human approval by default. In `--no-hitl` mode, explicit unsandboxed Shell access requires `--allow-unsafe-shell`; **this does not provide OS filesystem or network isolation**. +The built-in Shell skill requires human approval by default. In `--no-hitl` mode, Shell additionally requires `--allow-unsafe-shell`. The default `shell_sandbox=off` is **not** OS-isolated; Linux users may opt into `--shell-sandbox bubblewrap` for namespace-isolated execution with per-process resource limits. See [Linux Shell Sandbox](docs/SHELL_SANDBOX.md). + +## Optional Linux Shell Sandbox + +```bash +tiny-harness --provider deepseek --skills files,shell --shell-sandbox bubblewrap --sandbox-cpu-seconds 30 --sandbox-memory-mb 1024 --sandbox-file-mb 32 +``` + +Requires system-installed `bubblewrap` and `prlimit`, and working unprivileged namespaces. Sandboxed mode blocks external network by default; it never falls back to a host shell if setup fails. These per-process limits are **not** cgroup-wide quotas. ## Security