On October 14, 2026, CVE-2026-73800 (GHSA-r9ch-mqjm-g67v) was published for Gitea. When a trusted base-branch workflow ran for a pull_request_target event, Gitea resolved local reusable workflow files from the fork's head commit. A fork author could edit a referenced reusable workflow and run attacker-controlled code with the repository and organization secrets available to that event.
The pull request gate is meant to stop untrusted code from reaching main. Here the pull request reached the secrets first. A good gate design covers both: what the PR contains, and what the PR can run.
What's affected
- Gitea releases before 1.27.2, with affected releases starting at 1.19.0 per the OSV record. Fixed in 1.27.2.
- The advisory says exploitation requires a base workflow that calls a local reusable workflow.
- The same shape of problem, a trusted workflow pulling in untrusted code, applies to any CI that has a
pull_request_targetstyle trigger.
How to check if you're exposed
gitea --version
grep -rn "pull_request_target" .gitea/workflows .github/workflows 2>/dev/null
grep -rn "uses: \./" .gitea/workflows .github/workflows 2>/dev/null
The first two together show whether a privileged trigger exists, and the third whether it calls a local reusable workflow. Any file that matches both needs a careful read.
What to do now
- Upgrade Gitea to 1.27.2 or later.
- Remove secrets from any workflow that runs on
pull_request_targetunless it genuinely needs them. - Do not check out or execute fork-controlled code in a privileged workflow. Run tests in an unprivileged
pull_requestjob and pass results to a separate privileged job. - Require approval before workflows run on pull requests from first-time or outside contributors.
- Put the workflow directory under CODEOWNERS review.
How this could have been avoided
- No secrets in workflows triggered by forks, so a code execution bug yields nothing.
- Split untrusted build from trusted publish, with artifacts as the only bridge.
- Treat workflow files as code that needs the strictest review path in the repository.
- Scope tokens to the job, with read-only defaults.
Designing the gate
A gate has to be strict enough to matter and quiet enough that nobody bypasses it. Split findings into three tiers.
| Tier | Examples | Gate action |
|---|---|---|
| Block | Known exploited vulnerability in a reachable dependency, committed secret, new unauthenticated route, workflow change with secret access | Fail the check |
| Review | New high severity SAST finding on changed lines, new dependency with install scripts, change to auth code | Require a named reviewer |
| Report | Findings in untouched code, low severity, unreachable dependency issues | Comment, do not block |
Two rules keep SAST useful at the gate. First, scope to the diff: only findings on changed lines can block, so legacy debt does not stop an unrelated fix. Second, triage with reachability. A vulnerable function that your code never calls is a report, and the same function called from a request handler is a block. Safeguard does reachability analysis for this question: whether your code calls the vulnerable function.
Write the triage outcomes down per rule: true positive, accepted risk with an expiry date, or suppressed with a reason. A suppression without a reason or date is how a gate quietly stops working.
Why it matters
Most gate discussion is about the contents of the diff. The Gitea advisory is a reminder that the pipeline is also a target, and that a clean diff can still run a dirty workflow. Review the gate itself as carefully as the code it checks.
Try it yourself
List your workflow files that use a privileged trigger. For each, write down which secrets it can reach and whether it runs any code from the pull request. Then sort your last twenty scanner findings into the three tiers above and see how many would have blocked.
Click through it at your own pace, or open it full screen.
Quick check
Why should only findings on changed lines block a pull request?
Existing debt would otherwise block unrelated changes, and developers would route around the gate.
What was the core problem in CVE-2026-73800?
A trusted base-branch workflow loaded a reusable workflow from the fork's head commit, so fork-controlled code ran with the repository's secrets.