GHSA-3PWW-VCVM-3GMJ

Vulnerability from github – Published: 2026-07-21 20:30 – Updated: 2026-07-21 20:30
VLAI
Summary
Gitea: API access token scope enforcement bypass on repository RSS/Atom feed endpoints leaks private repository commit data
Details

Summary

A Gitea personal access token (PAT) restricted to a non-repository scope (e.g. read:issue) can read the commit history of any private repository the token owner can access, via the repository RSS/Atom feed endpoints. The same token is correctly denied (403) on /raw, /media, /archive, and the contents API. It leaks commit SHAs, full commit messages (which frequently contain secrets and internal context), and committer name + email.

Details

Gitea enforces PAT scope on repository-content endpoints via checkDownloadTokenScope() (added in PR #37698, extended to the archive endpoint by the CVE-2026-20706 fix in 1.26.2). The RSS/Atom feed handlers were never included: they (a) opt into PAT auth via webAuth.AllowBasic, (b) serve private-repo content, but (c) never call checkDownloadTokenScope().

Affected handlers (all carry AllowBasic, none call the scope check): - RenderBranchFeedRSS/Atom - routers/web/feed/render.go (last 10 commits: SHA, title, full message, committer name + email) - ShowFileFeed - routers/web/feed/file.go (per-file commit history) - repo activity feed /{owner}/{repo}.rss / .atom - TagsListFeedRSS/Atom, ReleasesFeedRSS/Atom - routers/web/repo/release.go

Root cause: routers/web/web.go registers the feed routes with webAuth.AllowBasic so a PAT authenticates, but the unit-permission middleware only checks the user's access, not the token's scope. checkDownloadTokenScope (routers/web/repo/download.go and the archive Download in repo.go) exists to close exactly that gap and is absent from the feed handlers. Same class as the recently fixed download/archive bypasses (GHSA-cr4g-f395-h25h / CVE-2026-20706); the feeds are the surface those fixes missed.

PoC

Tested on gitea/gitea:1.26.2-rootless, confirmed present at main HEAD (9608cc2, 2026-06-13). 1. User carol owns private repo carol/priv with a commit (message: "add confidential secret"). 2. Create a PAT scoped to issues only, no repository scope: curl -u carol:PASS -X POST $HOST/api/v1/users/carol/tokens -d '{"name":"t","scopes":["read:issue"]}' 3. Scope-enforcing sibling correctly denies it: curl -u carol:$TOK $HOST/carol/priv/raw/branch/main/secret.txt -> 403 4. The feed leaks private data with the same token: curl -u carol:$TOK $HOST/carol/priv/rss/branch/main -> 200, returns <description>add confidential secret ... this commit message itself is sensitive</description>

Auth (same read:issue token) /raw/branch/main/secret.txt /rss/branch/main
anonymous - 404 (private repo hidden)
invalid token - 401
read:issue token (no repo scope) 403 (scope enforced) 200 + private commit data
full-scope token 200 200 (legitimate)

Impact

PATs are routinely issued narrowly and handed to third-party bots, CI jobs, or chat integrations that are meant to have no code access. This bypass lets such a token exfiltrate private-repository commit history (messages often hold secrets, ticket refs, internal context) and committer emails for every repository the owner can read. Confidentiality only; no integrity or availability impact. Preconditions: a valid PAT of any non-repository scope owned by a user with read access to the target private repo, and feeds enabled (Other.EnableFeed, default ON).

Suggested fix: call checkDownloadTokenScope(ctx) at the start of each feed handler (mirroring download.go). Longer-term, enforce repository token scope in a single route-group middleware wherever AllowBasic / AllowOAuth2 is set on a repo-content route, so the next added endpoint cannot miss it.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.26.2"
      },
      "package": {
        "ecosystem": "Go",
        "name": "code.gitea.io/gitea"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.26.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-27761"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-21T20:30:11Z",
    "nvd_published_at": "2026-07-03T21:16:58Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\nA Gitea personal access token (PAT) restricted to a non-repository scope (e.g. `read:issue`) can read the commit history of any private repository the token owner can access, via the repository RSS/Atom feed endpoints. The same token is correctly denied (403) on `/raw`, `/media`, `/archive`, and the contents API. It leaks commit SHAs, full commit messages (which frequently contain secrets and internal context), and committer name + email.\n\n### Details\nGitea enforces PAT scope on repository-content endpoints via `checkDownloadTokenScope()` (added in PR #37698, extended to the archive endpoint by the CVE-2026-20706 fix in 1.26.2). The RSS/Atom feed handlers were never included: they (a) opt into PAT auth via `webAuth.AllowBasic`, (b) serve private-repo content, but (c) never call `checkDownloadTokenScope()`.\n\nAffected handlers (all carry `AllowBasic`, none call the scope check):\n- `RenderBranchFeedRSS/Atom` - `routers/web/feed/render.go` (last 10 commits: SHA, title, full message, committer name + email)\n- `ShowFileFeed` - `routers/web/feed/file.go` (per-file commit history)\n- repo activity feed `/{owner}/{repo}.rss` / `.atom`\n- `TagsListFeedRSS/Atom`, `ReleasesFeedRSS/Atom` - `routers/web/repo/release.go`\n\nRoot cause: `routers/web/web.go` registers the feed routes with `webAuth.AllowBasic` so a PAT authenticates, but the unit-permission middleware only checks the user\u0027s access, not the token\u0027s scope. `checkDownloadTokenScope` (`routers/web/repo/download.go` and the archive `Download` in `repo.go`) exists to close exactly that gap and is absent from the feed handlers. Same class as the recently fixed download/archive bypasses (GHSA-cr4g-f395-h25h / CVE-2026-20706); the feeds are the surface those fixes missed.\n\n### PoC\nTested on `gitea/gitea:1.26.2-rootless`, confirmed present at `main` HEAD (9608cc2, 2026-06-13).\n1. User `carol` owns private repo `carol/priv` with a commit (message: \"add confidential secret\").\n2. Create a PAT scoped to issues only, no repository scope:\n   `curl -u carol:PASS -X POST $HOST/api/v1/users/carol/tokens -d \u0027{\"name\":\"t\",\"scopes\":[\"read:issue\"]}\u0027`\n3. Scope-enforcing sibling correctly denies it:\n   `curl -u carol:$TOK $HOST/carol/priv/raw/branch/main/secret.txt`  -\u003e  403\n4. The feed leaks private data with the same token:\n   `curl -u carol:$TOK $HOST/carol/priv/rss/branch/main`  -\u003e  200, returns\n   `\u003cdescription\u003eadd confidential secret ... this commit message itself is sensitive\u003c/description\u003e`\n\n| Auth (same read:issue token) | /raw/branch/main/secret.txt | /rss/branch/main |\n|---|---|---|\n| anonymous | - | 404 (private repo hidden) |\n| invalid token | - | 401 |\n| read:issue token (no repo scope) | 403 (scope enforced) | 200 + private commit data |\n| full-scope token | 200 | 200 (legitimate) |\n\n### Impact\nPATs are routinely issued narrowly and handed to third-party bots, CI jobs, or chat integrations that are meant to have no code access. This bypass lets such a token exfiltrate private-repository commit history (messages often hold secrets, ticket refs, internal context) and committer emails for every repository the owner can read. Confidentiality only; no integrity or availability impact. Preconditions: a valid PAT of any non-repository scope owned by a user with read access to the target private repo, and feeds enabled (`Other.EnableFeed`, default ON).\n\nSuggested fix: call `checkDownloadTokenScope(ctx)` at the start of each feed handler (mirroring `download.go`). Longer-term, enforce repository token scope in a single route-group middleware wherever `AllowBasic` / `AllowOAuth2` is set on a repo-content route, so the next added endpoint cannot miss it.",
  "id": "GHSA-3pww-vcvm-3gmj",
  "modified": "2026-07-21T20:30:11Z",
  "published": "2026-07-21T20:30:11Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/security/advisories/GHSA-3pww-vcvm-3gmj"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27761"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/pull/38147"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/commit/9e84deb969aff5c1115c2984e41250f28c78451f"
    },
    {
      "type": "WEB",
      "url": "https://blog.gitea.com/release-of-1.26.3-and-1.26.4"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/go-gitea/gitea"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/releases/tag/v1.26.3"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Gitea: API access token scope enforcement bypass on repository RSS/Atom feed endpoints leaks private repository commit data"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…