Compare commits

..

1 Commits

Author SHA1 Message Date
Aiqiao Yan
011c9c50dd skip running unsafe pr check if input is default 2026-07-14 21:10:26 +00:00
5 changed files with 1146 additions and 2024 deletions

View File

@@ -180,4 +180,50 @@ describe('input-helper tests', () => {
const settings: IGitSourceSettings = await inputHelper.getInputs() const settings: IGitSourceSettings = await inputHelper.getInputs()
expect(settings.workflowOrganizationId).toBe(123456) 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 = mockGithubContext.eventName
const originalPayload = mockGithubContext.payload
try {
mockGithubContext.eventName = 'pull_request_target'
mockGithubContext.payload = forkPayload
// Simulate a rebase/fast-forward merge where the base tip (event SHA)
// equals the PR head SHA. The default self-checkout must still succeed.
mockGithubContext.sha = '1234567890123456789012345678901234567890'
const settings: IGitSourceSettings = await inputHelper.getInputs()
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
} finally {
mockGithubContext.eventName = originalEvent
mockGithubContext.payload = originalPayload
}
})
it('refuses an explicit fork repository on pull_request_target', async () => {
const originalEvent = mockGithubContext.eventName
const originalPayload = mockGithubContext.payload
try {
mockGithubContext.eventName = 'pull_request_target'
mockGithubContext.payload = forkPayload
inputs.repository = 'attacker/fork'
await expect(inputHelper.getInputs()).rejects.toThrow(
/Refusing to check out fork pull request code/
)
} finally {
mockGithubContext.eventName = originalEvent
mockGithubContext.payload = originalPayload
}
})
})
}) })

19
dist/index.js vendored
View File

@@ -42191,12 +42191,19 @@ async function getInputs() {
(getInput('allow-unsafe-pr-checkout') || 'false').toUpperCase() === (getInput('allow-unsafe-pr-checkout') || 'false').toUpperCase() ===
'TRUE'; 'TRUE';
core_debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`); core_debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`);
assertSafePrCheckout({ // The default self-checkout (this repository with no explicit ref) always
qualifiedRepository, // resolves to the trusted ref/commit GitHub set for the triggering event, so
ref: result.ref, // the fork-checkout guard only needs to run when the caller customized the
commit: result.commit, // repository or ref.
allowUnsafePrCheckout: result.allowUnsafePrCheckout const isDefaultCheckout = isWorkflowRepository && !getInput('ref');
}); if (!isDefaultCheckout) {
assertSafePrCheckout({
qualifiedRepository,
ref: result.ref,
commit: result.commit,
allowUnsafePrCheckout: result.allowUnsafePrCheckout
});
}
return result; return result;
} }

3082
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,7 @@
"@actions/tool-cache": "^4.0.0" "@actions/tool-cache": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^30.0.0", "@types/jest": "^29.5.12",
"@types/node": "^24.1.0", "@types/node": "^24.1.0",
"@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0", "@typescript-eslint/parser": "^8.54.0",
@@ -47,7 +47,7 @@
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-plugin-github": "^6.0.0", "eslint-plugin-github": "^6.0.0",
"eslint-plugin-jest": "^29.12.1", "eslint-plugin-jest": "^29.12.1",
"jest": "^30.4.2", "jest": "^29.7.0",
"js-yaml": "^4.2.0", "js-yaml": "^4.2.0",
"prettier": "^3.8.4", "prettier": "^3.8.4",
"ts-jest": "^29.4.11", "ts-jest": "^29.4.11",

View File

@@ -168,12 +168,19 @@ export async function getInputs(): Promise<IGitSourceSettings> {
'TRUE' 'TRUE'
core.debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`) core.debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`)
unsafePrCheckoutHelper.assertSafePrCheckout({ // The default self-checkout (this repository with no explicit ref) always
qualifiedRepository, // resolves to the trusted ref/commit GitHub set for the triggering event, so
ref: result.ref, // the fork-checkout guard only needs to run when the caller customized the
commit: result.commit, // repository or ref.
allowUnsafePrCheckout: result.allowUnsafePrCheckout const isDefaultCheckout = isWorkflowRepository && !core.getInput('ref')
}) if (!isDefaultCheckout) {
unsafePrCheckoutHelper.assertSafePrCheckout({
qualifiedRepository,
ref: result.ref,
commit: result.commit,
allowUnsafePrCheckout: result.allowUnsafePrCheckout
})
}
return result return result
} }