feat(sdk): add permissions system for filesystem access control#2633
feat(sdk): add permissions system for filesystem access control#2633Nick Hollon (nick-hollon-lc) merged 9 commits intomainfrom
Conversation
Introduce `FilesystemPermission` and `ToolPermission` dataclasses with first-match-wins evaluation and glob pattern matching via wcmatch. `FilesystemMiddleware` enforces path-level read/write rules; `ToolPermissionMiddleware` enforces tool-level rules at the `wrap_tool_call` boundary. Subagents inherit parent permissions unless they specify their own. Also fixes grep tool missing `validate_path` call on its path argument, which previously allowed path traversal bypasses on grep specifically.
| # Disable a tool entirely | ||
| ToolPermission(name="execute", mode="deny") |
There was a problem hiding this comment.
would our guidance be to just remove the tool entirely? (e.g. configurability)
There was a problem hiding this comment.
not sure i totally uderstand this question. just an example of disabling a tool completely here
| from deepagents.permissions import ToolPermission | ||
|
|
||
| # Allow only pytest invocations, deny everything else | ||
| ToolPermission(name="execute", args={"command": "pytest *"}) |
There was a problem hiding this comment.
is this just intended for pattern matching on basic tool call args? What happens if I have a tool schema thats not just dict[str, str]?
There was a problem hiding this comment.
yeah just for basic args. it will stringify the arg before attempting the comparison
…ything. use artifacts on tool message to pass out enough info for post filtering in the permission middleware
Sydney Runkle (sydney-runkle)
left a comment
There was a problem hiding this comment.
excited about this!! great work, approving pending comments are addressed
fast follows -- return ToolMessage from all filesystem tools + status of error instead of just error strings for relevant cases
figure out what we're doing for ToolPermissions + execute stuff
…f error when we fail because permission denied
|
OK this looks pretty good!
|
> [!CAUTION] > Merging this PR will automatically publish to **PyPI** and create a **GitHub release**. For the full release process, see [`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md). --- _Everything below this line will be the GitHub release body._ --- ## [0.5.2](deepagents==0.5.1...deepagents==0.5.2) (2026-04-10) ### Features * Permissions system for filesystem access control ([#2633](#2633)) ([41dc759](41dc759)) * Scope permissions to routes for composite backends with sandbox default ([#2659](#2659)) ([6dd6122](6dd6122)) * Raise `ValueError` for permission paths without leading slash and path traversal ([#2665](#2665)) ([723d27d](723d27d)) * Implement `upload_files` for `StateBackend` ([#2661](#2661)) ([5798345](5798345)) ### Bug Fixes * Catch `PermissionError` in `FilesystemBackend` ripgrep ([#2571](#2571)) ([3d5d673](3d5d673)) --- _Everything above this line will be the GitHub release body._ --- > [!NOTE] > A **New Contributors** section is appended to the GitHub release notes automatically at publish time (see [Release Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline), step 2). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Mason Daugherty <github@mdrxy.com>
Examples
Read-only agent (no writes anywhere):
Sandbox the agent to a workspace directory:
Protect examples while allowing broad access:
Subagent with tighter permissions than the parent:
What it enforces
ls,read_file,glob,grep,write_file,edit_file) when the canonicalized path matches a deny rule. Paths are resolved viavalidate_path()to prevent traversal bypasses.ls,glob, andgrepresults usingToolMessage.artifact(the structured backend result object), then rebuilds the content string.permissionsby default. A subagent can specify its ownpermissionsfield, which replaces the parent's rules entirely.PermissionMiddlewareis placed last in the middleware stack so it sees all tools (including those injected by other middleware).What it does not enforce
executetool: shell commands can trivially bypass filesystem restrictions. Rather than silently failing, the middleware raisesNotImplementedErrorat init if the backend supports execution.ToolPermissionrules but they were deferred — pattern-matching tool arguments with globs is unsafe for shell commands and type coercion of non-string args is a footgun.