From a7eba3e14c56557422f6c69c0c344fb5e31b1a23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:25:49 +0000 Subject: [PATCH 1/3] Initial plan From 701960528385cc68738b830366ba8acc64a52386 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:32:57 +0000 Subject: [PATCH 2/3] fix(rust-guard): deduplicate granular write rules and reuse file path segments Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/2477f609-0fec-4df5-a908-631451c0f984 Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- .../github-guard/rust-guard/src/labels/mod.rs | 31 +++++++++++++ .../rust-guard/src/labels/tool_rules.rs | 44 ++++++------------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/guards/github-guard/rust-guard/src/labels/mod.rs b/guards/github-guard/rust-guard/src/labels/mod.rs index 6186b12f..3eebb0fc 100644 --- a/guards/github-guard/rust-guard/src/labels/mod.rs +++ b/guards/github-guard/rust-guard/src/labels/mod.rs @@ -4797,6 +4797,37 @@ mod tests { } } + #[test] + fn test_apply_tool_labels_granular_pr_update_tools_writer_integrity() { + let ctx = default_ctx(); + let repo_id = "github/copilot"; + let tool_args = json!({ + "owner": "github", + "repo": "copilot", + "pullNumber": 42 + }); + + for tool in &[ + "update_pull_request_body", + "update_pull_request_draft_state", + "update_pull_request_state", + "update_pull_request_title", + ] { + let (secrecy, integrity, _desc) = apply_tool_labels( + tool, + &tool_args, + repo_id, + vec![], + vec![], + String::new(), + &ctx, + ); + + assert_eq!(secrecy, vec![] as Vec, "{tool} secrecy mismatch"); + assert_eq!(integrity, writer_integrity(repo_id, &ctx), "{tool} should have writer integrity"); + } + } + #[test] fn test_apply_tool_labels_granular_pr_review_tools_writer_integrity() { let ctx = default_ctx(); diff --git a/guards/github-guard/rust-guard/src/labels/tool_rules.rs b/guards/github-guard/rust-guard/src/labels/tool_rules.rs index b1d71a5e..6ba98578 100644 --- a/guards/github-guard/rust-guard/src/labels/tool_rules.rs +++ b/guards/github-guard/rust-guard/src/labels/tool_rules.rs @@ -533,49 +533,30 @@ pub fn apply_tool_labels( integrity = writer_integrity(repo_id, ctx); } - // === Granular issue update tools (repo-scoped writes) === + // === Granular repo-scoped write operations === + // Covers granular issue PATCH tools, sub-issue management, granular PR PATCH tools, + // and PR review tools. All follow: S = S(repo), I = writer. "update_issue_assignees" | "update_issue_body" | "update_issue_labels" | "update_issue_milestone" | "update_issue_state" | "update_issue_title" - | "update_issue_type" => { - // Granular PATCH tools that modify individual issue fields. - // S = S(repo); I = writer - secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx); - integrity = writer_integrity(repo_id, ctx); - } - - // === Sub-issue management tools (repo-scoped writes) === - "add_sub_issue" | "remove_sub_issue" | "reprioritize_sub_issue" => { - // Sub-issue link creation, removal, and reordering. - // S = S(repo); I = writer - secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx); - integrity = writer_integrity(repo_id, ctx); - } - - // === Granular PR update tools (repo-scoped read-write) === - "update_pull_request_body" + | "update_issue_type" + | "add_sub_issue" + | "remove_sub_issue" + | "reprioritize_sub_issue" + | "update_pull_request_body" | "update_pull_request_draft_state" | "update_pull_request_state" - | "update_pull_request_title" => { - // Granular PATCH tools that modify individual PR fields. - // S = S(repo); I = writer - secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx); - integrity = writer_integrity(repo_id, ctx); - } - - // === PR review tools (repo-scoped writes) === - "add_pull_request_review_comment" + | "update_pull_request_title" + | "add_pull_request_review_comment" | "create_pull_request_review" | "delete_pending_pull_request_review" | "request_pull_request_reviewers" | "resolve_review_thread" | "submit_pending_pull_request_review" | "unresolve_review_thread" => { - // PR review creation, commenting, submission, and thread resolution. - // S = S(repo); I = writer secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx); integrity = writer_integrity(repo_id, ctx); } @@ -746,16 +727,17 @@ fn check_file_secrecy( ctx: &PolicyContext, ) -> Vec { let path_lower = path.to_lowercase(); + let segments: Vec<&str> = path_lower.split('/').collect(); // Check for sensitive file extensions/names for pattern in SENSITIVE_FILE_PATTERNS { - if path_lower.ends_with(pattern) || path_lower.split('/').any(|seg| seg.starts_with(*pattern)) { + if path_lower.ends_with(pattern) || segments.iter().any(|seg| seg.starts_with(*pattern)) { return policy_private_scope_label(owner, repo, repo_id, ctx); } } // Get filename - let filename = path_lower.rsplit('/').next().unwrap_or(&path_lower); + let filename = segments.last().copied().unwrap_or(path_lower.as_str()); // Check for sensitive keywords in filename for keyword in SENSITIVE_FILE_KEYWORDS { From fd46c0fb367a5450a3c6080df53d453593615081 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Thu, 16 Apr 2026 12:14:37 -0700 Subject: [PATCH 3/3] Use explicit format args in assert_eq! messages for clarity Address review feedback: replace implicit named captures ({tool}) with explicit format arguments ({}, tool) in assert_eq! failure messages for granular PR update and PR review tool tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- guards/github-guard/rust-guard/src/labels/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/guards/github-guard/rust-guard/src/labels/mod.rs b/guards/github-guard/rust-guard/src/labels/mod.rs index 3eebb0fc..804dcc13 100644 --- a/guards/github-guard/rust-guard/src/labels/mod.rs +++ b/guards/github-guard/rust-guard/src/labels/mod.rs @@ -4823,8 +4823,8 @@ mod tests { &ctx, ); - assert_eq!(secrecy, vec![] as Vec, "{tool} secrecy mismatch"); - assert_eq!(integrity, writer_integrity(repo_id, &ctx), "{tool} should have writer integrity"); + assert_eq!(secrecy, vec![] as Vec, "{} secrecy mismatch", tool); + assert_eq!(integrity, writer_integrity(repo_id, &ctx), "{} should have writer integrity", tool); } } @@ -4857,8 +4857,8 @@ mod tests { &ctx, ); - assert_eq!(secrecy, vec![] as Vec, "{tool} secrecy mismatch"); - assert_eq!(integrity, writer_integrity(repo_id, &ctx), "{tool} should have writer integrity"); + assert_eq!(secrecy, vec![] as Vec, "{} secrecy mismatch", tool); + assert_eq!(integrity, writer_integrity(repo_id, &ctx), "{} should have writer integrity", tool); } }