|
19 | 19 | - opened |
20 | 20 | - reopened |
21 | 21 | - synchronize |
| 22 | + - labeled |
22 | 23 | paths: |
23 | 24 | - "**.go" |
24 | 25 | - ".github/workflows/**" |
|
32 | 33 | if: > |
33 | 34 | github.event_name == 'schedule' || |
34 | 35 | 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') |
36 | 38 | concurrency: |
37 | 39 | group: ${{ github.workflow }}-${{ matrix.provider }}-${{ github.event.pull_request.number || github.ref_name }} |
38 | 40 | cancel-in-progress: true |
@@ -136,6 +138,27 @@ jobs: |
136 | 138 | core.info(`📋 Repository: ${repoOwner}/${repoName}`); |
137 | 139 | core.info(`🏢 Target organization: ${targetOrg}`); |
138 | 140 |
|
| 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 | +
|
139 | 162 | // Condition 1: Check if the user is a trusted bot. |
140 | 163 | const trustedBots = ["dependabot[bot]", "renovate[bot]"]; |
141 | 164 | core.info(`🤖 Checking if @${actor} is a trusted bot...`); |
@@ -256,21 +279,24 @@ jobs: |
256 | 279 | ); |
257 | 280 | return; // Success |
258 | 281 | } else { |
259 | | - // If we reach here, no conditions were met. This is the final failure. |
260 | 282 | 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; |
265 | 283 | } |
266 | 284 | } catch (error) { |
267 | | - // This error means they are not even a collaborator. |
268 | 285 | 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; |
273 | 286 | } |
| 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 | + ); |
274 | 300 | } |
275 | 301 |
|
276 | 302 | run().catch(err => { |
|
0 commit comments