Skip to main content
VulnerabilityPublished

CVE-2026-58437: Gitea · Repository Visibility Manipulation via Git Push Options

Affects code.gitea.io/giteaSource GCVE Database

0
Compare
Authoritative record:CVE-2026-58437· High 7.1

Not known to be exploited · CVSS 7.1 High : upgrade to 1.27.0 soon.

Data as of 30 Oct 2026, 11:52 UTC

CVSS 3.1 base score
7.1High
How severe the flaw is on the 0–10 CVSS scale.
EPSS
0.3% chance of exploitation in 30 days – higher than 27% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
Patch
Available – fixed in 1.27.0
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N

Exploitation evidence

t = 0 is the CVE’s publication on cve.org

  1. Disclosed: evidence found.

    CVE published 13 Aug 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponized: evidence found.

    CISA SSVC: proof of concept public

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

Timeline

  1. CVE ID reserved

    by Gitea Limited

    , 44 days before disclosure

  2. CVE published

    on cve.org

    , t = 0

  3. CISA SSVC assessment

    proof of concept public

    , 26 hours after disclosure

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), CISA SSVC, the Metasploit, Exploit-DB and nuclei-templates index as of 30 Oct 2026.

What to do

Upgrade code.gitea.io/gitea to 1.27.0.

Affected software

ProductAffectedFixed in
Gocode.gitea.io/giteaAffected
  • <1.27.0
Fixed in
  • 1.27.0

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

Description

### 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 | --- #### Description Gitea'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 log - No webhook event fired (`repository` event with `visibility_changed` action) - No org-level notification to owners - No team permission re-calculation - No email alert to watchers - The database update uses `UpdateRepositoryColsNoAutoTime`, which also suppresses the `updated_at` timestamp change --- #### Vulnerable Code **`routers/private/hook_post_receive.go:173–225`** ```go isPrivate := opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate) // "repo.private" isTemplate := opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate) // "repo.template" if isPrivate.Has() || isTemplate.Has() { // ... loads repo and verifies pusher is owner or admin ... if !perm.IsOwner() && !perm.IsAdmin() { ctx.JSON(http.StatusNotFound, ...) return } // FIXME: these options are not quite right, for example: changing visibility // should do more works than just setting the is_private flag // These options should only be used for "push-to-create" if isPrivate.Has() && repo.IsPrivate != isPrivate.Value() { // TODO: it needs to do more work repo.IsPrivate = isPrivate.Value() repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private") // ^^^ bypasses updated_at timestamp, audit trail suppressed } if isTemplate.Has() && repo.IsTemplate != isTemplate.Value() { repo.IsTemplate = isTemplate.Value() repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_template") } } ``` The push option constants are defined in `modules/private/pushoptions.go:18–19`: ```go GitPushOptionRepoPrivate = "repo.private" GitPushOptionRepoTemplate = "repo.template" ``` --- #### 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** ```bash git clone http://USER:PASSWORD@<gitea-host>/OWNER/REPO.git /tmp/target-repo cd /tmp/target-repo ``` --- **Step 3 — Make any commit** *(the push option rides on a real push)* ```bash echo "$(date)" >> .gitkeep git add .gitkeep git commit -m "routine update" ``` --- **Step 4 — Execute the exploit push** ```bash # Make the repository public git push http://USER:PASSWORD@<gitea-host>/OWNER/REPO.git main \ -o repo.private=false # The push completes with a normal success message: # remote: Processed 1 references in total # To http://<gitea-host>/OWNER/REPO.git # abc1234..def5678 main -> main ``` --- **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 trail The repository activity feed shows only two

References

Community reviews

Loading reviews…