From 96ac3d7065c8f87084f77393c54f37181322c602 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:54:23 +0000 Subject: [PATCH 1/2] Initial plan From 7c5b43420d9f1e1b118c879315d69133c78b09fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 22:08:38 +0000 Subject: [PATCH 2/2] docs: document bypassPermissions + --allowed-tools security boundary for Claude engine Add explicit documentation explaining that --allowed-tools has no enforcement effect in bypassPermissions mode (unrestricted bash), and that the MCP gateway allowed: filter is the sole effective tool boundary in that case. - AGENTS.md: Add Claude Engine Tool Enforcement Security Model section - docs/reference/engines.md: Add Claude Tool Enforcement Security Model section - docs/guides/mcps.md: Add gateway-side enforcement note for MCP tool filtering - Add changeset for documentation update Agent-Logs-Url: https://github.com/github/gh-aw/sessions/17ba2d42-4fed-4f67-91ee-88e9c871e902 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- ...ent-bypasspermissions-security-boundary.md | 5 +++ AGENTS.md | 19 ++++++++++ docs/src/content/docs/guides/mcps.md | 5 +++ docs/src/content/docs/reference/engines.md | 35 +++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 .changeset/patch-document-bypasspermissions-security-boundary.md diff --git a/.changeset/patch-document-bypasspermissions-security-boundary.md b/.changeset/patch-document-bypasspermissions-security-boundary.md new file mode 100644 index 00000000000..9d4869e2db9 --- /dev/null +++ b/.changeset/patch-document-bypasspermissions-security-boundary.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Document Claude `bypassPermissions` + `--allowed-tools` security boundary: clarify in AGENTS.md, engines reference, and MCP guide that `--allowed-tools` is silently ignored in `bypassPermissions` mode (unrestricted bash) and that the MCP gateway `allowed:` filter is the sole effective tool boundary in that case. diff --git a/AGENTS.md b/AGENTS.md index 728e2746665..d711acfc9d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -679,6 +679,25 @@ When developing a new command: ### Go Code Style - **ALWAYS use `any` instead of `interface{}`** - Use the modern `any` type alias (Go 1.18+) for consistency across the codebase +### Claude Engine Tool Enforcement Security Model + +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: + +**Two permission modes are used at runtime:** + +1. **`acceptEdits` (default)** — Claude Code honors `--allowed-tools` as the effective tool boundary. Workflow `tools:` and `mcp-servers: allowed:` restrictions are enforced client-side. + +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`). + +**Security boundary in `bypassPermissions` mode:** +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. + +**Implication for code changes:** +- `hasBashWildcardInTools()` in `claude_tools.go` determines which mode is selected — changes here affect the security boundary +- `computeAllowedClaudeToolsString()` builds the `--allowed-tools` string — only effective in `acceptEdits` mode +- `convert_gateway_config_claude.sh` preserves the `tools` field from gateway output — this is what enforces restrictions in `bypassPermissions` mode +- Do not remove or weaken either enforcement layer; they complement each other for different access scenarios + ### Channel Lifecycle Guidelines Go channels require explicit lifecycle management to prevent goroutine leaks and resource exhaustion. Follow these guidelines when working with channels: diff --git a/docs/src/content/docs/guides/mcps.md b/docs/src/content/docs/guides/mcps.md index ab798067795..eb0f4e6cae9 100644 --- a/docs/src/content/docs/guides/mcps.md +++ b/docs/src/content/docs/guides/mcps.md @@ -139,6 +139,11 @@ mcp-servers: allowed: ["search_pages", "get_page"] # or ["*"] to allow all ``` +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. + +> [!IMPORTANT] +> 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. + ## Shared MCP Configurations 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: diff --git a/docs/src/content/docs/reference/engines.md b/docs/src/content/docs/reference/engines.md index 179cae81db8..ab68a87a9d2 100644 --- a/docs/src/content/docs/reference/engines.md +++ b/docs/src/content/docs/reference/engines.md @@ -339,6 +339,41 @@ timeout-minutes: 60 | `max-turns` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | Iteration budget (Claude only) | | `max-continuations` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Autopilot run budget (Copilot only) | +## Claude Tool Enforcement Security Model + +Claude Code uses one of two permission modes at runtime, and which mode is selected determines whether the declared `tools:` allowlist is enforced: + +### `acceptEdits` mode (default) + +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. + +### `bypassPermissions` mode (unrestricted bash) + +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. + +> [!WARNING] +> 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. + +### Gateway-side enforcement + +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. + +```yaml wrap +mcp-servers: + notion: + container: "mcp/notion" + allowed: ["search_pages", "get_page"] # enforced at gateway level +``` + +### Summary + +| Workflow config | Permission mode | `--allowed-tools` enforced? | Gateway `allowed:` enforced? | +|---|---|:---:|:---:| +| No unrestricted bash | `acceptEdits` | ✅ Yes | ✅ Yes | +| `bash: "*"` / `bash: [":*"]` / `bash: null` | `bypassPermissions` | ❌ No | ✅ Yes | + +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. + ## Related Documentation - [Frontmatter](/gh-aw/reference/frontmatter/) - Complete configuration reference