CVE-2026-50105
mediumpriority · track · 0OT risk · elevatedEPSS 0.004 · 30 %ileDEEURUUSEPSS · scored 2026-10-27
RSS/Atom feed handlers bypass API-token scope & public-only confinement (incomplete fix of #37698)
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode
ssvc
A stakeholder decision — act, attend, or track — from exploitation and impact.
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-6cqf-375w-639gGitea: RSS/Atom feed handlers bypass API-token scope & public-only confinement (incomplete fix of #37698)2026-07-21ghsa · osvUS
### Summary Gitea's RSS/Atom feed handlers accept API-token Basic auth but perform **no token-scope or public-only enforcement**. A personal access token that is correctly blocked (HTTP 403) from a private repository on `/raw`, `/media`, `/archive`, and `/releases/download/...` — because it is marked *public-only* or lacks the `repository` scope category — still returns that repository's private content through the feed routes. This is a token-confinement bypass and appears to be an incomplete fix of #37698, which added that scope enforcement to the download handlers but not to the sibling feed handlers. This is **not** a cross-user access bug: the requesting account must still legitimately have repo read access (`RepoAssignment` + `reqUnitCodeReader` are enforced). What is bypassed is the guarantee that a *confined* token cannot reach private content — which is exactly the property #37698 was shipped to provide for downloads, and which matters when such a token is handed to a third-party service/CI, leaked, or used in a lower-trust integration. ### Details #37698 added `context.CheckTokenScopes` / `CheckRepoScopedToken` (`services/context/permission.go`) to the raw / media / archive / attachment download handlers, so a public-only or wrong-scope-category token cannot read private-repo content even when the owning user otherwise has access. The feed handlers are registered with `webAuth.AllowBasic` (so they accept token Basic auth) but call no scope / public-only check. A grep for `CheckTokenScopes` / `CheckRepoScopedToken` / `IsApiToken` across `routers/web/feed/` and the release feed handlers returns nothing. Affected routes (all token-reachable via `webAuth.AllowBasic`, none call the scope check): | Route | Handler | Private data exposed | |---|---|---| | `GET /{owner}/{repo}.rss` / `.atom` | `repo.Home` → `handleRepoHomeFeed` (`view_home.go`) | last-10 commits: SHA, full message, author name + email | | `GET /{owner}/{repo}/rss/branch/*`, `/atom/branch/*` | `feed.RenderBranchFeed*` → `ShowBranchFeed` (`routers/web/feed/branch.go`) | same commit data, any branch | | `GET /{owner}/{repo}/releases.rss` / `.atom` | `ReleasesFeedRSS/Atom` (`routers/web/repo/release.go`) → `ShowReleaseFeed` | private release names, notes, descriptions | | `GET /{owner}/{repo}/tags.rss` / `.atom` | `TagsListFeedRSS/Atom` → `ShowReleaseFeed` | private tag names + messages | | `GET /{user}.rss` / `.atom` | `showUserFeed` (`routers/web/feed/profile.go`), `includePrivate = self \|\| admin` | the token owner's private cross-repo activity stream | Inconsistency that pins this down: the branch-feed routes sit in the **same route group** as `/raw`, `/media`, `/archive` (all of which call `checkDownloadTokenScope`), and `releases.rss` sits next to `/releases/attachments/{uuid}` and `/releases/download/...` (both go through `ServeAttachment` → scope check). Only the feeds were missed. The API equivalents (`ListReleases` / `ListTags`) are scope-gated via `tokenRequiresScopes`. Two distinct confinement bypasses: 1. **Public-only bypass.** A token created with the *public-only* option is blocked (403) from a private repo on `/raw`, `/archive`, `/releases/download/...`, but returns private commit / release data via `.../releases.rss`, `.../rss/branch/*`, `/{owner}/{repo}.rss`, and the owner's private activity via `/{user}.rss`. 2. **Scope-category bypass.** A token scoped to only e.g. `read:issue` (no `read:repository`) is rejected by the download handlers but reads repository commit/release content via the feeds. ### PoC Verified live against the official `gitea/gitea:1.26.2` Docker image (sqlite, feeds enabled). Setup: non-admin user `alice`; private repo `alice/secret` with a commit `"SECRET-COMMIT-MARKER ..."` (file `secret.txt`) and a release `"Private Release"` / body `"SECRET-RELEASE-MARKER ..."`. Two confined personal access tokens, both sent via HTTP Basic so the auth method is identical across download and feed — only the route differs: - Token A: scopes `["public-only", "read:repository"]` - Token B: scopes `["read:issue"]` ```bash # Token A — download is correctly blocked, feeds leak private content: curl -u alice:$TOKEN_A https://<host>/alice/secret/raw/branch/main/secret.txt # => 403 (fix works) curl -u alice:$TOKEN_A https://<host>/alice/secret/rss/branch/main # => 200, <title>SECRET-COMMIT-MARKER ...</title> curl -u alice:$TOKEN_A https://<host>/alice/secret/releases.rss # => 200, SECRET-RELEASE-MARKER ... curl -u alice:$TOKEN_A "https://<host>/alice.rss" # => 200, private activity # Token B — wrong scope category, same split: curl -u alice:$TOKEN_B https://<host>/alice/secret/raw/branch/main/secret.txt # => 403 curl -u alice:$TOKEN_B https://<host>/alice/secret/rss/branch/main # => 200, private commit leaked ``` Anonymous baseline returns 404 on the repo feeds (data is genuinely private) and a marker-free 200 on `/alice.rss` (public activity only) — confirming the leak is gated only by the missing token check. ### Impact Information disclosure of private **commit metadata** (SHA, message, author name+email), **release/tag notes**, and the owner's **private activity stream**, to the holder of a confined token that was specifically configured *not* to reach private content. Not raw file blobs (feeds don't serve file contents). Requires a token belonging to an account that already has repo read access, so the realistic threat is a leaked / shared / lower-trust token rather than an anonymous attacker — which is precisely the threat model #37698 addressed for downloads. ### Suggested remediation Add a token-scope check at the top of each feed handler, mirroring `checkDownloadTokenScope`: ```go if context.CheckRepoScopedToken(ctx, ctx.Repo.Repository, auth_model.Read); ctx.Written() { return } ``` for `ShowBranchFeed`, `ShowRepoFeed`, `ShowFileFeed`, `ShowReleaseFeed` (repo feeds). For the user feed, gate `includePrivate` behind a non-public-only token (or require the `user` / `repository` scope) so a confined token can't pull private activity.
- GO-2026-6036Gitea: RSS/Atom feed handlers bypass API-token scope & public-only confinement (incomplete fix of #37698) in gitea.dev2026-07-22vendor · osvUS
Gitea: RSS/Atom feed handlers bypass API-token scope & public-only confinement (incomplete fix of #37698) 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-6cqf-375w-639genisa_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-6cqf-375w-639gnvd
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan