Gitea
CVE-2026-58445
LOW
Severity by source
AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N
Any authenticated user with standard write:issue on their own repo can exploit the oracle, making PR:L more accurate than vendor's PR:H; no integrity or availability impact applies.
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N
Lifecycle Timeline
2DescriptionGitHub Advisory
Summary
The API endpoint DELETE /repos/{owner}/{repo}/issues/{index}/labels/{id} loads the label by ID with a global, unscoped lookup and never verifies the label belongs to the URL's repository (or its owning organization). Because the response status differs by whether the label ID exists anywhere on the instance (204) versus not (422), an authenticated user can use the endpoint as a cross-repository label-ID existence / enumeration oracle, including for labels in repositories and organizations they cannot access.
Severity
- The leaked information is minimal (existence/count of label IDs instance-wide); **no label name, color, or owning
repository is disclosed, and no cross-repository write occurs.**
Affected / patched versions
- Affected: through 1.26.3 (latest at time of report).
- Patched: none yet.
Details
DeleteIssueLabel resolves the label with a global loader and never checks its scope:
// routers/api/v1/repo/issue_label.go (DeleteIssueLabel)
label, err := issues_model.GetLabelByID(ctx, ctx.PathParamInt64("id")) // global, unscopedGetLabelByID (models/issues/label.go) is e.ID(labelID).Get(l) with no repo_id / org_id filter. The handler never verifies label.RepoID == ctx.Repo.Repository.ID (nor the org-label equivalent), and the downstream issue_service.RemoveLabel (services/issue/label.go) only re-checks the doer's write permission on the issue's own repository - never that the label belongs to it.
Every sibling label handler is correctly scoped - GetLabel / EditLabel / DeleteLabel (repo and org) use GetLabelInRepoByID / GetLabelInOrgByID and return 404 for a foreign ID. DeleteIssueLabel is the only outlier.
Why it is only an oracle: deleteIssueLabel (models/issues/issue_label.go) deletes the issue_label row keyed by (issue.ID, label.ID). For a foreign label, no such row exists → the function returns early before any mutation or comment creation. So there is no cross-repo write and no leak of the label's name. But the HTTP status differs:
- label ID exists anywhere on the instance (incl. private repos/orgs) → 204 No Content
- label ID does not exist → 422 (
ErrLabelNotExist)
Label IDs are sequential auto-increment, so this enumerates the instance-wide label population and probes existence of specific IDs across tenant boundaries.
Proof of Concept
Verified end-to-end on a build of the v1.26.3 tag.
alice(private repoalice/secret) creates a label → internal id 1.- Attacker
bob(separate user; public repobob/pubwith issue #1; no access toalice/secret) holds a token
with write:issue on his own repo.
bob DELETE /api/v1/repos/bob/pub/issues/1/labels/1 -> HTTP 204 (alice's PRIVATE label id exists)
bob DELETE /api/v1/repos/bob/pub/issues/1/labels/99999999 -> HTTP 422 (no such label)Differing only by the label ID: 204 vs 422 distinguishes "label ID exists" from "does not exist." bob has zero rights to alice/secret but can still learn label id 1 exists. (Alice's label is untouched - no write.)
Reproduction steps:
- Create two users
alice,bob. Asalice, create a private repo and a label on it (note the labelidfrom
the API response).
- As
bob, create any repo with an issue, and a token withwrite:issue. curl -u bob:$T -X DELETE https://<gitea>/api/v1/repos/bob/pub/issues/1/labels/<alice_label_id>→ 204.curl -u bob:$T -X DELETE https://<gitea>/api/v1/repos/bob/pub/issues/1/labels/99999999→ 422.- The differing status across an ID
bobcannot otherwise see is the oracle.
Impact
Cross-tenant authorization-key bypass producing a label-ID existence/enumeration oracle: an authenticated user can determine whether arbitrary label IDs (including in private repositories and organizations they cannot access) exist, and enumerate the instance-wide label population.
Remediation
Scope the loader like every sibling handler, returning 404 for a label that is not in the URL repository (or its owning organization), so the status no longer distinguishes existence:
// routers/api/v1/repo/issue_label.go - in DeleteIssueLabel, after loading the label
if label.RepoID != ctx.Repo.Repository.ID && label.OrgID != ctx.Repo.Repository.OwnerID {
ctx.APIErrorNotFound()
return
}(Equivalently, resolve via GetLabelInRepoByID and, for org repositories, also accept the repo owner's org labels - mirroring the scoping in GetLabel/EditLabel/DeleteLabel.)
AnalysisAI
Cross-repository label-ID enumeration oracle in Gitea through v1.26.3 allows any authenticated user with write:issue permission on any repository to probe label ID existence across all repositories and organizations on the instance - including private ones they cannot access. The flaw arises because the DELETE /repos/{owner}/{repo}/issues/{index}/labels/{id} API handler performs a global, unscoped label lookup and leaks existence via differential HTTP status codes (204 vs. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata
Vulnerability AssessmentAI
| Exploitation | The attacker must be an authenticated Gitea user holding write:issue permission on at least one repository they control, and must have network access to the Gitea REST API. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | The provided CVSS 3.1 base score of 2.7 (AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N) understates real-world exploitability: the vendor assigned PR:H, but any regular authenticated Gitea user holding write:issue on any repository they own - a standard, user-level permission - can trigger the oracle without administrative access, suggesting PR:L is more appropriate (raising the base score to approximately 4.3). … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | A working proof of concept was verified against Gitea v1.26.3 by the reporter. An authenticated attacker (bob) with write:issue on any repository they control sends DELETE /api/v1/repos/bob/pub/issues/1/labels/{target_id} with sequential label IDs; a 204 response confirms the label ID exists somewhere on the instance (including in alice's private repository), while a 422 confirms it does not. … |
| Remediation | Upgrade Gitea to v1.27.0 or later; this release scopes the label lookup in DeleteIssueLabel to the URL repository (or its owning organization), mirroring all sibling handlers, so the HTTP status no longer distinguishes cross-tenant label existence. … Detailed patch versions, workarounds, and compensating controls in full report. |
Threat intelligence, references, and detailed analysis are available after sign-in.
Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r
Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp
The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir
Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea
Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec
models/repo_mirror.go in Gitea before 1.7.6 and 1.8.x before 1.8-RC3 mishandles mirror repo URL settings, leading to rem
An issue was discovered in Gitea through 1.11.5. Rated high severity (CVSS 7.5), this vulnerability is remotely exploita
Missing Authorization in GitHub repository go-gitea/gitea prior to 1.16.4. Rated high severity (CVSS 7.1), this vulnerab
Open Redirect on login in GitHub repository go-gitea/gitea prior to 1.16.5. Rated medium severity (CVSS 6.1), this vulne
Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any
Branch-protection bypass in Gitea's self-hosted Git server (all versions before 1.26.0) allows a user with push access t
Migration transport protections in Gitea are bypassed for Git LFS operations, affecting all self-hosted instances before
Same weakness CWE-203 – Observable Discrepancy
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-pgqf-926r-548m