Skip to content

Commit a0a1169

Browse files
authored
Merge pull request #5 from pemamian/pr-rules-engine
feat: enforce label guardrails as the primary triage rule and add sup…
2 parents d8ca0c1 + 81b9344 commit a0a1169

2 files changed

Lines changed: 124 additions & 48 deletions

File tree

.github/workflows/scripts/routing/test_routing.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,40 @@ def test_unauthorized_needs_review_label_removal_guardrail(self):
297297
self.assertEqual(len(result.comments_to_create), 1)
298298
self.assertTrue("Warning: @unauthorized_tester, you do not have permission to remove `gov:needs-tc-review`." in result.comments_to_create[0])
299299

300+
def test_unauthorized_approved_label_removal_guardrail(self):
301+
"""Verifies ReviewerApprovalRule blocks unauthorized removal of active satisfied approved labels."""
302+
# Scenario: Changed schemas file, TC approvals met (amit approved), but user 'unauthorized_tester' manually removed gov:tc-approved
303+
self.mock_client.check_team_membership.return_value = False # User is not a member of TC or DevOps
304+
305+
context_unlabel_approved = PRContext(
306+
pr_number=112,
307+
repo_name="Universal-Commerce-Protocol/ucp",
308+
title="feat: corespec updates",
309+
author="developer2",
310+
is_draft=False,
311+
labels={Label.LABEL_TC_APPROVED}, # Target is currently marked approved
312+
modified_files=["schemas/v1/transaction.json"],
313+
reviews=[
314+
ReviewInfo(user="amithanda", state="APPROVED") # TC reviews fully satisfied (amit override)
315+
],
316+
event_name="pull_request",
317+
event_payload={
318+
"action": "unlabeled",
319+
"sender": {"login": "unauthorized_tester"},
320+
"label": {"name": "gov:tc-approved"} # Removed approved label name
321+
}
322+
)
323+
324+
rule = ReviewerApprovalRule(self.mock_config)
325+
result = rule.evaluate(context_unlabel_approved, self.mock_client)
326+
327+
# Assert that the approved label is dynamically re-applied
328+
self.assertTrue("gov:tc-approved" in result.labels_to_add)
329+
# Assert warning comment is prepared
330+
self.assertEqual(len(result.comments_to_create), 1)
331+
self.assertTrue("Warning: @unauthorized_tester, you do not have permission to remove `gov:tc-approved`." in result.comments_to_create[0])
332+
333+
300334
def test_label_lifecycle_blocked_resume(self):
301335
"""Verifies LabelLifecycleRule handles blocked and resumed triggers."""
302336
rule = LabelLifecycleRule()

.github/workflows/scripts/routing/triage/rules.py

Lines changed: 90 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,86 @@ def evaluate(self, context: PRContext, client: GitHubAPIClient) -> RuleResult:
108108
labels_to_remove = set()
109109
comments = []
110110

