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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:**
Expand All @@ -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.
Expand Down
75 changes: 62 additions & 13 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Loading