Skip to content

Commit 8a6e2c6

Browse files
Copilotlpcox
andauthored
Extend guard coverage checker to also scan GitHub CLI (cli/cli) operations
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/37f71d5b-7091-4a97-847e-f297587b0a82
1 parent 2cdbc36 commit 8a6e2c6

2 files changed

Lines changed: 127 additions & 40 deletions

File tree

.github/workflows/github-mcp-guard-coverage-checker.lock.yml

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

.github/workflows/github-mcp-guard-coverage-checker.md

Lines changed: 120 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: GitHub MCP Guard Coverage Checker
3-
description: Daily check that compares tools exposed by the official GitHub MCP server against the guard implementation and creates issues for any coverage gaps.
3+
description: Daily check that compares tools exposed by the official GitHub MCP server and GitHub CLI operations against the guard implementation and creates issues for any coverage gaps.
44
on:
55
schedule: daily
66
workflow_dispatch:
@@ -32,7 +32,7 @@ tools:
3232
cache-memory: true
3333
github:
3434
toolsets: [default]
35-
repos: ["github/gh-aw-mcpg", "github/github-mcp-server"]
35+
repos: ["github/gh-aw-mcpg", "github/github-mcp-server", "cli/cli"]
3636
min-integrity: unapproved
3737
bash:
3838
- "*"
@@ -43,21 +43,22 @@ strict: true
4343

4444
# 🔍 GitHub MCP Guard Coverage Checker
4545

