Короткий ответ
Нужно использовать событие `pull_request` (а не `pull_request_target`) для запуска тестов untrusted-кода из форков — в этом режиме GitHub Actions по умолчанию не даёт джобе доступ к секретам репозитория и использует `GITHUB_TOKEN` с ограниченными правами; `pull_request_target`, выполняющийся в контексте целевой ветки с полным доступом к секретам, следует применять только для доверенных операций (например, проставление лейблов) и никогда не чекаутить и не выполнять код из PR внутри него.
Как это работает подробнее
`pull_request` запускает workflow с кодом и конфигурацией из ветки/форка контрибьютора, но в изолированном контексте без доступа к секретам репозитория и с токеном, ограниченным read-only для большинства ресурсов. `pull_request_target` запускает workflow из конфигурации целевой ветки (main), но с полным доступом к секретам — опасность возникает, если такой workflow дополнительно явно чекаутит и исполняет код из PR (`actions/checkout` с `ref: ${{ github.event.pull_request.head.sha }}`), что фактически даёт внешнему контрибьютору выполнение произвольного кода с доступом к секретам.
Пример кода
# Небезопасно: pull_request_target + checkout кода PR + доступ к секретам
on: pull_request_target
jobs:
test:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # код из форка!
- run: npm test
env:
PROD_API_KEY: ${{ secrets.PROD_API_KEY }} # утечка возможнаЧто использовать на практике
- На практике. безопасный паттерн — разделить pipeline на две части: `pull_request` для сборки/тестов untrusted-кода без секретов, и отдельный `workflow_run`, который запускается после успешного завершения первого в контексте main (с доступом к секретам) и работает только с уже собранными артефактами, а не с исходным кодом PR напрямую.
- Пример. Ожидаемое поведение после исправления (замена на `pull_request` без секретов в этой джобе): даже вредоносный код в `npm test` из форка не сможет прочитать `PROD_API_KEY`, так как секрет просто не передаётся в контекст такого workflow.
Подводные камни
- Частые ошибки. используют `pull_request_target` для запуска тестов «потому что нужен доступ к секретам для интеграционных тестов», не осознавая, что тем самым открывают путь для кражи секретов через вредоносный PR; доверяют `pull_request_target` автоматически, не проверяя, что именно workflow выполняет с кодом PR.