fix(ci): move Test job to a GitHub-hosted runner (fork-PR RCE on self-hosted) - #31
Open
mattmattox wants to merge 1 commit into
Open
fix(ci): move Test job to a GitHub-hosted runner (fork-PR RCE on self-hosted)#31mattmattox wants to merge 1 commit into
mattmattox wants to merge 1 commit into
Conversation
…self-hosted)
This is a PUBLIC repository and the Test job was reachable by `pull_request` from
ANY fork with no job-level guard, on `runs-on: self-hosted-linux`.
`make test` is `go test ./...`, so a fork PR executed attacker-authored test code
on our self-hosted ARC runner - a runner with privileged dind, `sudo`, a shared
/cache hostPath holding GOCACHE/GOMODCACHE (poisoning it runs attacker code in
later, unrelated builds across two GitHub orgs), node-level Harbor pull
credentials, and network reach to Vault, Harbor, Athens and the Kubernetes API.
The runner namespace also hosts per-repo ci-deployer ServiceAccounts, and Vault's
Kubernetes token-mint role is bound to it.
The sibling jobs were already safe: Build and Publish carry
`if: github.event_name != 'pull_request'` and Release is tag-gated. Only Test was
unguarded - the file was internally inconsistent.
MOVED TO HOSTED RATHER THAN ADDING THAT SAME GUARD, deliberately. The guard would
close the hole by disabling PR testing entirely, which is a real loss for a public
repo that accepts outside contributions. A hosted runner keeps full PR coverage AND
costs nothing: GitHub-hosted standard runners have unlimited free minutes on public
repositories.
Verified safe to run hosted before making the change:
- go.mod has ZERO private-org requires and ZERO replace directives, so
`go mod download` resolves entirely from proxy.golang.org and needs no
Athens/LAN access.
- No Vault, Harbor or cluster access in the job. `azure/setup-kubectl` only
installs the binary.
- Steps are checkout / apt-get make / setup-go / go mod download / make test /
make test-crds - all runnable on a stock ubuntu-latest image.
Build, Publish and Release stay on self-hosted: they are guarded to non-PR events
and genuinely need Harbor.
Negative check after the change - no job is both pull_request-reachable AND
self-hosted without a guard:
Test ubuntu-latest (no guard needed)
Build self-hosted-linux if: github.event_name != 'pull_request'
Publish self-hosted-linux if: github.event_name != 'pull_request'
Release self-hosted-linux if: startsWith(github.ref, 'refs/tags/v')
If a private dependency is ever added here, do NOT move this back to self-hosted -
split the private part into a separate, fork-guarded job instead. A comment on the
job records this.
Refs: cluster-services-517
Claude-Session: https://claude.ai/code/session_01PYjZywQworVe5gurTn1J5D
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.
What
Moves the
Testjob fromself-hosted-linuxtoubuntu-latest. One line changed, plus a comment explaining why it must stay that way.Why
This is a public repository, and
Testwas reachable bypull_requestfrom any fork with no job-level guard, running on our self-hosted ARC runner.make testisgo test ./...— so a fork PR executed attacker-authored test code on a runner that has:sudo/cachehostPath holdingGOCACHE/GOMODCACHE— poisoning it runs attacker code in later, unrelated builds across two GitHub orgsThe sibling jobs were already safe —
BuildandPublishcarryif: github.event_name != 'pull_request',Releaseis tag-gated. OnlyTestwas unguarded, so the file was internally inconsistent.Why hosted instead of adding the same guard
Adding
if: github.event_name != 'pull_request'would close the hole by disabling PR testing entirely — a real loss for a public repo that accepts outside contributions.Moving to a hosted runner keeps full PR coverage and costs nothing: GitHub-hosted standard runners have unlimited free minutes on public repositories.
Verified safe to run hosted, before making the change
go.modhas 0 private-org requires and 0 replace directives →go mod downloadresolves entirely fromproxy.golang.org, no Athens/LAN neededazure/setup-kubectlonly installs the binaryubuntu-latestimageNegative check after the change
No job is both
pull_request-reachable and self-hosted without a guard:TestBuildgithub.event_name != 'pull_request'Publishgithub.event_name != 'pull_request'ReleasestartsWith(github.ref, 'refs/tags/v')This PR is its own test
It triggers
on: pull_request, so theTestrun on this PR is the live proof that the job works on a hosted runner. Merge once it's green.Note for the future
If a private dependency is ever added here, do not move this back to self-hosted — split the private part into a separate, fork-guarded job. There's a comment on the job saying so.
Refs:
cluster-services-517https://claude.ai/code/session_01PYjZywQworVe5gurTn1J5D