Skip to content

Commit 9bf907c

Browse files
docs: expand security documentation in README and SECURITY.md (#30)
Co-authored-by: Max Rattray <maxrat@amazon.com>
1 parent d29ff94 commit 9bf907c

2 files changed

Lines changed: 76 additions & 13 deletions

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ shell = strands_shell.Shell(
139139
)
140140
```
141141

142+
> ⚠️ **`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.
143+
142144
### TOML
143145

144146
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
174176

175177
## Security Model
176178

179+
> **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.
180+
177181
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.
178182

179183
**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).
189193

190194
**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.
191195

196+
### Secure Defaults
197+
198+
Out of the box, the shell is an empty sandbox — no files, no network, no credentials. When you grant access, follow least privilege:
199+
200+
- **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.
201+
- **Scope binds narrowly.** Bind `/my/project/src` rather than `/my/project` or `/`. The agent doesn't need your `.git/`, `.env`, or `node_modules/`.
202+
- **Allowlist URLs explicitly.** Don't use `allowed_urls: ["https://"]` — this disables SSRF protection entirely. List the specific API endpoints the agent needs.
203+
- **Set timeouts.** The default has no per-command timeout. Set `timeout` to bound runaway commands (30s is reasonable for most agent loops).
204+
- **Use limits.** Set `max_output` to prevent agents from filling memory with unbounded command output (1MB is a good default).
205+
192206
## Commands
193207

194208
25 builtins, 33 commands, and a Bourne-compatible shell with pipes, loops, functions, and subshells.

SECURITY.md

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,72 @@ treated as a security issue, including:
2121
bypass)
2222
- Escaping Kernel mediation to make direct syscalls, `fork`/`exec`, or
2323
otherwise reach the host environment
24+
- Crafted input that causes the shell engine to panic, consume unbounded
25+
memory, or hang indefinitely (e.g. unbounded recursion in the parser, Lua
26+
memory exhaustion without limits)
27+
- Injected credentials appearing in command output, error messages,
28+
environment variable dumps, or the Lua scripting context accessible to the
29+
agent
30+
- Using symlinks, `..` components, or race conditions in bind mounts to
31+
escape the declared filesystem boundary
32+
- Discrepancies between how the SSRF guard parses a URL and how the HTTP
33+
client interprets it (e.g. userinfo injection, encoding tricks, scheme
34+
confusion)
35+
- Any mechanism that causes credentials to be sent to a destination other
36+
than the originally-matched URL prefix, including via redirects
37+
- One MCP client session accessing state (VFS, environment, credentials)
38+
belonging to another session on the same server process
2439

25-
Some behaviors are explicitly out of scope. Best-effort resource limits
26-
(timeouts, output caps, fd/inode limits, pipeline depth), speculative side
27-
channels (Spectre and similar), and multi-tenancy within a single process are
28-
**not** part of the security boundary. See the
29-
[Security Model](README.md#security-model) in the README for the full threat
30-
model and guidance on running Strands Shell inside VM- or container-level
40+
41+
## Security Architecture
42+
43+
The security boundary is the **Kernel trait** (`src/os.rs`). All filesystem,
44+
network, and credential operations flow through this interface. The default
45+
`VfsKernel` implementation enforces:
46+
47+
1. **Filesystem isolation** — in-memory VFS with explicit bind mounts; no path
48+
can escape declared mounts
49+
2. **Network SSRF guard** — two-layer check: URL-level parse + DNS-resolution-time
50+
IP filtering via `SafeResolver`. Blocks RFC1918, link-local, loopback, IMDS,
51+
IPv4-mapped-IPv6, 6to4, Teredo
52+
3. **Credential injection** — per-URL prefix matching with path-boundary checks;
53+
credentials injected only on original request, stripped on redirects
54+
4. **No syscalls** — pure userspace shell; no `fork`, `exec`, or raw syscall paths
55+
56+
Custom Kernel implementations (for embedding in other runtimes) carry their own
57+
security properties. Reports about the `VfsKernel` implementation are in scope;
58+
reports about third-party Kernel implementations should go to those maintainers.
59+
60+
61+
## Out of Scope
62+
63+
The following are explicitly **not** part of the security boundary and will not
64+
be treated as security issues:
65+
66+
- Resource exhaustion via CPU/memory within configured limits (limits are
67+
best-effort, use OS-level cgroups for hard guarantees)
68+
- Speculative execution and side-channel attacks (same-process architecture,
69+
use VM isolation for this threat model)
70+
- Multi-tenancy within a single OS process (documented non-goal, one Shell
71+
instance per session is the contract)
72+
- Agent reading files it was explicitly granted access to via binds (working
73+
as designed, the bind is the grant)
74+
- Lua scripts consuming memory up to configured limits (limits are advisory,
75+
Lua sandbox is not a security boundary)
76+
77+
See the [Security Model](README.md#security-model) in the README for the full
78+
threat model and guidance on running Strands Shell inside VM- or container-level
3179
isolation when stronger guarantees are required.
3280

81+
3382
## Reporting Security Issues
3483

35-
Amazon Web Services (AWS) is dedicated to the responsible disclosure of security vulnerabilities.
36-
37-
We kindly ask that you **do not** open a public GitHub issue to report security concerns.
38-
39-
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).
40-
41-
For more details, visit the [AWS Vulnerability Reporting Page](http://aws.amazon.com/security/vulnerability-reporting/).
84+
Amazon Web Services (AWS) is dedicated to the responsible disclosure of security vulnerabilities.
85+
86+
We kindly ask that you **do not** open a public GitHub issue to report security concerns.
87+
88+
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).
89+
90+
For more details, visit the [AWS Vulnerability Reporting Page](http://aws.amazon.com/security/vulnerability-reporting/).
4291

4392
Thank you in advance for collaborating with us to help protect our customers.

0 commit comments

Comments
 (0)