diff --git a/README.md b/README.md index 032504b..f467320 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,8 @@ shell = strands_shell.Shell( ) ``` +> ⚠️ **`mode: "direct"` mounts are live.** The agent can read and modify host files in real time. Use only for designated output directories. Never direct-bind directories containing secrets, credentials, or configuration you don't want the agent to modify. + ### TOML You can load all of this from a config file instead: @@ -174,6 +176,8 @@ If you declare `[[mcp]]` servers in your TOML config, they show up as Lua module ## Security Model +> **Strands Shell is a mediation layer, not a security sandbox.** It enforces what the agent *should* access via Kernel-mediated deny-by-default. It does NOT protect against: memory-safety exploits in the shell engine itself, timing side-channels, or an attacker who controls the host process. For multi-tenant or adversarial workloads, run each Shell instance inside a container or microVM. + The Kernel mediates everything; it runs in the same process as your code, not in a VM. If your threat model is "untrusted tenant running arbitrary code," put Strands Shell inside a container too. For "my agent shouldn't access things I haven't explicitly allowed," the Kernel handles it. **Default-deny. You allowlist what the agent can reach:** @@ -189,6 +193,16 @@ If you bypass any of these, report it. See [SECURITY.md](SECURITY.md). **Multi-tenant:** a Shell instance is single-owner. If you're serving multiple agents, create one Shell per session. Construction is cheap (no containers, no VMs, just an in-memory VFS), so spinning up per-request is the intended pattern. +### Secure Defaults + +Out of the box, the shell is an empty sandbox — no files, no network, no credentials. When you grant access, follow least privilege: + +- **Prefer `mode: "copy"` over `mode: "direct"` for source code.** Copy-on-create isolates the agent from your live files. Use `direct` only for output directories where the agent needs to persist results. +- **Scope binds narrowly.** Bind `/my/project/src` rather than `/my/project` or `/`. The agent doesn't need your `.git/`, `.env`, or `node_modules/`. +- **Allowlist URLs explicitly.** Don't use `allowed_urls: ["https://"]` — this disables SSRF protection entirely. List the specific API endpoints the agent needs. +- **Set timeouts.** The default has no per-command timeout. Set `timeout` to bound runaway commands (30s is reasonable for most agent loops). +- **Use limits.** Set `max_output` to prevent agents from filling memory with unbounded command output (1MB is a good default). + ## Commands 25 builtins, 33 commands, and a Bourne-compatible shell with pipes, loops, functions, and subshells. diff --git a/SECURITY.md b/SECURITY.md index 95129c0..9425cc3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -21,23 +21,72 @@ treated as a security issue, including: bypass) - Escaping Kernel mediation to make direct syscalls, `fork`/`exec`, or otherwise reach the host environment +- Crafted input that causes the shell engine to panic, consume unbounded + memory, or hang indefinitely (e.g. unbounded recursion in the parser, Lua + memory exhaustion without limits) +- Injected credentials appearing in command output, error messages, + environment variable dumps, or the Lua scripting context accessible to the + agent +- Using symlinks, `..` components, or race conditions in bind mounts to + escape the declared filesystem boundary +- Discrepancies between how the SSRF guard parses a URL and how the HTTP + client interprets it (e.g. userinfo injection, encoding tricks, scheme + confusion) +- Any mechanism that causes credentials to be sent to a destination other + than the originally-matched URL prefix, including via redirects +- One MCP client session accessing state (VFS, environment, credentials) + belonging to another session on the same server process -Some behaviors are explicitly out of scope. Best-effort resource limits -(timeouts, output caps, fd/inode limits, pipeline depth), speculative side -channels (Spectre and similar), and multi-tenancy within a single process are -**not** part of the security boundary. See the -[Security Model](README.md#security-model) in the README for the full threat -model and guidance on running Strands Shell inside VM- or container-level + +## Security Architecture + +The security boundary is the **Kernel trait** (`src/os.rs`). All filesystem, +network, and credential operations flow through this interface. The default +`VfsKernel` implementation enforces: + +1. **Filesystem isolation** — in-memory VFS with explicit bind mounts; no path + can escape declared mounts +2. **Network SSRF guard** — two-layer check: URL-level parse + DNS-resolution-time + IP filtering via `SafeResolver`. Blocks RFC1918, link-local, loopback, IMDS, + IPv4-mapped-IPv6, 6to4, Teredo +3. **Credential injection** — per-URL prefix matching with path-boundary checks; + credentials injected only on original request, stripped on redirects +4. **No syscalls** — pure userspace shell; no `fork`, `exec`, or raw syscall paths + +Custom Kernel implementations (for embedding in other runtimes) carry their own +security properties. Reports about the `VfsKernel` implementation are in scope; +reports about third-party Kernel implementations should go to those maintainers. + + +## Out of Scope + +The following are explicitly **not** part of the security boundary and will not +be treated as security issues: + +- Resource exhaustion via CPU/memory within configured limits (limits are + best-effort, use OS-level cgroups for hard guarantees) +- Speculative execution and side-channel attacks (same-process architecture, + use VM isolation for this threat model) +- Multi-tenancy within a single OS process (documented non-goal, one Shell + instance per session is the contract) +- Agent reading files it was explicitly granted access to via binds (working + as designed, the bind is the grant) +- Lua scripts consuming memory up to configured limits (limits are advisory, + Lua sandbox is not a security boundary) + +See the [Security Model](README.md#security-model) in the README for the full +threat model and guidance on running Strands Shell inside VM- or container-level isolation when stronger guarantees are required. + ## Reporting Security Issues -Amazon Web Services (AWS) is dedicated to the responsible disclosure of security vulnerabilities. - -We kindly ask that you **do not** open a public GitHub issue to report security concerns. - -Instead, please submit the issue to the AWS Vulnerability Disclosure Program via [HackerOne](https://hackerone.com/aws_vdp) or send your report via [email](mailto:aws-security@amazon.com). - -For more details, visit the [AWS Vulnerability Reporting Page](http://aws.amazon.com/security/vulnerability-reporting/). +Amazon Web Services (AWS) is dedicated to the responsible disclosure of security vulnerabilities. + +We kindly ask that you **do not** open a public GitHub issue to report security concerns. + +Instead, please submit the issue to the AWS Vulnerability Disclosure Program via [HackerOne](https://hackerone.com/aws_vdp) or send your report via [email](mailto:aws-security@amazon.com). + +For more details, visit the [AWS Vulnerability Reporting Page](http://aws.amazon.com/security/vulnerability-reporting/). Thank you in advance for collaborating with us to help protect our customers.