fix: secure CI against fork PRs using pull_request + workflow_run split#18
Merged
Conversation
Fork PRs previously couldn't access DOCKER_USERNAME/DOCKER_PASSWORD secrets, causing the push step to silently fail. This implements the pull_request + workflow_run split pattern recommended by GitHub Security Lab to safely push PR images even from fork contributors. - ci.yaml: runs on pull_request with no secrets; builds a multi-arch OCI tarball and uploads it as an artifact. For non-PR events (push, dispatch) it logs in and pushes directly as before. - ci-publish.yaml: runs on workflow_run after ci succeeds; always executes from the default branch so its code is never fork-controlled. Downloads the pre-built tarball, validates the image tag with a strict regex, and uses docker buildx imagetools create to push to portainerci. Posts a PR comment with the published image tag. The fork author's code is only ever executed in the sandboxed, secrets-less ci workflow. Secrets are only accessed in ci-publish.yaml which never runs untrusted code, preventing the "pwn request" supply chain attack vector. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fork PRs cannot access repository secrets (
DOCKER_USERNAME,DOCKER_PASSWORD), causing the Docker push step to fail silently. The previous workflow also heldpull-requests: writepermission in the same context as the build, which is unnecessary exposure.Solution
Split the single CI workflow into two, following the pattern recommended by GitHub Security Lab:
ci.yaml— triggered bypull_request. Runs withcontents: readonly, no secrets. Builds a multi-arch OCI tarball and uploads it as an artifact. Fork contributors can never access secrets here regardless of what their code does.ci-publish.yaml— triggered byworkflow_runaftercisucceeds. Always executes from thedevelopbranch, so its code is never fork-controlled. Downloads the pre-built artifact, validates the image tag with a strict regex, logs in with registry credentials, and pushes viadocker buildx imagetools create. Posts a PR comment with the published image reference.For non-PR events (push to
develop,workflow_dispatch),ci.yamlbuilds and pushes directly as before — no change to that path.Security properties
ciworkflowci-publishworkflow never executes any code from the fork^pr-[0-9]+$before use, preventing injection