From 30e4b401388eb7ea3dca014c1ef216fc558e88eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:54:26 +0000 Subject: [PATCH] skip running unsafe pr check if input is default (backport #2518 to releases/v5) --- __test__/input-helper.test.ts | 46 +++++++++++++++++++++++++++++++++++ dist/index.js | 19 ++++++++++----- src/input-helper.ts | 19 ++++++++++----- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/__test__/input-helper.test.ts b/__test__/input-helper.test.ts index 2b7e918..3901125 100644 --- a/__test__/input-helper.test.ts +++ b/__test__/input-helper.test.ts @@ -145,4 +145,50 @@ describe('input-helper tests', () => { const settings: IGitSourceSettings = await inputHelper.getInputs() expect(settings.workflowOrganizationId).toBe(123456) }) + + describe('unsafe PR checkout guard', () => { + const forkPayload = { + repository: {id: 100}, + pull_request: { + head: { + sha: '1234567890123456789012345678901234567890', + repo: {id: 200, full_name: 'attacker/fork'} + }, + merge_commit_sha: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + } + } + + it('allows the default self-checkout on a fork pull_request_target', async () => { + const originalEvent = github.context.eventName + const originalPayload = github.context.payload + try { + github.context.eventName = 'pull_request_target' + github.context.payload = forkPayload as any + // Simulate a rebase/fast-forward merge where the base tip (event SHA) + // equals the PR head SHA. The default self-checkout must still succeed. + github.context.sha = '1234567890123456789012345678901234567890' + const settings: IGitSourceSettings = await inputHelper.getInputs() + expect(settings.commit).toBe('1234567890123456789012345678901234567890') + } finally { + github.context.eventName = originalEvent + github.context.payload = originalPayload + } + }) + + it('refuses an explicit fork repository on pull_request_target', async () => { + const originalEvent = github.context.eventName + const originalPayload = github.context.payload + try { + github.context.eventName = 'pull_request_target' + github.context.payload = forkPayload as any + inputs.repository = 'attacker/fork' + await expect(inputHelper.getInputs()).rejects.toThrow( + /Refusing to check out fork pull request code/ + ) + } finally { + github.context.eventName = originalEvent + github.context.payload = originalPayload + } + }) + }) }) diff --git a/dist/index.js b/dist/index.js index a7b3b69..faf08d4 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1986,12 +1986,19 @@ function getInputs() { (core.getInput('allow-unsafe-pr-checkout') || 'false').toUpperCase() === 'TRUE'; core.debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`); - unsafePrCheckoutHelper.assertSafePrCheckout({ - qualifiedRepository, - ref: result.ref, - commit: result.commit, - allowUnsafePrCheckout: result.allowUnsafePrCheckout - }); + // The default self-checkout (this repository with no explicit ref) always + // resolves to the trusted ref/commit GitHub set for the triggering event, so + // the fork-checkout guard only needs to run when the caller customized the + // repository or ref. + const isDefaultCheckout = isWorkflowRepository && !core.getInput('ref'); + if (!isDefaultCheckout) { + unsafePrCheckoutHelper.assertSafePrCheckout({ + qualifiedRepository, + ref: result.ref, + commit: result.commit, + allowUnsafePrCheckout: result.allowUnsafePrCheckout + }); + } return result; }); } diff --git a/src/input-helper.ts b/src/input-helper.ts index 2535148..3d98ed8 100644 --- a/src/input-helper.ts +++ b/src/input-helper.ts @@ -168,12 +168,19 @@ export async function getInputs(): Promise { 'TRUE' core.debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`) - unsafePrCheckoutHelper.assertSafePrCheckout({ - qualifiedRepository, - ref: result.ref, - commit: result.commit, - allowUnsafePrCheckout: result.allowUnsafePrCheckout - }) + // The default self-checkout (this repository with no explicit ref) always + // resolves to the trusted ref/commit GitHub set for the triggering event, so + // the fork-checkout guard only needs to run when the caller customized the + // repository or ref. + const isDefaultCheckout = isWorkflowRepository && !core.getInput('ref') + if (!isDefaultCheckout) { + unsafePrCheckoutHelper.assertSafePrCheckout({ + qualifiedRepository, + ref: result.ref, + commit: result.commit, + allowUnsafePrCheckout: result.allowUnsafePrCheckout + }) + } return result }