|
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 |
@@ -137,6 +139,27 @@ jobs: |
137 | 139 | core.info(`📋 Repository: ${repoOwner}/${repoName}`); |
138 | 140 | core.info(`🏢 Target organization: ${targetOrg}`); |
139 | 141 |
|
| 142 | + // Security: On non-labeled events (opened, reopened, synchronize), |
| 143 | + // remove the ok-to-test label if present. This prevents an external |
| 144 | + // contributor from pushing malicious code after a maintainer approved via label. |
| 145 | + if (context.payload.action !== 'labeled') { |
| 146 | + const currentLabels = context.payload.pull_request.labels.map(l => l.name); |
| 147 | + if (currentLabels.includes('ok-to-test')) { |
| 148 | + core.info(`🔒 Removing ok-to-test label due to '${context.payload.action}' event — re-approval required.`); |
| 149 | + try { |
| 150 | + await github.rest.issues.removeLabel({ |
| 151 | + owner: repoOwner, |
| 152 | + repo: repoName, |
| 153 | + issue_number: context.payload.pull_request.number, |
| 154 | + name: 'ok-to-test', |
| 155 | + }); |
| 156 | + core.info(` Label removed successfully.`); |
| 157 | + } catch (err) { |
| 158 | + core.warning(` Failed to remove ok-to-test label: ${err.message}`); |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | +
|
140 | 163 | // Condition 1: Check if the user is a trusted bot. |
141 | 164 | const trustedBots = ["dependabot[bot]", "renovate[bot]"]; |
142 | 165 | core.info(`🤖 Checking if @${actor} is a trusted bot...`); |
@@ -257,21 +280,24 @@ jobs: |
257 | 280 | ); |
258 | 281 | return; // Success |
259 | 282 | } else { |
260 | | - // If we reach here, no conditions were met. This is the final failure. |
261 | 283 | core.info(` ❌ Permission '${permission}' is insufficient (requires 'write' or 'admin')`); |
262 | | - core.setFailed( |
263 | | - `❌ Permission check failed. User @${actor} did not meet any required conditions (trusted bot, org member, or repo write access).`, |
264 | | - ); |
265 | | - return; |
266 | 284 | } |
267 | 285 | } catch (error) { |
268 | | - // This error means they are not even a collaborator. |
269 | 286 | core.info(` ❌ Collaborator permission check failed: ${error.message}`); |
270 | | - core.setFailed( |
271 | | - `❌ Permission check failed. User @${actor} is not a collaborator on this repository and did not meet other conditions.`, |
272 | | - ); |
273 | | - return; |
274 | 287 | } |
| 288 | +
|
| 289 | + // Condition 4: Check for ok-to-test label (for external contributors approved by maintainers). |
| 290 | + // Only users with repo write access can add labels, so this is inherently gated. |
| 291 | + core.info(`\n🏷️ Condition 4: Checking for ok-to-test label...`); |
| 292 | + if (context.payload.action === 'labeled' && context.payload.label && context.payload.label.name === 'ok-to-test') { |
| 293 | + core.info(`✅ Condition met: ok-to-test label applied by @${context.actor}. Proceeding with tests.`); |
| 294 | + return; // Success |
| 295 | + } |
| 296 | +
|
| 297 | + core.info(` ❌ No ok-to-test label event detected.`); |
| 298 | + core.setFailed( |
| 299 | + `❌ Permission check failed. User @${actor} did not meet any required conditions (trusted bot, org/team member, repo write access, or ok-to-test label).`, |
| 300 | + ); |
275 | 301 | } |
276 | 302 |
|
277 | 303 | run().catch(err => { |
|
0 commit comments