Kestrel
대시보드로 돌아가기
CVE-2026-50105MEDIUM· 4.3GHSA대응게시일: 2026. 07. 21.수정일: 2026. 07. 21.

Gitea: RSS/Atom feed handlers bypass API-token scope & public-only confinement (incomplete fix of #37698)

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
4.3medium

이론적 심각도 점수

EPSS

예측 데이터 없음

KEV
미등재

실측 악용 기록 없음

권장 대응 기한60일 이내CISA SSVC 기준

계획된 패치 주기 내 조치(60일 이내)

외부 노출· KEV 미등재 · 자동화 어려움 · 부분 영향 · 외부 노출

CVSS 벡터 · 메트릭

악용 경로
공격 벡터네트워크
공격 복잡도낮음
필요 권한낮음
사용자 상호작용불필요
범위불변
영향
기밀성 영향낮음
무결성 영향없음
가용성 영향없음
버전별 점수
CVSS 3.14.3MODERATE
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

상세 설명

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):

RouteHandlerPrivate data exposed
GET /{owner}/{repo}.rss / .atomrepo.HomehandleRepoHomeFeed (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 / .atomReleasesFeedRSS/Atom (routers/web/repo/release.go) → ShowReleaseFeedprivate release names, notes, descriptions
GET /{owner}/{repo}/tags.rss / .atomTagsListFeedRSS/AtomShowReleaseFeedprivate tag names + messages
GET /{user}.rss / .atomshowUserFeed (routers/web/feed/profile.go), includePrivate = self || adminthe 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
1# Token A — download is correctly blocked, feeds leak private content:
2curl -u alice:$TOKEN_A https://<host>/alice/secret/raw/branch/main/secret.txt # => 403 (fix works)
3curl -u alice:$TOKEN_A https://<host>/alice/secret/rss/branch/main # => 200, <title>SECRET-COMMIT-MARKER ...</title>
4curl -u alice:$TOKEN_A https://<host>/alice/secret/releases.rss # => 200, SECRET-RELEASE-MARKER ...
5curl -u alice:$TOKEN_A "https://<host>/alice.rss" # => 200, private activity
6
7# Token B — wrong scope category, same split:
8curl -u alice:$TOKEN_B https://<host>/alice/secret/raw/branch/main/secret.txt # => 403
9curl -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:

text
1if context.CheckRepoScopedToken(ctx, ctx.Repo.Repository, auth_model.Read); ctx.Written() {
2 return
3}

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.

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.