Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
16 changes: 16 additions & 0 deletions guards/github-guard/rust-guard/src/labels/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ mod tests {
assert_eq!(tool_names::UNARCHIVE_REPOSITORY, "unarchive_repository");
assert_eq!(tool_names::RENAME_REPOSITORY, "rename_repository");
assert_eq!(tool_names::TRANSFER_REPOSITORY, "transfer_repository");
assert_eq!(tool_names::ADD_GPG_KEY, "add_gpg_key");
assert_eq!(tool_names::ADD_SSH_KEY, "add_ssh_key");
assert_eq!(tool_names::DELETE_GPG_KEY, "delete_gpg_key");
assert_eq!(tool_names::DELETE_SSH_KEY, "delete_ssh_key");
assert_eq!(tool_names::DISABLE_WORKFLOW, "disable_workflow");
assert_eq!(tool_names::ENABLE_WORKFLOW, "enable_workflow");
assert_eq!(tool_names::SYNC_FORK, "sync_fork");
assert_eq!(tool_names::CREATE_AGENT_TASK, "create_agent_task");
assert_eq!(
UI_GET_REPO_SCOPED_METHODS,
&["labels", "milestones", "branches"]
Expand Down Expand Up @@ -390,6 +398,14 @@ pub mod tool_names {
pub const LIST_DEPENDABOT_ALERTS: &str = "list_dependabot_alerts";
pub const GET_SECRET_SCANNING_ALERT: &str = "get_secret_scanning_alert";
pub const LIST_SECRET_SCANNING_ALERTS: &str = "list_secret_scanning_alerts";
pub const ADD_GPG_KEY: &str = "add_gpg_key";
pub const ADD_SSH_KEY: &str = "add_ssh_key";
pub const DELETE_GPG_KEY: &str = "delete_gpg_key";
pub const DELETE_SSH_KEY: &str = "delete_ssh_key";
pub const DISABLE_WORKFLOW: &str = "disable_workflow";
pub const ENABLE_WORKFLOW: &str = "enable_workflow";
pub const SYNC_FORK: &str = "sync_fork";
pub const CREATE_AGENT_TASK: &str = "create_agent_task";
}

/// UI metadata methods that are scoped to a specific repository.
Expand Down
2 changes: 1 addition & 1 deletion guards/github-guard/rust-guard/src/labels/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6187,7 +6187,7 @@ mod tests {
let tool_args = json!({ "owner": owner, "repo": repo });

let (secrecy, _integrity, _desc) = apply_tool_labels(
"create_agent_task",
constants::tool_names::CREATE_AGENT_TASK,
&tool_args,
repo_id,
vec![],
Expand Down
23 changes: 15 additions & 8 deletions guards/github-guard/rust-guard/src/labels/tool_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,7 @@ pub fn apply_tool_labels(
}

// === Repo-scoped workflow/fork writes ===
"disable_workflow" | "enable_workflow" | "sync_fork" => {
tool_names::DISABLE_WORKFLOW | tool_names::ENABLE_WORKFLOW | tool_names::SYNC_FORK => {
secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
integrity = writer_integrity(repo_id, ctx);
}
Expand Down Expand Up @@ -953,7 +953,7 @@ pub fn apply_tool_labels(
}

// === Copilot coding-agent task (blocked: unsupported agent operation) ===
"create_agent_task" => {
tool_names::CREATE_AGENT_TASK => {
// Creates a Copilot coding-agent job that modifies repo branches and opens a PR.
// Blocked via is_blocked_tool(); secrecy applied so the resource is correctly
// classified before the integrity override in label_resource.
Expand All @@ -979,7 +979,10 @@ pub fn apply_tool_labels(
// Managing auth/signing keys is a high-risk account-level write operation.
// S = private:user (user-account-scoped sensitive data)
// I = writer(user) (requires authenticated account write access)
"add_gpg_key" | "add_ssh_key" | "delete_gpg_key" | "delete_ssh_key" => {
tool_names::ADD_GPG_KEY
| tool_names::ADD_SSH_KEY
| tool_names::DELETE_GPG_KEY
| tool_names::DELETE_SSH_KEY => {
secrecy = private_user_label();
baseline_scope = Cow::Borrowed(scope_names::USER);
integrity = writer_integrity(scope_names::USER, ctx);
Expand Down Expand Up @@ -2611,10 +2614,10 @@ mod tests {
let expected_integrity = writer_integrity(scope_names::USER, &ctx);

for tool in &[
"add_gpg_key",
"add_ssh_key",
"delete_gpg_key",
"delete_ssh_key",
tool_names::ADD_GPG_KEY,
tool_names::ADD_SSH_KEY,
tool_names::DELETE_GPG_KEY,
tool_names::DELETE_SSH_KEY,
] {
let (secrecy, integrity, _) =
super::apply_tool_labels(tool, &args, "", vec![], vec![], String::new(), &ctx);
Expand Down Expand Up @@ -2699,7 +2702,11 @@ mod tests {
let args = serde_json::json!({ "owner": "github", "repo": "copilot" });
let repo_id = "github/copilot";

for op in &["disable_workflow", "enable_workflow", "sync_fork"] {
for op in &[
tool_names::DISABLE_WORKFLOW,
tool_names::ENABLE_WORKFLOW,
tool_names::SYNC_FORK,
] {
let (secrecy, integrity, _desc) =
super::apply_tool_labels(op, &args, repo_id, vec![], vec![], String::new(), &ctx);
assert_eq!(
Expand Down
40 changes: 20 additions & 20 deletions guards/github-guard/rust-guard/src/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ pub const WRITE_OPERATIONS: &[&str] = &[
pub const CLI_WRITE_OPERATIONS: &[&str] = &[
// Keep sorted for binary_search correctness.
"add_deploy_key", // gh repo deploy-key add — POST /repos/.../keys
"add_gpg_key", // gh gpg-key add — adds a user GPG signing key
"add_ssh_key", // gh ssh-key add — adds a user SSH auth/signing key
tool_names::ADD_GPG_KEY, // gh gpg-key add — adds a user GPG signing key
tool_names::ADD_SSH_KEY, // gh ssh-key add — adds a user SSH auth/signing key
"archive_project_item", // gh project item-archive — archives a Projects v2 item
tool_names::ARCHIVE_REPOSITORY, // gh repo archive — blocked: repo settings change unsupported
tool_names::CANCEL_WORKFLOW_RUN, // gh run cancel — cancels an in-progress workflow run
Expand All @@ -66,7 +66,7 @@ pub const CLI_WRITE_OPERATIONS: &[&str] = &[
"delete_codespace", // gh codespace delete — DELETE /user/codespaces/{name} or /orgs/{org}/members/{user}/codespaces/{name}
"delete_deploy_key", // gh repo deploy-key delete — DELETE /repos/.../keys/{id}
"delete_gist", // gh gist delete
"delete_gpg_key", // gh gpg-key delete — removes a user GPG signing key
tool_names::DELETE_GPG_KEY, // gh gpg-key delete — removes a user GPG signing key
"delete_issue", // gh issue delete — deletes an issue via GraphQL deleteIssue
"delete_issue_comment", // DELETE /repos/.../issues/comments/{id}
"delete_project", // gh project delete — deletes a Projects v2 project
Expand All @@ -75,14 +75,14 @@ pub const CLI_WRITE_OPERATIONS: &[&str] = &[
"delete_release_asset", // gh release delete-asset — deletes a release asset
"delete_repository_autolink", // gh repo autolink delete — DELETE /repos/.../autolinks/{id}
tool_names::DELETE_SECRET, // gh secret delete — deletes org/repo/env/user codespaces secrets
"delete_ssh_key", // gh ssh-key delete — removes a user SSH auth/signing key
tool_names::DELETE_SSH_KEY, // gh ssh-key delete — removes a user SSH auth/signing key
tool_names::DELETE_VARIABLE, // gh variable delete — deletes org/repo/environment Actions variables
"delete_workflow_run", // gh run delete — deletes a workflow run record
"disable_workflow", // gh workflow disable
tool_names::DISABLE_WORKFLOW, // gh workflow disable
tool_names::EDIT_DISCUSSION, // gh discussion edit — edits title/body/labels of a discussion
"edit_release", // PATCH /repos/.../releases/{id}
"edit_repository", // gh repo edit — can change visibility, security settings
"enable_workflow", // gh workflow enable
tool_names::ENABLE_WORKFLOW, // gh workflow enable
tool_names::FORCE_CANCEL_WORKFLOW_RUN, // gh run cancel --force — force-cancels a workflow run
"link_project", // gh project link — links a Projects v2 board to a repository or team
"lock_issue", // gh issue lock
Expand All @@ -102,7 +102,7 @@ pub const CLI_WRITE_OPERATIONS: &[&str] = &[
tool_names::SET_SECRET, // gh secret set
tool_names::SET_VARIABLE, // gh variable set
"stop_codespace", // gh codespace stop — POST /user|/orgs/.../codespaces/.../stop
"sync_fork", // gh repo sync
tool_names::SYNC_FORK, // gh repo sync
"transfer_issue", // gh issue transfer
"unarchive_project_item", // gh project item-archive --undo — unarchives a Projects v2 item
tool_names::UNARCHIVE_REPOSITORY, // gh repo unarchive — blocked: symmetric to archive_repository
Expand Down Expand Up @@ -176,7 +176,7 @@ pub const READ_WRITE_OPERATIONS: &[&str] = &[
/// Synthetic read-write operations reachable through GitHub CLI but not current upstream MCP tools.
pub const CLI_READ_WRITE_OPERATIONS: &[&str] = &[
// Keep sorted for binary_search correctness.
"create_agent_task", // gh agent-task create — creates a Copilot coding-agent job; blocked as unsupported
tool_names::CREATE_AGENT_TASK, // gh agent-task create — creates a Copilot coding-agent job; blocked as unsupported
"update_project_draft_issue", // gh project item-edit --title/--body — GraphQL updateProjectV2DraftIssue
];

Expand Down Expand Up @@ -232,7 +232,7 @@ pub(crate) fn is_unlock_operation(tool_name: &str) -> bool {
/// Entries here should also be classified by `is_write_operation` or `is_read_write_operation`.
pub const BLOCKED_TOOLS: &[&str] = &[
tool_names::ARCHIVE_REPOSITORY, // repo settings change; unsupported
"create_agent_task", // unsupported agent-task creation
tool_names::CREATE_AGENT_TASK, // unsupported agent-task creation
tool_names::RENAME_REPOSITORY, // breaks clone URLs and integrations
tool_names::TRANSFER_REPOSITORY, // irreversible ownership transfer
tool_names::UNARCHIVE_REPOSITORY, // symmetric to archive_repository
Expand Down Expand Up @@ -464,10 +464,10 @@ mod tests {
"revert_pull_request",
"add_deploy_key",
"delete_deploy_key",
"add_gpg_key",
"add_ssh_key",
"delete_gpg_key",
"delete_ssh_key",
tool_names::ADD_GPG_KEY,
tool_names::ADD_SSH_KEY,
tool_names::DELETE_GPG_KEY,
tool_names::DELETE_SSH_KEY,
"delete_release_asset",
"delete_workflow_run",
"stop_codespace",
Expand Down Expand Up @@ -499,15 +499,15 @@ mod tests {
#[test]
fn test_create_agent_task_is_read_write_and_blocked() {
assert!(
is_read_write_operation("create_agent_task"),
is_read_write_operation(tool_names::CREATE_AGENT_TASK),
"create_agent_task must be classified as a read-write operation"
);
assert!(
is_blocked_tool("create_agent_task"),
is_blocked_tool(tool_names::CREATE_AGENT_TASK),
"create_agent_task must be unconditionally blocked (unsupported agent operation)"
);
assert!(
!is_write_operation("create_agent_task"),
!is_write_operation(tool_names::CREATE_AGENT_TASK),
"create_agent_task should not be in WRITE_OPERATIONS (it is in READ_WRITE_OPERATIONS)"
);
}
Expand Down Expand Up @@ -817,8 +817,8 @@ mod tests {
fn test_cli_only_operations_are_not_in_upstream_mcp_buckets() {
for op in &[
"add_deploy_key",
"add_gpg_key",
"add_ssh_key",
tool_names::ADD_GPG_KEY,
tool_names::ADD_SSH_KEY,
"archive_project_item",
"close_issue",
"close_pull_request",
Expand All @@ -831,14 +831,14 @@ mod tests {
"create_repository_autolink",
"delete_codespace",
"delete_deploy_key",
"delete_gpg_key",
tool_names::DELETE_GPG_KEY,
"delete_issue",
"delete_issue_comment",
"delete_project_field",
"delete_release",
"delete_release_asset",
"delete_repository_autolink",
"delete_ssh_key",
tool_names::DELETE_SSH_KEY,
"delete_workflow_run",
tool_names::EDIT_DISCUSSION,
"edit_release",
Expand Down
Loading