111+
# ==============================================================================
112+
# 0. Enforce Guardrail Logic FIRST (Always executes, prevents early return bypass)
113+
# ==============================================================================
114+
event_user = context.event_payload.get("sender", {}).get("login")
115+
event_action = context.event_payload.get("action")
116+
org_name = context.repo_name.split("/")[0]
117+
118+
# A. Guard against unauthorized addition of TC Majority Label
119+
if Label.LABEL_TC_MAJORITY_APPROVED in context.labels:
120+
if event_user and context.event_name == "pull_request" and event_action == "labeled":
121+
label_added = context.event_payload.get("label", {}).get("name")
122+
if label_added == Label.LABEL_TC_MAJORITY_APPROVED:
123+
is_tc = client.check_team_membership(org_name, "tech-council", event_user)
124+
is_devops = client.check_team_membership(org_name, "devops-maintainers", event_user)
125+
if not is_tc and not is_devops:
126+
print(f"[GUARDRAIL] Unauthorized user {event_user} applied majority label. Revoking.")
127+
labels_to_remove.add(Label.LABEL_TC_MAJORITY_APPROVED)
128+
comments.append(
129+
f"Warning: @{event_user}, you do not have permission to apply "
130+
f"`{Label.LABEL_TC_MAJORITY_APPROVED}`. This action has been automatically reverted."
131+
)
132+
133+
# B. Guard against unauthorized removal of active needs-review or approved labels
134+
if context.event_name == "pull_request" and event_action == "unlabeled":
135+
label_removed = context.event_payload.get("label", {}).get("name")
136+
137+
for rule in self.config:
138+
for team_handle, req_details in rule.get("review_requirements", {}).items():
139+
needs_label = req_details.get("needs_review_label")
140+
approved_label = req_details.get("approved_label")
141+
142+
# Check if the removed label is either the needs-review or the approved label of this team
143+
is_needs = needs_label and label_removed == needs_label
144+
is_approved = approved_label and label_removed == approved_label
145+
146+
if is_needs or is_approved:
147+
# Verify if the user who removed it is a member of the team or DevOps
148+
clean_handle = team_handle.lstrip("@")
149+
team_org, team_slug = clean_handle.split("/", 1)
150+
151+
is_team_member = client.check_team_membership(team_org, team_slug, event_user)
152+
is_devops = client.check_team_membership(org_name, "devops-maintainers", event_user)
153+
154+
if not is_team_member and not is_devops:
155+
satisfied, _ = verify_team_approvals(context, team_handle, req_details.get("threshold", 1), client)
156+
157+
# Scenario A: Removed needs-review label while reviews are still pending
158+
if is_needs and not satisfied:
159+
print(f"[GUARDRAIL] Unauthorized user {event_user} removed required label {needs_label}. Re-applying.")
160+
labels_to_add.add(needs_label)
161+
comments.append(
162+
f"Warning: @{event_user}, you do not have permission to remove "
163+
f"`{needs_label}`. Reviews from `{team_handle}` are still pending. "
164+
f"This action has been automatically reverted."
165+
)
166+
167+
# Scenario B: Removed approved label while reviews are fully satisfied
168+
elif is_approved and satisfied:
169+
print(f"[GUARDRAIL] Unauthorized user {event_user} removed approved label {approved_label}. Re-applying.")
170+
labels_to_add.add(approved_label)
171+
comments.append(
172+
f"Warning: @{event_user}, you do not have permission to remove "
173+
f"`{approved_label}`. Reviews from `{team_handle}` are satisfied and approved. "
174+
f"This action has been automatically reverted."
175+
)
176+
177+
# If guardrails re-applied labels, we exit early with results to prevent regular evaluation overrides
178+
if labels_to_add or labels_to_remove:
179+
return RuleResult(
180+
self.name,
181+
satisfied=False,
182+
labels_to_add=labels_to_add,
183+
labels_to_remove=labels_to_remove,
184+
comments_to_create=comments,
185+
action_taken="Guardrails override triggered to revert unauthorized label modifications."
186+
)
187+
188+
# ==============================================================================
111189
# 1. Superpower Override (e.g., Amit's approval satisfies all rules)
190+
# ==============================================================================
112191
SUPERPOWER_USERS = {"amithanda"}
113192
for review in context.reviews:
114193
if review.user in SUPERPOWER_USERS and review.state == "APPROVED":
@@ -131,7 +210,9 @@ def evaluate(self, context: PRContext, client: GitHubAPIClient) -> RuleResult:
131210
action_taken="Superpower approval override triggered."
132211
)
133212

213+
# ==============================================================================
134214
# 2. Review requirements matching core spec rules or relaxed SDK settings
215+
# ==============================================================================
135216
is_sdk = any(sdk_repo in context.repo_name.lower() for sdk_repo in ["sdk", "meeting-minutes"])
136217

137218
all_rules_satisfied = True
@@ -185,53 +266,6 @@ def evaluate(self, context: PRContext, client: GitHubAPIClient) -> RuleResult:
185266
if approved_label:
186267
labels_to_add.add(approved_label)
187268

