Skip to content

Commit b93ebc3

Browse files
authored
docs: document Claude bypassPermissions/--allowed-tools security boundary (#28174)
1 parent bd75cb6 commit b93ebc3

4 files changed

Lines changed: 64 additions & 0 deletions

File tree

.changeset/patch-document-bypasspermissions-security-boundary.md

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

AGENTS.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,25 @@ When developing a new command:
679679
### Go Code Style
680680
- **ALWAYS use `any` instead of `interface{}`** - Use the modern `any` type alias (Go 1.18+) for consistency across the codebase
681681

682+
### Claude Engine Tool Enforcement Security Model
683+
684+
When adding code to `pkg/workflow/claude_engine.go` or `pkg/workflow/claude_tools.go`, be aware of how Claude's permission mode affects tool enforcement:
685+
686+
**Two permission modes are used at runtime:**
687+
688+
1. **`acceptEdits` (default)** — Claude Code honors `--allowed-tools` as the effective tool boundary. Workflow `tools:` and `mcp-servers: allowed:` restrictions are enforced client-side.
689+
690+
2. **`bypassPermissions`** — Claude Code silently ignores `--allowed-tools`. Every tool exposed by the MCP gateway is reachable regardless of the workflow's declared tool configuration. This mode is only used when the workflow grants unrestricted bash access (e.g., `bash: "*"`, `bash: [":*"]`, or `bash: null`).
691+
692+
**Security boundary in `bypassPermissions` mode:**
693+
When `bypassPermissions` is active, the **MCP gateway `allowed:` filter is the sole effective tool boundary**. The compiled `tools` field in the gateway configuration (`/tmp/gh-aw/mcp-config/mcp-servers.json`) controls which tools each MCP server exposes. Gateway-side enforcement applies regardless of what the agent requests.
694+
695+
**Implication for code changes:**
696+
- `hasBashWildcardInTools()` in `claude_tools.go` determines which mode is selected — changes here affect the security boundary
697+
- `computeAllowedClaudeToolsString()` builds the `--allowed-tools` string — only effective in `acceptEdits` mode
698+
- `convert_gateway_config_claude.sh` preserves the `tools` field from gateway output — this is what enforces restrictions in `bypassPermissions` mode
699+
- Do not remove or weaken either enforcement layer; they complement each other for different access scenarios
700+
682701
### Channel Lifecycle Guidelines
683702

684703
Go channels require explicit lifecycle management to prevent goroutine leaks and resource exhaustion. Follow these guidelines when working with channels:

docs/src/content/docs/guides/mcps.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ mcp-servers:
139139
allowed: ["search_pages", "get_page"] # or ["*"] to allow all
140140
```
141141

142+
The `allowed:` filter is enforced at the **MCP gateway level** — the gateway only exposes the listed tools to the agent. This enforcement applies regardless of which AI engine or permission mode is in use.
143+
144+
> [!IMPORTANT]
145+
> For Claude workflows that grant unrestricted bash access (`bash: "*"` or `bash: [":*"]`), Claude runs in `bypassPermissions` mode and its `--allowed-tools` flag is silently ignored. In that case, the `allowed:` gateway filter is the **sole effective tool boundary**. Always specify `allowed:` on each `mcp-servers:` entry when tool restrictions matter. See [Claude Tool Enforcement Security Model](/gh-aw/reference/engines/#claude-tool-enforcement-security-model) for details.
146+
142147
## Shared MCP Configurations
143148

144149
Pre-configured MCP server specifications are available in [`.github/workflows/shared/mcp/`](https://github.com/github/gh-aw/tree/main/.github/workflows/shared/mcp) and can be copied or imported directly. Examples include:

docs/src/content/docs/reference/engines.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,41 @@ timeout-minutes: 60
339339
| `max-turns` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | Iteration budget (Claude only) |
340340
| `max-continuations` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Autopilot run budget (Copilot only) |
341341

342+
## Claude Tool Enforcement Security Model
343+
344+
Claude Code uses one of two permission modes at runtime, and which mode is selected determines whether the declared `tools:` allowlist is enforced:
345+
346+
### `acceptEdits` mode (default)
347+
348+
By default, gh-aw starts Claude Code with `--permission-mode acceptEdits`. In this mode, Claude honors the `--allowed-tools` flag. The workflow's declared `tools:` and `mcp-servers: allowed:` configuration is compiled into an explicit allowlist and passed to the Claude CLI. Only the tools listed there are accessible to the agent.
349+
350+
### `bypassPermissions` mode (unrestricted bash)
351+
352+
When the workflow grants unrestricted bash access — `bash: "*"`, `bash: [":*"]`, or `bash: null` — gh-aw switches to `--permission-mode bypassPermissions`. **In this mode, Claude Code silently ignores `--allowed-tools`.** Every tool exposed by the MCP gateway is reachable regardless of the workflow's declared tool configuration.
353+
354+
> [!WARNING]
355+
> Do not rely on `tools:` or `mcp-servers: allowed:` for security guarantees when unrestricted bash is granted. In `bypassPermissions` mode, the agent can already run arbitrary shell commands, so `--allowed-tools` provides no meaningful additional boundary.
356+
357+
### Gateway-side enforcement
358+
359+
The **MCP gateway's `allowed:` filter is the sole effective tool boundary in `bypassPermissions` mode** (and a second layer of enforcement in `acceptEdits` mode). gh-aw compiles the `allowed:` list from each `mcp-servers:` entry into the gateway configuration before the agent starts. The gateway enforces this list server-side, regardless of what the agent requests.
360+
361+
```yaml wrap
362+
mcp-servers:
363+
notion:
364+
container: "mcp/notion"
365+
allowed: ["search_pages", "get_page"] # enforced at gateway level
366+
```
367+
368+
### Summary
369+
370+
| Workflow config | Permission mode | `--allowed-tools` enforced? | Gateway `allowed:` enforced? |
371+
|---|---|:---:|:---:|
372+
| No unrestricted bash | `acceptEdits` | ✅ Yes | ✅ Yes |
373+
| `bash: "*"` / `bash: [":*"]` / `bash: null` | `bypassPermissions` | ❌ No | ✅ Yes |
374+
375+
For workflows that must restrict which MCP tools are accessible, always specify `allowed:` on each `mcp-servers:` entry. This applies regardless of whether unrestricted bash is used.
376+
342377
## Related Documentation
343378

344379
- [Frontmatter](/gh-aw/reference/frontmatter/) - Complete configuration reference

0 commit comments

Comments
 (0)