Skip to content

Commit a2d85a5

Browse files
authored
Optimize rust-guard scope and integrity hot paths
1 parent 56865cf commit a2d85a5

2 files changed

Lines changed: 110 additions & 16 deletions

File tree

guards/github-guard/rust-guard/src/labels/helpers.rs

Lines changed: 109 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! This module contains utility functions used across the labeling system,
44
//! including JSON extraction, integrity determination, and common operations.
55
6+
use std::borrow::Cow;
67
use std::sync::atomic::{AtomicBool, Ordering};
78

89
use serde_json::Value;
@@ -156,10 +157,10 @@ pub struct PolicyContext {
156157
pub demotion_label: String,
157158
}
158159

159-
fn normalize_scope(scope: &str, ctx: &PolicyContext) -> String {
160+
fn normalize_scope<'a>(scope: &'a str, ctx: &'a PolicyContext) -> Cow<'a, str> {
160161
let token = policy_scope_token(&ctx.scopes);
161162
if token.is_empty() {
162-
scope.to_string()
163+
Cow::Borrowed(scope)
163164
} else if ctx
164165
.scopes
165166
.iter()
@@ -176,10 +177,10 @@ fn normalize_scope(scope: &str, ctx: &PolicyContext) -> String {
176177
if matches_any_scope {
177178
token
178179
} else {
179-
scope.to_string()
180+
Cow::Borrowed(scope)
180181
}
181182
} else {
182-
scope.to_string()
183+
Cow::Borrowed(scope)
183184
}
184185
}
185186

@@ -191,16 +192,24 @@ fn split_repo_id(repo_id: &str) -> Option<(&str, &str)> {
191192
Some((owner, repo))
192193
}
193194

194-
pub(crate) fn policy_scope_token(scopes: &[PolicyScopeEntry]) -> String {
195-
let labels: Vec<&str> = scopes
195+
pub(crate) fn policy_scope_token(scopes: &[PolicyScopeEntry]) -> Cow<'_, str> {
196+
let mut labels = scopes
196197
.iter()
197198
.map(|s| s.scope_label.as_str())
198-
.filter(|s| !s.is_empty())
199-
.collect();
200-
if labels.is_empty() {
201-
String::new()
202-
} else {
203-
labels.join(" | ")
199+
.filter(|s| !s.is_empty());
200+
match (labels.next(), labels.next()) {
201+
(None, _) => Cow::Borrowed(""),
202+
(Some(first), None) => Cow::Borrowed(first),
203+
(Some(first), Some(second)) => {
204+
let mut value = String::from(first);
205+
value.push_str(" | ");
206+
value.push_str(second);
207+
for rest in labels {
208+
value.push_str(" | ");
209+
value.push_str(rest);
210+
}
211+
Cow::Owned(value)
212+
}
204213
}
205214
}
206215

@@ -1284,16 +1293,30 @@ fn integrity_rank(scope: &str, labels: &[String], ctx: &PolicyContext) -> u8 {
12841293
}
12851294

12861295
fn integrity_rank_normalized(normalized_scope: &str, labels: &[String]) -> u8 {
1287-
// Check from highest to lowest, allocating one label at a time.
1296+
// Check from highest to lowest.
12881297
for (rank, (prefix, base)) in INTEGRITY_LEVELS.iter().enumerate().rev() {
1289-
let tag = format_integrity_label(prefix, normalized_scope, base);
1290-
if labels.iter().any(|l| l == &tag) {
1298+
if labels
1299+
.iter()
1300+
.any(|label| label_matches_normalized(label, prefix, normalized_scope, base))
1301+
{
12911302
return (rank + 1) as u8;
12921303
}
12931304
}
12941305
0
12951306
}
12961307

1308+
#[inline]
1309+
fn label_matches_normalized(label: &str, prefix: &str, scope: &str, base: &str) -> bool {
1310+
if scope.is_empty() {
1311+
label == base
1312+
} else if scope.contains('|') {
1313+
// Multi-scope uses canonical "integrity=<base>;scopes=..." encoding.
1314+
label == format_integrity_label(prefix, scope, base)
1315+
} else {
1316+
label.strip_prefix(prefix) == Some(scope)
1317+
}
1318+
}
1319+
12971320
/// Elevate integrity to the max of current and candidate levels for a scope.
12981321
pub fn max_integrity(
12991322
scope: &str,
@@ -1917,11 +1940,54 @@ pub(crate) fn is_any_trusted_actor(username: &str, ctx: &PolicyContext) -> bool
19171940
#[cfg(test)]
19181941
mod tests {
19191942
use super::*;
1943+
use std::borrow::Cow;
19201944

19211945
fn test_ctx() -> PolicyContext {
19221946
PolicyContext::default()
19231947
}
19241948

1949+
#[test]
1950+
fn test_policy_scope_token_borrows_for_single_scope() {
1951+
let scopes = vec![PolicyScopeEntry {
1952+
scope_kind: ScopeKind::Owner,
1953+
scope_owner: Some("octocat".to_string()),
1954+
scope_repo: None,
1955+
scope_label: "octocat".to_string(),
1956+
}];
1957+
let token = policy_scope_token(&scopes);
1958+
assert_eq!(token, "octocat");
1959+
assert!(matches!(token, Cow::Borrowed(_)));
1960+
}
1961+
1962+
#[test]
1963+
fn test_policy_scope_token_owns_for_multi_scope() {
1964+
let scopes = vec![
1965+
PolicyScopeEntry {
1966+
scope_kind: ScopeKind::Owner,
1967+
scope_owner: Some("octocat".to_string()),
1968+
scope_repo: None,
1969+
scope_label: "octocat".to_string(),
1970+
},
1971+
PolicyScopeEntry {
1972+
scope_kind: ScopeKind::RepoPrefix,
1973+
scope_owner: Some("octocat".to_string()),
1974+
scope_repo: Some("hello".to_string()),
1975+
scope_label: "octocat/hello*".to_string(),
1976+
},
1977+
];
1978+
let token = policy_scope_token(&scopes);
1979+
assert_eq!(token, "octocat | octocat/hello*");
1980+
assert!(matches!(token, Cow::Owned(_)));
1981+
}
1982+
1983+
#[test]
1984+
fn test_normalize_scope_borrows_input_when_no_scopes() {
1985+
let ctx = test_ctx();
1986+
let normalized = normalize_scope("owner/repo", &ctx);
1987+
assert_eq!(normalized, "owner/repo");
1988+
assert!(matches!(normalized, Cow::Borrowed(_)));
1989+
}
1990+
19251991
#[test]
19261992
fn test_is_any_trusted_actor_tiers_and_negative() {
19271993
let ctx = PolicyContext {
@@ -2317,6 +2383,34 @@ mod tests {
23172383
assert_eq!(result, candidate, "max_integrity should keep the higher integrity rank");
23182384
}
23192385

2386+
#[test]
2387+
fn test_label_matches_normalized_common_and_multiscope_paths() {
2388+
assert!(label_matches_normalized(
2389+
"approved:owner/repo",
2390+
label_constants::WRITER_PREFIX,
2391+
"owner/repo",
2392+
label_constants::WRITER_BASE
2393+
));
2394+
assert!(label_matches_normalized(
2395+
"integrity=approved;scopes=owner/repo,owner/tool*",
2396+
label_constants::WRITER_PREFIX,
2397+
"owner/repo | owner/tool*",
2398+
label_constants::WRITER_BASE
2399+
));
2400+
assert!(label_matches_normalized(
2401+
label_constants::READER_BASE,
2402+
label_constants::READER_PREFIX,
2403+
"",
2404+
label_constants::READER_BASE
2405+
));
2406+
assert!(!label_matches_normalized(
2407+
"approved:owner/repo",
2408+
label_constants::MERGED_PREFIX,
2409+
"owner/repo",
2410+
label_constants::MERGED_BASE
2411+
));
2412+
}
2413+
23202414
#[test]
23212415
fn test_integrity_for_level_mapping() {
23222416
let ctx = test_ctx();

guards/github-guard/rust-guard/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,6 @@ pub extern "C" fn label_agent(
529529
})
530530
.collect();
531531

532-
let token = labels::helpers::policy_scope_token(&scopes);
533532
let scope_kind_str = normalized_scope_kind(&scopes);
534533

535534
let ctx = PolicyContext {
@@ -547,6 +546,7 @@ pub extern "C" fn label_agent(
547546
};
548547

549548
// Compute integrity before moving ctx into the global — borrows ctx, no clone needed.
549+
let token = labels::helpers::policy_scope_token(&ctx.scopes);
550550
let integrity = match integrity_floor {
551551
MinIntegrity::None => labels::none_integrity(&token, &ctx),
552552
MinIntegrity::Unapproved => labels::reader_integrity(&token, &ctx),

0 commit comments

Comments
 (0)