188-
# Enforce guardrail logic for governance labels application & removal security
189-
event_user = context.event_payload.get("sender", {}).get("login")
190-
event_action = context.event_payload.get("action")
191-
org_name = context.repo_name.split("/")[0]
192-
193-
# 1. Guard against unauthorized addition of TC Majority Label
194-
if Label.LABEL_TC_MAJORITY_APPROVED in context.labels:
195-
if event_user and context.event_name == "pull_request" and event_action == "labeled":
196-
label_added = context.event_payload.get("label", {}).get("name")
197-
if label_added == Label.LABEL_TC_MAJORITY_APPROVED:
198-
is_tc = client.check_team_membership(org_name, "tech-council", event_user)
199-
is_devops = client.check_team_membership(org_name, "devops-maintainers", event_user)
200-
if not is_tc and not is_devops:
201-
print(f"[GUARDRAIL] Unauthorized user {event_user} applied majority label. Revoking.")
202-
labels_to_remove.add(Label.LABEL_TC_MAJORITY_APPROVED)
203-
comments.append(
204-
f"Warning: @{event_user}, you do not have permission to apply "
205-
f"`{Label.LABEL_TC_MAJORITY_APPROVED}`. This action has been automatically reverted."
206-
)
207-
208-
# 2. Guard against unauthorized removal of active needs-review labels
209-
if context.event_name == "pull_request" and event_action == "unlabeled":
210-
label_removed = context.event_payload.get("label", {}).get("name")
211-
212-
for rule in self.config:
213-
for team_handle, req_details in rule.get("review_requirements", {}).items():
214-
needs_label = req_details.get("needs_review_label")
215-
216-
if needs_label and label_removed == needs_label:
217-
# Verify if the user who removed it is a member of the team or DevOps
218-
clean_handle = team_handle.lstrip("@")
219-
team_org, team_slug = clean_handle.split("/", 1)
220-
221-
is_team_member = client.check_team_membership(team_org, team_slug, event_user)
222-
is_devops = client.check_team_membership(org_name, "devops-maintainers", event_user)
223-
224-
# If the requirements are not satisfied and the user is unauthorized, re-apply!
225-
satisfied, _ = verify_team_approvals(context, team_handle, req_details.get("threshold", 1), client)
226-
if not satisfied and not is_team_member and not is_devops:
227-
print(f"[GUARDRAIL] Unauthorized user {event_user} removed required label {needs_label}. Re-applying.")
228-
labels_to_add.add(needs_label)
229-
comments.append(
230-
f"Warning: @{event_user}, you do not have permission to remove "
231-
f"`{needs_label}`. Reviews from `{team_handle}` are still pending. "
232-
f"This action has been automatically reverted."
233-
)
234-
235269
# If all rules passed, transition to ready-to-merge
236270
if all_rules_satisfied and rules_evaluated > 0:
237271
labels_to_add.add(Label.LABEL_APPROVED)
@@ -374,14 +408,22 @@ def verify_team_approvals(context: PRContext, team_handle: str, threshold: any,
374408
approvals = 0
375409
approved_users = set()
376410

377-
# Select active approvals
411+
# Select active approvals (including superpower overrides check)
412+
SUPERPOWER_USERS = {"amithanda"}
413+
has_superpower_approval = False
414+
378415
for review in context.reviews:
379416
if review.state == "APPROVED":
417+
if review.user in SUPERPOWER_USERS:
418+
has_superpower_approval = True
380419
is_member = client.check_team_membership(org_name, team_slug, review.user)
381420
if is_member:
382421
approvals += 1
383422
approved_users.add(review.user)
384423

424+
if has_superpower_approval:
425+
return True, approvals
426+
385427
if threshold == "majority":
386428
# Programmatic majority overrides: TC triggers majority via manual majority label or meetings
387429
return Label.LABEL_TC_MAJORITY_APPROVED in context.labels, approvals

0 commit comments

Comments
 (0)