mirror of
https://github.com/actions/checkout.git
synced 2026-06-17 01:14:54 +08:00
* block checking out fork pr for some events * address copilot and reviewer feedback * run prettier formatting * build * update urls * update readme * update description and url again * edit url one more time
89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
import * as github from '@actions/github'
|
|
import {fromPayload} from './ref-helper'
|
|
|
|
const PR_REF_PATTERN = /^refs\/pull\/[0-9]+\/(?:head|merge)$/
|
|
|
|
export interface IUnsafePrCheckoutInput {
|
|
qualifiedRepository: string
|
|
ref: string
|
|
commit: string | undefined
|
|
allowUnsafePrCheckout: boolean
|
|
}
|
|
|
|
export function assertSafePrCheckout(input: IUnsafePrCheckoutInput): void {
|
|
if (input.allowUnsafePrCheckout) {
|
|
return
|
|
}
|
|
|
|
const eventName = github.context.eventName
|
|
if (eventName !== 'pull_request_target' && eventName !== 'workflow_run') {
|
|
return
|
|
}
|
|
|
|
const baseRepoId = fromPayload('repository.id')
|
|
if (typeof baseRepoId !== 'number') {
|
|
return
|
|
}
|
|
|
|
let prHeadRepoId: unknown
|
|
let prHeadRepoFullName: unknown
|
|
const prShas: string[] = []
|
|
|
|
if (eventName === 'pull_request_target') {
|
|
prHeadRepoId = fromPayload('pull_request.head.repo.id')
|
|
prHeadRepoFullName = fromPayload('pull_request.head.repo.full_name')
|
|
pushIfSha(prShas, fromPayload('pull_request.head.sha'))
|
|
pushIfSha(prShas, fromPayload('pull_request.merge_commit_sha'))
|
|
} else {
|
|
const wrEvent = fromPayload('workflow_run.event')
|
|
if (typeof wrEvent !== 'string' || !wrEvent.startsWith('pull_request')) {
|
|
return
|
|
}
|
|
prHeadRepoId = fromPayload('workflow_run.head_repository.id')
|
|
prHeadRepoFullName = fromPayload('workflow_run.head_repository.full_name')
|
|
pushIfSha(prShas, fromPayload('workflow_run.head_commit.id'))
|
|
// For `pull_request_target`-triggered workflow_run, `head_sha` is the base
|
|
// default branch SHA (not the PR head)
|
|
if (wrEvent !== 'pull_request_target') {
|
|
pushIfSha(prShas, fromPayload('workflow_run.head_sha'))
|
|
}
|
|
}
|
|
|
|
// (A) Fork PR?
|
|
if (typeof prHeadRepoId !== 'number' || prHeadRepoId === baseRepoId) {
|
|
return
|
|
}
|
|
|
|
// (B) We cannot check for all fork PR refs so check to see
|
|
// if the resolved input points to the fork PR sha we have in the payload
|
|
const repositoryMatchesPrHead =
|
|
typeof prHeadRepoFullName === 'string' &&
|
|
input.qualifiedRepository.toLowerCase() === prHeadRepoFullName.toLowerCase()
|
|
const refMatchesPullPattern = PR_REF_PATTERN.test(input.ref)
|
|
const commitMatchesPrHeadSha =
|
|
!!input.commit && prShas.includes(input.commit.toLowerCase())
|
|
|
|
if (
|
|
!repositoryMatchesPrHead &&
|
|
!refMatchesPullPattern &&
|
|
!commitMatchesPrHeadSha
|
|
) {
|
|
return
|
|
}
|
|
|
|
throw new Error(
|
|
`Refusing to check out fork pull request code from a '${eventName}' workflow. ` +
|
|
`This workflow runs with the base repository's GITHUB_TOKEN, secrets, default-branch ` +
|
|
`cache scope, and runner access. Fetching and executing a fork's code in that trusted ` +
|
|
`context commonly leads to "pwn request" vulnerabilities. To opt in after reviewing ` +
|
|
`the risks at https://gh.io/securely-using-pull_request_target, set ` +
|
|
`'allow-unsafe-pr-checkout: true' on the actions/checkout step.`
|
|
)
|
|
}
|
|
|
|
function pushIfSha(target: string[], value: unknown): void {
|
|
if (typeof value === 'string' && value.length > 0) {
|
|
target.push(value.toLowerCase())
|
|
}
|
|
}
|