46-
You are an AI security auditor that verifies the GitHub guard implementation covers all tools exposed by the official [GitHub MCP server](https://github.com/github/github-mcp-server). Your job is to find tools that are missing from the guard's classification logic and report them with actionable remediation steps.
46+
You are an AI security auditor that verifies the GitHub guard implementation covers all tools exposed by the official [GitHub MCP server](https://github.com/github/github-mcp-server) **and** all write/mutating operations reachable via the [GitHub CLI](https://github.com/cli/cli). Your job is to find operations that are missing from the guard's classification logic and report them with actionable remediation steps.
4747

4848
## Context
4949

5050
- **Repository**: ${{ github.repository }}
5151
- **Run ID**: ${{ github.run_id }}
5252
- **Guard implementation**: `guards/github-guard/rust-guard/src/tools.rs` and `guards/github-guard/rust-guard/src/labels/tool_rules.rs`
53-
- **Upstream source**: `github/github-mcp-server`
53+
- **Upstream sources**: `github/github-mcp-server` (MCP tools), `cli/cli` (GitHub CLI commands)
5454

5555
## Step 1: Load Previous State from Cache
5656

5757
Use cache-memory to check:
5858
- `last_run_date`: ISO date of the last coverage check
59-
- `known_gaps`: Array of tool names already reported as gaps (to avoid duplicate issues)
60-
- `last_upstream_tools_hash`: A short hash or count of the tool list from the last run (to detect when new tools are added)
59+
- `known_gaps`: Array of tool/command names already reported as gaps (to avoid duplicate issues)
60+
- `last_upstream_tools_hash`: A short hash or count of the MCP tool list from the last run (to detect when new tools are added)
61+
- `last_cli_commands_hash`: A short hash or count of the CLI write-command list from the last run (to detect when new CLI commands are added)
6162

6263
If cache is empty, start fresh.
6364

@@ -97,13 +98,67 @@ Then read the relevant source files to extract tool function names. In Go MCP se
9798

9899
### 2.3 Build the canonical tool list
99100

100-
Produce a complete, deduplicated list of tool names from the upstream GitHub MCP server. This is your **reference set**. Record the total count for cache comparison.
101+
Produce a complete, deduplicated list of tool names from the upstream GitHub MCP server. This is your **MCP reference set**. Record the total count for cache comparison.
101102

102-
## Step 3: Read the Guard Implementation
103+
## Step 3: Fetch Write Operations from the GitHub CLI
104+
105+
The GitHub CLI (`cli/cli`) exposes a rich set of GitHub API operations through its commands. Any write or mutating operation reachable via the CLI that could also be invoked through the MCP gateway should be represented in the guard's classification.
106+
107+
### 3.1 Discover CLI command categories
108+
109+
Read the CLI source structure to understand what command groups exist:
110+
111+
```
112+
Use github get_file_contents with owner=cli, repo=cli, path=pkg/cmd, ref=trunk
113+
```
114+
115+
This lists the top-level command directories (e.g., `pr/`, `issue/`, `repo/`, `gist/`, `release/`, `workflow/`, `label/`, `project/`, `org/`, `secret/`, etc.).
116+
117+
### 3.2 Read write-command implementations
118+
119+
For each command group that has write/mutating sub-commands, read the corresponding Go source files to extract the HTTP method (POST, PATCH, PUT, DELETE) used. Focus on these high-value categories:
120+
121+
- **`pkg/cmd/pr/`**: `create`, `merge`, `close`, `reopen`, `edit`, `review`, `comment`, `ready`, `convert`, `lock`, `unlock`
122+
- **`pkg/cmd/issue/`**: `create`, `close`, `reopen`, `edit`, `comment`, `lock`, `unlock`, `pin`, `unpin`, `transfer`, `delete`
123+
- **`pkg/cmd/repo/`**: `create`, `fork`, `delete`, `edit`, `archive`, `rename`, `transfer`, `set-default`
124+
- **`pkg/cmd/release/`**: `create`, `edit`, `delete`, `upload`
125+
- **`pkg/cmd/gist/`**: `create`, `edit`, `delete`
126+
- **`pkg/cmd/workflow/`**: `run`, `enable`, `disable`
127+
- **`pkg/cmd/label/`**: `create`, `edit`, `delete`, `clone`
128+
- **`pkg/cmd/project/`**: `create`, `edit`, `delete`, `link`, `unlink`, `item-add`, `item-edit`, `item-delete`, `item-archive`
129+
- **`pkg/cmd/secret/`**: `set`, `delete`
130+
- **`pkg/cmd/variable/`**: `set`, `delete`
131+
- **`pkg/cmd/org/`**: any write sub-commands
132+
133+
Use `get_file_contents` to read individual command files if needed. For example:
134+
135+
```
136+
Use github get_file_contents with owner=cli, repo=cli, path=pkg/cmd/pr/merge/merge.go, ref=trunk
137+
```
138+
139+
### 3.3 Extract GitHub API endpoints used by CLI write commands
140+
141+
For each write command file you read, note:
142+
- The REST API endpoint (e.g., `POST /repos/{owner}/{repo}/issues`)
143+
- The corresponding MCP tool name that covers the same operation (if any)
144+
- Whether the operation has a counterpart in the GitHub MCP server's tool set
145+
146+
The goal is to build a **CLI write-operations list**: a mapping of `{cli_command} → {rest_endpoint} → {mcp_tool_or_none}`.
147+
148+
### 3.4 Identify CLI operations without MCP/guard coverage
149+
150+
A CLI write operation has a **guard coverage gap** if:
151+
1. It uses a mutating HTTP method (POST, PATCH, PUT, DELETE) against the GitHub API, AND
152+
2. There is no equivalent MCP tool name in the guard's `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS`, AND
153+
3. The operation is not covered by a prefix pattern (`merge_*`, `delete_*`, `update_*`, `create_*`)
154+
155+
**Important**: You are looking for *semantic equivalences*, not exact name matches. For example, `gh issue comment --edit` maps to editing an issue comment, which might map to an `edit_issue_comment` MCP tool. Check whether the guard covers the underlying GitHub API operation, not just the CLI command name.
156+
157+
## Step 4: Read the Guard Implementation
103158

104159
Read the local guard files to understand what's currently covered:
105160

106-
### 3.1 Read tools.rs (explicit classifications)
161+
### 4.1 Read tools.rs (explicit classifications)
107162

108163
```bash
109164
cat guards/github-guard/rust-guard/src/tools.rs
@@ -114,15 +169,15 @@ This file contains:
114169
- `READ_WRITE_OPERATIONS`: explicit list of read-write tools
115170
- Pattern functions: `is_merge_operation`, `is_delete_operation`, `is_update_operation`, `is_create_operation`
116171

117-
### 3.2 Read tool_rules.rs (per-tool DIFC labeling)
172+
### 4.2 Read tool_rules.rs (per-tool DIFC labeling)
118173

119174
```bash
120175
cat guards/github-guard/rust-guard/src/labels/tool_rules.rs
121176
```
122177

123178
This file contains the `apply_tool_labels` function with a `match tool_name { ... }` block. Tools with explicit match arms have **specific DIFC labeling rules** (secrecy tags, integrity levels). Tools without explicit match arms fall through to default handling.
124179

125-
### 3.3 Build the guard coverage sets
180+
### 4.3 Build the guard coverage sets
126181

127182
From reading the code, produce:
128183

@@ -131,68 +186,80 @@ From reading the code, produce:
131186
3. **pattern_covered**: tools from the upstream list that match any pattern (`merge_*`, `delete_*`, `update_*`, `create_*`)
132187
4. **label_ruled**: set of tool names with explicit match arms in `apply_tool_labels`
133188

134-
## Step 4: Identify Coverage Gaps
189+
## Step 5: Identify Coverage Gaps
135190

136-
### 4.1 Classification gaps (tools.rs)
191+
### 5.1 MCP tool classification gaps (tools.rs)
137192

138-
A tool has a **classification gap** if it is in the upstream tool list AND:
193+
A tool has a **classification gap** if it is in the upstream MCP tool list AND:
139194
- It is NOT in `WRITE_OPERATIONS`
140195
- It is NOT in `READ_WRITE_OPERATIONS`
141196
- It does NOT match any prefix pattern (`merge_*`, `delete_*`, `update_*`, `create_*`)
142197
- AND it appears to perform write or mutating operations based on its name or description (e.g., tools with verbs like "add", "set", "enable", "disable", "submit", "publish", "request", "approve", "reject", "resolve", "reopen", "close", "lock", "unlock", "pin", "unpin", "convert")
143198

144199
For read-only tools (get, list, search, read), missing classification is expected and not a gap.
145200

146-
### 4.2 Labeling gaps (tool_rules.rs)
201+
### 5.2 MCP tool labeling gaps (tool_rules.rs)
202+
203+
A tool has a **labeling gap** if it is in the upstream MCP tool list AND has no explicit match arm in `apply_tool_labels`. This is lower severity than a classification gap, but still important for DIFC correctness — read tools that return repo-scoped data (issues, PRs, code, files) should have explicit secrecy/integrity rules.
204+
205+
### 5.3 GitHub CLI gaps
206+
207+
For each write operation discovered in Step 3.4, determine if the underlying GitHub API operation has guard coverage:
147208

148-
A tool has a **labeling gap** if it is in the upstream tool list AND has no explicit match arm in `apply_tool_labels`. This is lower severity than a classification gap, but still important for DIFC correctness — read tools that return repo-scoped data (issues, PRs, code, files) should have explicit secrecy/integrity rules.
209+
- If there is an equivalent MCP tool and it is already in `WRITE_OPERATIONS` / `READ_WRITE_OPERATIONS` (or covered by a pattern) → **covered, skip**.
210+
- If there is an equivalent MCP tool but it is NOT in the guard lists and not covered by any pattern → **MCP classification gap** (already captured in 5.1).
211+
- If there is **no equivalent MCP tool** for a CLI write command → flag as a **CLI-only gap**: the guard does not model this operation at all. Note the CLI command, the REST endpoint, and the GitHub API action it performs.
149212

150-
### 4.3 Stale entries (bonus check)
213+
For CLI-only gaps, the fix is to add a new entry to `WRITE_OPERATIONS` (or `READ_WRITE_OPERATIONS`) using a descriptive MCP-style tool name (snake_case) that maps to the CLI operation — or to file an issue requesting that the GitHub MCP server add a corresponding tool.
151214

152-
Check if any entries in `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` are **no longer in the upstream tool list**. These are stale guard entries that should be removed to keep the implementation clean.
215+
### 5.4 Stale entries (bonus check)
153216

154-
### 4.4 Filter known gaps
217+
Check if any entries in `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` are **no longer in the upstream MCP tool list** and also have no equivalent in the CLI write-operations list. These are stale guard entries that should be removed.
218+
219+
### 5.5 Filter known gaps
155220

156221
Remove any gaps that are already in `known_gaps` from the cache (previously reported). Only report **new** gaps discovered since the last run.
157222

158-
## Step 5: Determine Output
223+
## Step 6: Determine Output
159224

160225
### If no new gaps found
161226

162227
Call the `noop` safe output with a message like:
163-
> "GitHub MCP Guard coverage check complete — no new gaps found. Guard covers [N] tools from github-mcp-server. Last upstream count: [M] tools."
228+
> "GitHub guard coverage check complete — no new gaps found. MCP tools: [M] scanned. CLI write commands: [C] scanned. Guard write ops: [N]."
164229
165230
Then update cache:
166231
- `last_run_date`: today's ISO date
167-
- `last_upstream_tools_hash`: total count of upstream tools (as string)
232+
- `last_upstream_tools_hash`: total count of upstream MCP tools (as string)
233+
- `last_cli_commands_hash`: total count of CLI write commands scanned (as string)
168234

169235
### If new gaps are found
170236

171-
Proceed to Step 6 to create an issue.
237+
Proceed to Step 7 to create an issue.
172238

173-
## Step 6: Create a Gap Report Issue
239+
## Step 7: Create a Gap Report Issue
174240

175241
Create a GitHub issue using the `create-issue` safe output.
176242

177-
**Title**: `Guard coverage gap: [N] tools from github-mcp-server not fully covered`
243+
**Title**: `Guard coverage gap: [N] operations from github-mcp-server / GitHub CLI not fully covered`
178244

179245
**Body**:
180246

181247
```markdown
182248
## Summary
183249

184-
The GitHub guard does not fully cover **[N]** tool(s) from the [github-mcp-server](https://github.com/github/github-mcp-server). This may allow write operations to bypass DIFC classification or leave read operations without proper secrecy/integrity labeling.
250+
The GitHub guard does not fully cover **[N]** operation(s) from the [github-mcp-server](https://github.com/github/github-mcp-server) and/or [GitHub CLI](https://github.com/cli/cli). This may allow write operations to bypass DIFC classification or leave read operations without proper secrecy/integrity labeling.
185251

186-
- **Upstream tools scanned**: [total count from github-mcp-server]
252+
- **MCP tools scanned**: [total count from github-mcp-server]
253+
- **CLI write commands scanned**: [total count from cli/cli]
187254
- **Guard-covered write tools (tools.rs)**: [count in WRITE_OPERATIONS + READ_WRITE_OPERATIONS]
188255
- **Tools with explicit DIFC rules (tool_rules.rs)**: [count of match arms]
189256
- **New gaps found this run**: [N]
190257

191258
---
192259

193-
## Classification Gaps (tools.rs)
260+
## MCP Tool Classification Gaps (tools.rs)
194261

195-
These tools perform write or mutating operations but are missing from `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` in `guards/github-guard/rust-guard/src/tools.rs`:
262+
These MCP tools perform write or mutating operations but are missing from `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` in `guards/github-guard/rust-guard/src/tools.rs`:
196263

197264
| Tool Name | Operation Type | Suggested Classification | Notes |
198265
|-----------|---------------|--------------------------|-------|
@@ -210,9 +277,9 @@ pub const WRITE_OPERATIONS: &[&str] = &[
210277

211278
---
212279

213-
## DIFC Labeling Gaps (tool_rules.rs)
280+
## MCP Tool DIFC Labeling Gaps (tool_rules.rs)
214281

215-
These tools exist in the upstream server but have no explicit match arm in `apply_tool_labels` in `guards/github-guard/rust-guard/src/labels/tool_rules.rs`. They fall through to default label handling, which may not correctly apply repo-scoped secrecy tags or appropriate integrity levels:
282+
These MCP tools exist in the upstream server but have no explicit match arm in `apply_tool_labels` in `guards/github-guard/rust-guard/src/labels/tool_rules.rs`. They fall through to default label handling, which may not correctly apply repo-scoped secrecy tags or appropriate integrity levels:
216283

217284
| Tool Name | Data Scope | Suggested Labels | Risk |
218285
|-----------|-----------|-----------------|------|
@@ -224,9 +291,25 @@ Add a match arm to `apply_tool_labels` for each missing tool, following the patt
224291

225292
---
226293

294+
## GitHub CLI-Only Gaps
295+
296+
These write operations are reachable via the GitHub CLI but have no corresponding MCP tool and no guard entry. The guard has no visibility into these operations if an agent invokes them via `gh` or direct API calls:
297+
298+
| CLI Command | REST Endpoint | GitHub API Action | Risk |
299+
|-------------|--------------|------------------|------|
300+
| `gh issue transfer` | `POST /repos/{owner}/{repo}/issues/{issue_number}/transfer` | Transfers issue to another repo | High |
301+
302+
### Suggested remediation
303+
304+
For each CLI-only gap, either:
305+
1. Add a new entry to `WRITE_OPERATIONS` using a descriptive MCP-style name (e.g., `transfer_issue`) — this pre-emptively guards the operation when/if a matching MCP tool is added, OR
306+
2. File a request to add the equivalent tool to the GitHub MCP server so it can be properly guarded
307+
308+
---
309+
227310
## Stale Guard Entries (bonus)
228311

229-
These tools are in `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` but no longer appear in the upstream github-mcp-server. Consider removing them to keep the guard clean:
312+
These tools are in `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` but no longer appear in the upstream github-mcp-server or CLI write-operations list. Consider removing them to keep the guard clean:
230313

231314
- `stale_tool_name` — not found in upstream
232315

@@ -235,17 +318,19 @@ These tools are in `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` but no longer a
235318
## References
236319

237320
- [github-mcp-server tools](https://github.com/github/github-mcp-server/blob/main/README.md)
321+
- [GitHub CLI commands](https://github.com/cli/cli/tree/trunk/pkg/cmd)
238322
- [guard tools.rs](https://github.com/github/gh-aw-mcpg/blob/main/guards/github-guard/rust-guard/src/tools.rs)
239323
- [guard tool_rules.rs](https://github.com/github/gh-aw-mcpg/blob/main/guards/github-guard/rust-guard/src/labels/tool_rules.rs)
240324
- Run: [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
241325
```
242326

243-
## Step 7: Update Cache
327+
## Step 8: Update Cache
244328

245329
After creating the issue (or calling noop), update cache-memory:
246330
- `last_run_date`: today's ISO date
247-
- `known_gaps`: add the newly-reported tool names to the existing list
248-
- `last_upstream_tools_hash`: total count of upstream tools (as string)
331+
- `known_gaps`: add the newly-reported tool/command names to the existing list
332+
- `last_upstream_tools_hash`: total count of upstream MCP tools (as string)
333+
- `last_cli_commands_hash`: total count of CLI write commands scanned (as string)
249334

250335
Keep `known_gaps` bounded to the last 200 entries — remove the oldest if it exceeds this limit.
251336

0 commit comments

Comments
 (0)