GHSA-8p9h-49rc-qgxj Gitea: Repository Visibility Manipulation via Git Push Options
Repository Visibility Manipulation via Git Push Options| Field | Value |
|-------|-------|
| **Affected File** | `routers/private/hook_post_receive.go` |
| **Affected Function** | `HookPostReceive()` |
| **Affected Lines** | 173–225 |
| **Prerequisite** | Attacker must have owner-level or admin collaborator access to the target repository |DescriptionGitea's post-receive git hook handler processes git push options — key-value pairs transmitted by a client during `git push` using the `-o` flag. Two undocumented push options, `repo.private` and `repo.template`, allow any user with repository owner or admin-collaborator access to toggle the visibility (`private/public`) and template status of a repository as a side effect of a normal git push.This capability was originally intended solely for the "push-to-create" feature (automatically creating a repo on first push). However, the options are processed without restriction on already-existing repositories, and — critically — the visibility change bypasses every control that a proper settings change would trigger:No entry written to the repository's audit/activity logNo webhook event fired (`repository` event with `visibility_changed` action)No org-level notification to ownersNo team permission re-calculationNo email alert to watchersThe database update uses `UpdateRepositoryColsNoAutoTime`, which also suppresses the `updated_at` timestamp changeVulnerable Code**`routers/private/hook_post_receive.go:173–225`**The push option constants are defined in `modules/private/pushoptions.go:18–19`:Attack Scenario**Scenario A — Insider threat / rogue admin collaborator**An organization grants a contractor repo admin access to contribute to a private repository containing proprietary source code. The contractor, before their access is revoked, makes a private repo public for several minutes — long enough to clone, archive, or index the content — then makes it private again. The action leaves no audit trail distinguishable from a normal git push.**Scenario B — Supply-chain template poisoning**A repository marked as a template is used by CI/CD pipelines to generate new project repositories. An admin collaborator uses `repo.template=false` to silently remove the template designation, then makes changes to the repo's content, re-marks it as a template with `repo.template=true`, and waits for downstream consumers to regenerate projects from the now-backdoored template. The `updated_at` timestamp is unchanged due to `UpdateRepositoryColsNoAutoTime`, making diff-detection harder.Step-by-Step Reproduction**Prerequisites:**A Gitea user account with either owner or admin-collaborator access to a private repository`git` client with push access to the repository**Step 1 — Confirm the target repository is private****Step 2 — Clone the repository****Step 3 — Make any commit** *(the push option rides on a real push)***Step 4 — Execute the exploit push****Step 5 — Verify the repository is now public****Step 6 — Restore and cover tracks**Re-make it private in the same session, leaving no visible audit trailThe repository activity feed shows only two normal push events. The visibility change is invisible.**Verification: confirm no activity log entry**Impact Details| Impact | Description |
|--------|-------------|
| **Data exfiltration** | Private source code, CI/CD secrets in plain-text files, environment configs become publicly cloneable for the window the repo is public |
| **No audit trail** | `UpdateRepositoryColsNoAutoTime` suppresses the `updated_at` change; no activity log entry; no webhook; no notification |
| **Supply chain** | Combined with `repo.template=true/false`, an attacker can silently rotate repository template status, affecting all downstream repositories that generate from this template |
| **Scope** | Affects all repos where the attacker has admin-collaborator access — not only repos they own |Recommended Fix**Option 1 (preferred) — Remove the options from post-receive hook entirely.** The `repo.private` and `repo.template` push options were designed for the push-to-create flow and have no legitimate use on existing repositories. They should be gated with:**Option 2 — Route through the full visibility-change service** so that audit events, webhooks, and team re-syncs are triggered:Where `UpdateRepositoryVisibility` fires the `repository` webhook event and writes an activity log entry.
CVSS v3.1 score: 7.1