Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions guards/github-guard/rust-guard/src/labels/tool_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ pub fn apply_tool_labels(
secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
}

// === Modifying Repository Operations (blocked: unsupported gh repo operations) ===
"archive_repository" | "unarchive_repository" | "rename_repository" => {
// All modifying `gh repo` operations (archive, unarchive, rename) are treated as
// unsupported for automated agents — the same policy as transfer_repository.
// Blocking is enforced in label_resource via is_blocked_tool(); this arm applies
// repo-visibility secrecy so the resource is correctly classified before the
// integrity override happens in label_resource.
secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
}
Comment on lines +174 to +182
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing apply_tool_labels unit tests for transfer_repository in labels/mod.rs, but none covering this new match arm. Add tests that call apply_tool_labels("archive_repository"|"unarchive_repository"|"rename_repository", …) to verify the secrecy behavior matches transfer_repository (and that the call completes deterministically in cfg(test) without relying on the backend host).

Copilot uses AI. Check for mistakes.

// Search issues: extract repo scope from query or tool_args when available
"search_issues" => {
let (s_owner, s_repo, s_repo_id) = resolve_search_scope(tool_args, &owner, &repo);
Expand Down
45 changes: 39 additions & 6 deletions guards/github-guard/rust-guard/src/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ pub const WRITE_OPERATIONS: &[&str] = &[
// Dynamically enables additional toolsets, expanding the agent's capability set
"enable_toolset",
// Pre-emptive entries for anticipated future MCP tools (no equivalent tool today)
"archive_repository", // gh repo archive
"transfer_issue", // gh issue transfer
"transfer_repository", // gh repo transfer — blocked: repo ownership transfer is irreversible
"pin_issue", // gh issue pin
"unpin_issue", // gh issue unpin
"archive_repository", // gh repo archive — blocked: repo settings change unsupported
"unarchive_repository", // gh repo unarchive — blocked: symmetric to archive_repository
"rename_repository", // gh repo rename — blocked: breaks clone URLs and integrations
"transfer_issue", // gh issue transfer
"transfer_repository", // gh repo transfer — blocked: repo ownership transfer is irreversible
"pin_issue", // gh issue pin
"unpin_issue", // gh issue unpin
"enable_workflow", // gh workflow enable
"disable_workflow", // gh workflow disable
"set_secret", // gh secret set
Expand Down Expand Up @@ -116,8 +118,17 @@ pub fn is_unlock_operation(tool_name: &str) -> bool {
/// Current entries:
/// - `transfer_repository`: repository ownership transfer is irreversible and
/// must never be performed by an automated agent.
/// - `archive_repository`: archives a repository, restricting contributions; unsupported as an
/// agent operation.
/// - `unarchive_repository`: re-enables contributions to a previously archived repository;
/// symmetric to `archive_repository` and equally unsupported.
/// - `rename_repository`: renames a repository, breaking all clone URLs, webhooks, and external
/// references; unsupported as an agent operation.
pub fn is_blocked_tool(tool_name: &str) -> bool {
matches!(tool_name, "transfer_repository")
matches!(
tool_name,
"transfer_repository" | "archive_repository" | "unarchive_repository" | "rename_repository"
)
Comment on lines 127 to +131
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding unit tests at the label_resource level (in lib.rs tests) for the newly blocked tools (archive/unarchive/rename), similar to the existing transfer_repository_integrity_is_blocked_after_ensure_baseline test. The current tests here validate is_blocked_tool(), but they don’t exercise the end-to-end blocked-integrity override that actually enforces the policy in runtime.

Copilot uses AI. Check for mistakes.
}

#[cfg(test)]
Expand All @@ -132,6 +143,17 @@ mod tests {
);
}

#[test]
fn test_is_blocked_tool_repo_modifying_operations() {
for op in &["archive_repository", "unarchive_repository", "rename_repository"] {
assert!(
is_blocked_tool(op),
"{} must be unconditionally blocked (modifying gh repo operation)",
op
);
}
}

#[test]
fn test_is_blocked_tool_other_write_ops_not_blocked() {
// Regular write operations should not be blocked
Expand All @@ -152,6 +174,17 @@ mod tests {
);
}

#[test]
fn test_repo_modifying_operations_are_write_operations() {
for op in &["archive_repository", "unarchive_repository", "rename_repository"] {
assert!(
is_write_operation(op),
"{} must be classified as a write operation",
op
);
}
}

#[test]
fn test_pin_unpin_issue_are_write_operations() {
assert!(
Expand Down
Loading