Skip to content

[guard-coverage] Guard coverage gap: 3 CLI write operations missing pre-emptive WRITE_OPERATIONS entries #3596

@github-actions

Description

@github-actions

Summary

The GitHub guard covers all 80 tools currently in github-mcp-server with both write-classification and DIFC-labeling rules. However, 3 GitHub CLI write operations have no pre-emptive guard entry — meaning if a corresponding MCP tool is added to the GitHub MCP server, or if an agent invokes these operations via gh CLI, the guard would not classify them.

  • MCP tools scanned: 80 (from pkg/github/__toolsnaps__/)
  • CLI write commands scanned: ~63
  • Guard-covered write tools (tools.rs): 50 (42 WRITE_OPERATIONS + 8 READ_WRITE_OPERATIONS)
  • Tools with explicit DIFC rules (tool_rules.rs): 80 (complete coverage)
  • MCP classification gaps: 0
  • MCP DIFC labeling gaps: 0
  • New CLI-only gaps found this run: 3

GitHub CLI-Only Gaps

These write operations are reachable via the GitHub CLI but have no corresponding MCP tool today and no pre-emptive guard entry. If GitHub MCP server adds a matching tool, the guard would not intercept it.

CLI Command REST/GraphQL Endpoint GitHub API Action Suggested Guard Name Risk
gh repo edit PATCH /repos/{owner}/{repo} Modifies repo settings: visibility (private↔public), default branch, security features, merge options edit_repository High — can expose private code by changing visibility
gh pr revert GraphQL revertPullRequest mutation Creates a new branch + PR that reverts a merged PR's changes revert_pull_request Medium — creates new branch and PR
gh repo deploy-key add POST /repos/{owner}/{repo}/keys Adds an SSH deploy key with optional write access to the repository add_deploy_key High — grants persistent SSH write access

Detail: gh repo editedit_repository

gh repo edit uses PATCH /repos/{owner}/{repo} and can change:

  • visibility: public/private (can accidentally expose private code)
  • default_branch: changes the default merge target
  • delete_branch_on_merge, allow_auto_merge, security settings

This is a HIGH-risk operation because changing visibility from private to public would expose all repository contents. The guard already has pre-emptive entries for other irreversible repo operations (archive_repository, rename_repository, transfer_repository) but edit_repository is missing.

Detail: gh pr revertrevert_pull_request

gh pr revert uses the GraphQL revertPullRequest mutation. It creates a new branch (from the revert commit) and opens a new PR. This is semantically similar to create_pull_request + create_branch, both of which are already in WRITE_OPERATIONS, but the combined operation is not pre-emptively covered.

Detail: gh repo deploy-key addadd_deploy_key

gh repo deploy-key add uses POST /repos/{owner}/{repo}/keys to register an SSH key as a deploy key. With --write flag, the key gets push access to the repository. This is a HIGH-risk persistent access grant. Note: delete_deploy_key would also need coverage since delete_* is NOT an automatic pattern in the guard (only lock_* and unlock_* are pattern-matched).


Suggested Fix for tools.rs

Add pre-emptive entries to WRITE_OPERATIONS in guards/github-guard/rust-guard/src/tools.rs:

pub const WRITE_OPERATIONS: &[&str] = &[
    // ... existing entries ...

    // Pre-emptive: gh repo edit (PATCH /repos/{owner}/{repo}) — can change visibility, security settings
    "edit_repository",
    // Pre-emptive: gh pr revert (GraphQL revertPullRequest) — creates revert branch + PR
    "revert_pull_request",
    // Pre-emptive: gh repo deploy-key add/delete — SSH key with optional write access
    "add_deploy_key",
    "delete_deploy_key",
];

Suggested Fix for tool_rules.rs

Add match arms to apply_tool_labels in guards/github-guard/rust-guard/src/labels/tool_rules.rs:

// === Repository settings edit (can change visibility) ===
"edit_repository" => {
    // Can change repo visibility, security settings, default branch.
    // S = S(repo); I = writer (requires admin access)
    secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
    integrity = writer_integrity(repo_id, ctx);
}

// === PR revert (creates revert branch + PR) ===
"revert_pull_request" => {
    // Creates a new branch + PR reverting a merged PR.
    // S = S(repo); I = writer
    secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
    integrity = writer_integrity(repo_id, ctx);
}

// === Deploy key management (SSH key with optional write access) ===
"add_deploy_key" | "delete_deploy_key" => {
    // Manages SSH deploy keys — `add_deploy_key` may grant persistent write access.
    // S = private:owner/repo (deploy key secrets should be restricted)
    // I = writer (requires admin access)
    secrecy = policy_private_scope_label(&owner, &repo, repo_id, ctx);
    integrity = writer_integrity(repo_id, ctx);
}

Coverage Architecture Note

The guard's is_delete_operation() and is_merge_operation() functions are used only to select integrity levels within already-classified write operations — NOT to auto-classify new tools. Only lock_* and unlock_* are auto-classified by pattern. This means any new delete_* or merge_* tool added to the MCP server will NOT be automatically guarded unless explicitly added to WRITE_OPERATIONS.


References

Generated by GitHub Guard Coverage Checker (MCP + CLI) · ● 1.8M ·

  • expires on Apr 25, 2026, 8:09 PM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions