-
Notifications
You must be signed in to change notification settings - Fork 2.5k
97 lines (82 loc) · 3.96 KB
/
protect-labels.yml
File metadata and controls
97 lines (82 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
name: Protect Labels from Unauthorized Changes
on:
issues:
types: [labeled, unlabeled]
jobs:
check-label-permissions:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Check if label change is authorized
uses: actions/github-script@v7
with:
script: |
const actor = context.actor;
const action = context.payload.action;
const label = context.payload.label.name;
const issue_number = context.issue.number;
// Labels that should be protected
const protectedLabels = [
'resource-submission',
'validation-passed',
'validation-failed',
'approved',
'rejected',
'changes-requested',
'pr-created',
'error-creating-pr'
];
// Check if this is a protected label
if (!protectedLabels.includes(label)) {
console.log(`Label "${label}" is not protected, allowing change`);
return;
}
// WORKAROUND: Allow adding template labels (resource-submission and pending-validation)
// GitHub attributes labels from issue templates as being added by the issue author,
// not by GitHub itself. Without this exception, the protect-labels workflow would
// incorrectly flag and revert these template-defined labels as unauthorized changes.
if (action === 'labeled' && (label === 'resource-submission' || label === 'pending-validation')) {
console.log(`Allowing template label "${label}" to be added (used by issue templates)`);
return;
}
// Check if actor has write permissions
const actorPermission = context.payload.sender.author_association;
const isRepoOwner = context.repo.owner === actor;
console.log(`Actor: ${actor}, Author Association: ${actorPermission}, Repo Owner: ${context.repo.owner}, Is Repo Owner: ${isRepoOwner}`);
// Check if authorized - include repo owner check for forks
const isAuthorized = isRepoOwner || ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(actorPermission);
if (isAuthorized) {
console.log(`User ${actor} is authorized to change labels`);
return;
}
// Unauthorized change detected - revert it
console.log(`Unauthorized label change by ${actor} - reverting`);
try {
if (action === 'labeled') {
// Remove the unauthorized label
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
name: label
});
} else if (action === 'unlabeled') {
// Re-add the removed label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
labels: [label]
});
}
// Add a comment explaining the reversion
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: `⚠️ **Unauthorized label change detected**\n\n@${actor} - The \`${label}\` label is protected and can only be modified by maintainers. Your change has been reverted.\n\nProtected labels are managed automatically by our workflows or by maintainers only.`
});
} catch (error) {
console.error('Error reverting label change:', error);
}