Skip to content

Commit c119c8d

Browse files
committed
test(e2e): allow external PRs via ok-to-test label
Add ok-to-test label gating as a fallback for external contributors who are not org members or repo collaborators. Maintainers can approve external PRs by adding the ok-to-test label after code review. The label is automatically removed on synchronize, opened, and reopened events to prevent running CI on new untrusted code without re-approval. Signed-off-by: Zaki Shaikh <zashaikh@redhat.com> Assisted-by: Claude Opus 4.6 (via Claude Code)
1 parent 7e6df69 commit c119c8d

File tree

1 file changed

+37
-11
lines changed

1 file changed

+37
-11
lines changed

.github/workflows/e2e.yaml

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ on:
1919
- opened
2020
- reopened
2121
- synchronize
22+
- labeled
2223
paths:
2324
- "**.go"
2425
- ".github/workflows/**"
@@ -32,7 +33,8 @@ jobs:
3233
if: >
3334
github.event_name == 'schedule' ||
3435
github.event_name == 'workflow_dispatch' ||
35-
github.event_name == 'pull_request_target'
36+
(github.event_name == 'pull_request_target' && github.event.action != 'labeled') ||
37+
(github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.event.label.name == 'ok-to-test')
3638
concurrency:
3739
group: ${{ github.workflow }}-${{ matrix.provider }}-${{ github.event.pull_request.number || github.ref_name }}
3840
cancel-in-progress: true
@@ -136,6 +138,27 @@ jobs:
136138
core.info(`📋 Repository: ${repoOwner}/${repoName}`);
137139
core.info(`🏢 Target organization: ${targetOrg}`);
138140
141+
// Security: On non-labeled events (opened, reopened, synchronize),
142+
// remove the ok-to-test label if present. This prevents an external
143+
// contributor from pushing malicious code after a maintainer approved via label.
144+
if (context.payload.action !== 'labeled') {
145+
const currentLabels = context.payload.pull_request.labels.map(l => l.name);
146+
if (currentLabels.includes('ok-to-test')) {
147+
core.info(`🔒 Removing ok-to-test label due to '${context.payload.action}' event — re-approval required.`);
148+
try {
149+
await github.rest.issues.removeLabel({
150+
owner: repoOwner,
151+
repo: repoName,
152+
issue_number: context.payload.pull_request.number,
153+
name: 'ok-to-test',
154+
});
155+
core.info(` Label removed successfully.`);
156+
} catch (err) {
157+
core.warning(` Failed to remove ok-to-test label: ${err.message}`);
158+
}
159+
}
160+
}
161+
139162
// Condition 1: Check if the user is a trusted bot.
140163
const trustedBots = ["dependabot[bot]", "renovate[bot]"];
141164
core.info(`🤖 Checking if @${actor} is a trusted bot...`);
@@ -256,21 +279,24 @@ jobs:
256279
);
257280
return; // Success
258281
} else {
259-
// If we reach here, no conditions were met. This is the final failure.
260282
core.info(` ❌ Permission '${permission}' is insufficient (requires 'write' or 'admin')`);
261-
core.setFailed(
262-
`❌ Permission check failed. User @${actor} did not meet any required conditions (trusted bot, org member, or repo write access).`,
263-
);
264-
return;
265283
}
266284
} catch (error) {
267-
// This error means they are not even a collaborator.
268285
core.info(` ❌ Collaborator permission check failed: ${error.message}`);
269-
core.setFailed(
270-
`❌ Permission check failed. User @${actor} is not a collaborator on this repository and did not meet other conditions.`,
271-
);
272-
return;
273286
}
287+
288+
// Condition 4: Check for ok-to-test label (for external contributors approved by maintainers).
289+
// Only users with repo write access can add labels, so this is inherently gated.
290+
core.info(`\n🏷️ Condition 4: Checking for ok-to-test label...`);
291+
if (context.payload.action === 'labeled' && context.payload.label && context.payload.label.name === 'ok-to-test') {
292+
core.info(`✅ Condition met: ok-to-test label applied by @${context.actor}. Proceeding with tests.`);
293+
return; // Success
294+
}
295+
296+
core.info(` ❌ No ok-to-test label event detected.`);
297+
core.setFailed(
298+
`❌ Permission check failed. User @${actor} did not meet any required conditions (trusted bot, org/team member, repo write access, or ok-to-test label).`,
299+
);
274300
}
275301
276302
run().catch(err => {

0 commit comments

Comments
 (0)