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 Vendor (https://github.com/go-gitea/gitea).
CVSS VectorVendor: https://github.com/go-gitea/gitea
Lifecycle Timeline
2DescriptionCVE.org
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. 422). No label names, colors, repository associations, or content are disclosed, and no cross-repository writes occur; however, a working proof of concept was verified against v1.26.3 and a fix is available in v1.27.0. No public exploit identified at time of analysis beyond the verified PoC in the advisory.
Technical ContextAI
Gitea is a self-hosted Git service written in Go (package: go/code.gitea.io/gitea). The vulnerable handler, DeleteIssueLabel in routers/api/v1/repo/issue_label.go, calls issues_model.GetLabelByID - implemented as a bare e.ID(labelID).Get(l) ORM query with no repo_id or org_id filter in models/issues/label.go. Every sibling handler (GetLabel, EditLabel, DeleteLabel for both repo and org contexts) correctly uses scoped variants GetLabelInRepoByID or GetLabelInOrgByID, returning 404 for foreign label IDs. DeleteIssueLabel is the sole outlier. The downstream service layer (issue_service.RemoveLabel) only validates the doer's write permission on the issue's own repository and never checks that the label belongs to that repository. The root cause is CWE-203 (Observable Discrepancy): differential HTTP responses (204 No Content on global ID hit; 422 ErrLabelNotExist on miss) expose a side-channel. Because label IDs are sequential auto-increments, an attacker can trivially enumerate the entire instance-wide label population with linear scanning.
RemediationAI
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. See the advisory at https://github.com/go-gitea/gitea/security/advisories/GHSA-pgqf-926r-548m and the release at https://github.com/go-gitea/gitea/releases/tag/v1.27.0. If immediate patching is not feasible, operators can restrict API access to trusted users only by disabling public registration (reducing the attacker pool to known accounts) or placing the Gitea API behind an authentication proxy; neither eliminates the oracle for existing authenticated users but reduces exposure surface. There is no configuration toggle that disables only the DeleteIssueLabel endpoint without patching.
Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi
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
Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any
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
Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica
Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran
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
Same weakness CWE-203 – Observable Discrepancy
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-58172
GHSA-pgqf-926r-548m