Security: CI shares persistent Go cache volumes across the fork-PR / trusted-main trust boundary #272

Closed
opened 2026-07-19 03:43:30 +00:00 by hexajon · 0 comments
hexajon commented 2026-07-19 03:43:30 +00:00 (Migrated from codeberg.org)

Every job in both workflows mounts the same runner-local named volumes:

options: -v madtea-gomod:/go/pkg/mod -v madtea-gobuild:/root/.cache/go-build

(.forgejo/workflows/ci.yml - present on all jobs, verified lines 79/112/139/171/207/238/271/302/329/...; same pattern in drift.yml). These volumes persist on the runner and are shared across both fork-PR runs and trusted main runs.

Once the repo is public and takes fork PRs, a hostile fork PR's go test / go build has write access to both caches. Scenario: the PR job poisons madtea-gobuild or madtea-gomod; a later trusted main run reuses the poisoned cache, producing a compromised build or a falsely-green gate. Go's content-addressing and go.sum blunt naive tampering, but a shared mutable cache spanning a trust boundary is the anti-pattern regardless.

Blast radius is limited because release artifacts are built locally on the maintainer machine, never on this runner (confirmed separately) - so a poisoned CI cache can corrupt gate results but not shipped binaries. Still worth closing before public launch.

Fix: do not share build/mod caches between fork-PR runs and trusted runs - disable the cache mount for fork PRs, or namespace the volume by trust level (e.g. a separate throwaway cache for PR runs).

Acceptance: fork-PR jobs and trusted main jobs no longer read/write the same cache volume.

Every job in both workflows mounts the same runner-local named volumes: ``` options: -v madtea-gomod:/go/pkg/mod -v madtea-gobuild:/root/.cache/go-build ``` (`.forgejo/workflows/ci.yml` - present on all jobs, verified lines 79/112/139/171/207/238/271/302/329/...; same pattern in `drift.yml`). These volumes persist on the runner and are shared across both fork-PR runs and trusted `main` runs. Once the repo is public and takes fork PRs, a hostile fork PR's `go test` / `go build` has write access to both caches. Scenario: the PR job poisons `madtea-gobuild` or `madtea-gomod`; a later trusted `main` run reuses the poisoned cache, producing a compromised build or a falsely-green gate. Go's content-addressing and `go.sum` blunt naive tampering, but a shared mutable cache spanning a trust boundary is the anti-pattern regardless. Blast radius is limited because release artifacts are built locally on the maintainer machine, never on this runner (confirmed separately) - so a poisoned CI cache can corrupt gate results but not shipped binaries. Still worth closing before public launch. Fix: do not share build/mod caches between fork-PR runs and trusted runs - disable the cache mount for fork PRs, or namespace the volume by trust level (e.g. a separate throwaway cache for PR runs). Acceptance: fork-PR jobs and trusted `main` jobs no longer read/write the same cache volume.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea#272
No description provided.