From 930f48cd6a4617facb58aa77f583bd520fa79d6e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:49:21 +0000 Subject: [PATCH 1/2] refactor(rust-guard): extract is_any_trusted_actor helper and collapse URL fallback loop - Add is_any_trusted_actor(username, ctx) helper combining the three constituent trust predicates (first-party bot, configured bot, trusted user); replaces copy-pasted triple-OR at three call sites in helpers.rs and tool_rules.rs - Replace three structurally identical if-let blocks in extract_repo_from_item with a for-field loop over ["repository_url", "html_url", "url"], matching the idiom already used in extract_number_from_url All 317 Rust guard tests pass. Closes #4252 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../rust-guard/src/labels/helpers.rs | 42 +++++++------------ .../rust-guard/src/labels/tool_rules.rs | 9 ++-- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/guards/github-guard/rust-guard/src/labels/helpers.rs b/guards/github-guard/rust-guard/src/labels/helpers.rs index ea190ba7..31702c8e 100644 --- a/guards/github-guard/rust-guard/src/labels/helpers.rs +++ b/guards/github-guard/rust-guard/src/labels/helpers.rs @@ -973,23 +973,12 @@ pub fn extract_repo_from_item(item: &Value) -> String { { return name.to_string(); } - // repository_url parsing for search endpoints - if let Some(url) = item.get("repository_url").and_then(|v| v.as_str()) { - if let Some(repo_id) = extract_repo_from_github_url(url) { - return repo_id; - } - } - // html_url parsing as last resort - extract owner/repo from URLs like: - // https://github.com/owner/repo/pull/123 or https://github.com/owner/repo/issues/456 - if let Some(url) = item.get("html_url").and_then(|v| v.as_str()) { - if let Some(repo_id) = extract_repo_from_github_url(url) { - return repo_id; - } - } - // Generic URL field fallback - if let Some(url) = item.get("url").and_then(|v| v.as_str()) { - if let Some(repo_id) = extract_repo_from_github_url(url) { - return repo_id; + // URL field fallback (repository_url for search results, html_url / url as generic fallbacks) + for field in &["repository_url", "html_url", "url"] { + if let Some(url) = item.get(*field).and_then(|v| v.as_str()) { + if let Some(repo_id) = extract_repo_from_github_url(url) { + return repo_id; + } } } String::new() @@ -1278,11 +1267,7 @@ pub fn has_author_association(item: &Value) -> bool { /// Users in the trusted_users list are also elevated to approved integrity. pub fn author_association_floor(item: &Value, scope: &str, ctx: &PolicyContext) -> Vec { let author_login = extract_author_login(item); - if !author_login.is_empty() - && (is_trusted_first_party_bot(author_login) - || is_configured_trusted_bot(author_login, ctx) - || is_trusted_user(author_login, ctx)) - { + if !author_login.is_empty() && is_any_trusted_actor(author_login, ctx) { return writer_integrity(scope, ctx); } @@ -1476,10 +1461,7 @@ pub fn pr_integrity( ); // Elevate trusted bots and trusted users let enriched_floor = if let Some(ref login) = facts.author_login { - if is_trusted_first_party_bot(login) - || is_configured_trusted_bot(login, ctx) - || is_trusted_user(login, ctx) - { + if is_any_trusted_actor(login, ctx) { max_integrity( repo_full_name, enriched_floor, @@ -1772,6 +1754,14 @@ pub fn is_trusted_user(username: &str, ctx: &PolicyContext) -> bool { username_in_list(username, &ctx.trusted_users) } +/// Returns `true` if `username` belongs to any trusted actor tier: +/// first-party bots, gateway-configured bots, or trusted users. +pub(crate) fn is_any_trusted_actor(username: &str, ctx: &PolicyContext) -> bool { + is_trusted_first_party_bot(username) + || is_configured_trusted_bot(username, ctx) + || is_trusted_user(username, ctx) +} + #[cfg(test)] mod tests { 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 84838784..084fd646 100644 --- a/guards/github-guard/rust-guard/src/labels/tool_rules.rs +++ b/guards/github-guard/rust-guard/src/labels/tool_rules.rs @@ -10,8 +10,8 @@ use super::helpers::{ author_association_floor_from_str, elevate_via_collaborator_permission, ensure_integrity_baseline, extract_number_as_string, extract_repo_info, extract_repo_info_from_search_query, - format_repo_id, is_configured_trusted_bot, is_default_branch_commit_context, - is_default_branch_ref, is_trusted_first_party_bot, is_trusted_user, max_integrity, + format_repo_id, is_any_trusted_actor, is_default_branch_commit_context, + is_default_branch_ref, max_integrity, merged_integrity, policy_private_scope_label, private_user_label, project_github_label, reader_integrity, writer_integrity, PolicyContext, }; @@ -95,10 +95,7 @@ fn resolve_author_integrity( let mut floor = author_association_floor_from_str(repo_id, author_association, ctx); if let Some(login) = author_login { - if is_trusted_first_party_bot(login) - || is_configured_trusted_bot(login, ctx) - || is_trusted_user(login, ctx) - { + if is_any_trusted_actor(login, ctx) { floor = max_integrity(repo_id, floor, writer_integrity(repo_id, ctx), ctx); } let resource_id = format!("{}/{}#{}", owner, repo, resource_num); From 761545cbb2738b28d5025c95fdadeddda3f0693d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:15:55 +0000 Subject: [PATCH 2/2] test(rust-guard): cover trusted actor helper tiers and update URL fallback docs Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/ecd07254-bbdd-4935-a0c0-b4d4b0114e76 Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- .../rust-guard/src/labels/helpers.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/guards/github-guard/rust-guard/src/labels/helpers.rs b/guards/github-guard/rust-guard/src/labels/helpers.rs index 31702c8e..ae9753b0 100644 --- a/guards/github-guard/rust-guard/src/labels/helpers.rs +++ b/guards/github-guard/rust-guard/src/labels/helpers.rs @@ -940,7 +940,8 @@ pub(crate) fn extract_repo_from_github_url(url: &str) -> Option { /// Extract repository full name from a response item /// Tries multiple fields in order: full_name, repository.full_name, -/// base.repo.full_name, head.repo.full_name, html_url parsing +/// base.repo.full_name, head.repo.full_name, then URL parsing from +/// repository_url, html_url, and url. /// Returns empty string if no repo info found pub fn extract_repo_from_item(item: &Value) -> String { // Direct full_name (repositories) @@ -975,7 +976,7 @@ pub fn extract_repo_from_item(item: &Value) -> String { } // URL field fallback (repository_url for search results, html_url / url as generic fallbacks) for field in &["repository_url", "html_url", "url"] { - if let Some(url) = item.get(*field).and_then(|v| v.as_str()) { + if let Some(url) = item.get(field).and_then(|v| v.as_str()) { if let Some(repo_id) = extract_repo_from_github_url(url) { return repo_id; } @@ -1771,6 +1772,20 @@ mod tests { PolicyContext::default() } + #[test] + fn test_is_any_trusted_actor_tiers_and_negative() { + let ctx = PolicyContext { + trusted_bots: vec!["custom-bot".to_string()], + trusted_users: vec!["trusted-human".to_string()], + ..Default::default() + }; + + assert!(is_any_trusted_actor("github-actions[bot]", &ctx)); + assert!(is_any_trusted_actor("custom-bot", &ctx)); + assert!(is_any_trusted_actor("trusted-human", &ctx)); + assert!(!is_any_trusted_actor("random-user", &ctx)); + } + #[test] fn test_collaborator_permission_floor_admin() { let ctx = test_ctx();