From 1537d04da4fa72b9c0b429b9a61579b859f563b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 23:08:18 +0000 Subject: [PATCH 1/3] Initial plan From a8b57f23a340b108073d0851b1505e7cc4acdb7a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 23:12:56 +0000 Subject: [PATCH 2/3] fix(rust-guard): sort BLOCKED_TOOLS, use binary_search, promote login to cfg(test) - Sort BLOCKED_TOOLS alphabetically to match WRITE_OPERATIONS/READ_WRITE_OPERATIONS convention - Switch is_blocked_tool to binary_search (O(log n)) instead of .contains() (O(n)) - Trim verbose redundant doc comment on is_blocked_tool to a concise one-liner - Add blocked_tools_are_sorted test mirroring write_operations_are_sorted - Change CollaboratorPermission.login from cfg_attr(not(test), allow(dead_code)) to #[cfg(test)], removing the field and its two allocations from production/WASM builds --- .../rust-guard/src/labels/backend.rs | 9 +++- guards/github-guard/rust-guard/src/tools.rs | 42 ++++++++----------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/guards/github-guard/rust-guard/src/labels/backend.rs b/guards/github-guard/rust-guard/src/labels/backend.rs index 2303834b8..f659f7700 100644 --- a/guards/github-guard/rust-guard/src/labels/backend.rs +++ b/guards/github-guard/rust-guard/src/labels/backend.rs @@ -130,7 +130,7 @@ pub struct IssueAuthorInfo { #[derive(Debug, Clone)] pub struct CollaboratorPermission { pub permission: Option, - #[cfg_attr(not(test), allow(dead_code))] + #[cfg(test)] pub login: Option, } @@ -494,6 +494,7 @@ pub fn get_collaborator_permission_with_callback( )); return cached.map(|permission| CollaboratorPermission { permission: Some(permission), + #[cfg(test)] login: Some(username.to_string()), }); } @@ -572,7 +573,11 @@ pub fn get_collaborator_permission_with_callback( set_cached_collaborator_permission(&cache_key, permission.clone()); - Some(CollaboratorPermission { permission, login }) + Some(CollaboratorPermission { + permission, + #[cfg(test)] + login, + }) } pub fn get_collaborator_permission( diff --git a/guards/github-guard/rust-guard/src/tools.rs b/guards/github-guard/rust-guard/src/tools.rs index d684fa977..92e90c6bc 100644 --- a/guards/github-guard/rust-guard/src/tools.rs +++ b/guards/github-guard/rust-guard/src/tools.rs @@ -137,36 +137,19 @@ pub fn is_unlock_operation(tool_name: &str) -> bool { /// Tools that are unconditionally blocked regardless of agent integrity. /// -/// These operations are too dangerous or unsupported to ever permit via an agent. -/// Entries here should also appear in `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS` -/// so the tool is still subject to all normal write-path checks before being denied. +/// Keep sorted for `binary_search` correctness (see `blocked_tools_are_sorted` test). +/// Entries here should also appear in `WRITE_OPERATIONS` or `READ_WRITE_OPERATIONS`. pub const BLOCKED_TOOLS: &[&str] = &[ - "transfer_repository", // irreversible ownership transfer "archive_repository", // repo settings change; unsupported - "unarchive_repository", // symmetric to archive_repository - "rename_repository", // breaks clone URLs and integrations "create_agent_task", // unsupported agent-task creation + "rename_repository", // breaks clone URLs and integrations + "transfer_repository", // irreversible ownership transfer + "unarchive_repository", // symmetric to archive_repository ]; -/// Check if a tool is unconditionally blocked (always denied regardless of agent integrity). -/// -/// Blocked tools are listed here when the operation is considered too dangerous -/// to ever permit via an agent, even if the agent would otherwise satisfy the -/// integrity requirements for a normal write operation. -/// -/// 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. -/// - `create_agent_task`: creates a Copilot coding-agent job that opens a branch and PR; -/// unsupported as a directly invocable agent operation. +/// Returns `true` if `tool_name` is in [`BLOCKED_TOOLS`] — denied regardless of agent integrity. pub fn is_blocked_tool(tool_name: &str) -> bool { - BLOCKED_TOOLS.contains(&tool_name) + BLOCKED_TOOLS.binary_search(&tool_name).is_ok() } #[cfg(test)] @@ -205,6 +188,17 @@ mod tests { ); } + #[test] + fn blocked_tools_are_sorted() { + let mut sorted = BLOCKED_TOOLS.to_vec(); + sorted.sort_unstable(); + assert_eq!( + BLOCKED_TOOLS, + sorted.as_slice(), + "BLOCKED_TOOLS must be kept in sorted order for binary_search correctness" + ); + } + #[test] fn test_is_blocked_tool_transfer_repository() { assert!( From aded48d0d28616aaed5dc055b20210c996866e52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 23:27:08 +0000 Subject: [PATCH 3/3] rust-guard: gate login extraction and log reference behind #[cfg(test)] --- guards/github-guard/rust-guard/src/labels/backend.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/guards/github-guard/rust-guard/src/labels/backend.rs b/guards/github-guard/rust-guard/src/labels/backend.rs index f659f7700..4d6169d4a 100644 --- a/guards/github-guard/rust-guard/src/labels/backend.rs +++ b/guards/github-guard/rust-guard/src/labels/backend.rs @@ -560,12 +560,19 @@ pub fn get_collaborator_permission_with_callback( .and_then(|v| v.as_str()) .map(String::from); + #[cfg(test)] let login = data .get("user") .and_then(|u| u.get("login")) .and_then(|v| v.as_str()) .map(String::from); + #[cfg(not(test))] + crate::log_info(&format!( + "get_collaborator_permission: {}/{} user {} → permission={:?}", + owner, repo, username, permission + )); + #[cfg(test)] crate::log_info(&format!( "get_collaborator_permission: {}/{} user {} → permission={:?} login={:?}", owner, repo, username, permission, login