← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2025-21614 · CWE-400 · Disclosed 2025-01-06

go-git is a highly extensible git implementation library written in pure Go

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a bad package delivery only if your app willingly drives to the attacker’s warehouse

CVE-2025-21614 is a resource-exhaustion bug in the go-git client library, not the upstream git CLI. Affected code paths exist in github.com/go-git/go-git/v5 before v5.13.0, while v4/gopkg.in/src-d/go-git.v4 remain affected with no maintained fix. The attacker wins by operating or tampering with a Git server and sending crafted protocol replies that make the client burn memory or CPU until the calling process becomes unstable or dies.

The vendor's HIGH score reflects the protocol-level reality that a malicious server can hit an unpatched client with no credentials. In enterprise patch triage, that overstates the fleet risk because this is a *library* bug with availability-only impact: the attacker usually needs the victim app to initiate a Git connection to an attacker-controlled or compromised server, which sharply narrows exposed population compared with a true internet-reachable RCE.

"High on paper, but real-world abuse usually requires the victim to pull from an attacker-controlled Git server."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Land a client that uses go-git

The target must run an application built against vulnerable go-git modules, commonly in CI/CD workers, GitOps controllers, automation platforms, or internal developer tooling. The bug is in the client library, so there is nothing to probe directly on the network like a normal appliance CVE.
Conditions required:
  • A deployed application includes github.com/go-git/go-git/v5 before v5.13.0 or a vulnerable v4 line
  • That application performs clone/fetch/pull operations through go-git
Where this breaks in practice:
  • Software-composition analysis usually identifies the dependency quickly
  • Many hosts in a 10,000-endpoint estate will never run go-git at all
  • Library presence does not equal exposure; the Git client code path must actually be reachable
Detection/coverage: SCA/SBOM scanners have good package-level coverage; network scanners do not.
STEP 02

Get the victim to talk to a hostile Git server

Exploitation requires server-controlled protocol replies, so the attacker needs the victim workload to connect outbound to infrastructure they control or to a legitimate Git server they can tamper with. In practice that means a user-supplied repository URL, a poisoned integration, a compromised mirror, or an already-compromised internal Git service.
Conditions required:
  • Victim can reach the attacker-controlled or compromised Git server over the network
  • The app accepts or is configured with a repository endpoint the attacker can influence
Where this breaks in practice:
  • Many enterprise Git consumers only sync from a short allowlist of trusted hosts
  • Outbound filtering, repo allowlists, and change control often block arbitrary Git endpoints
  • If internal access or config control is needed, this becomes post-initial-access rather than edge exploitation
Detection/coverage: Proxy logs, egress filtering logs, and GitOps/controller audit trails can show unexpected repository hosts; vuln scanners generally cannot validate this prerequisite.
STEP 03

Serve malicious protocol replies with a custom rogue Git server

A hostile Git server or protocol shim sends crafted responses that trigger uncontrolled resource consumption in the vulnerable client. No widely cited public exploit kit was identified in the sources used here, but the primitive is conceptually simple once you control the server side of the exchange.
Conditions required:
  • Attacker controls Git protocol responses
  • Victim reaches the vulnerable code path during clone/fetch/pull
Where this breaks in practice:
  • Lack of broad public weaponization lowers opportunistic abuse
  • Protocol quirks and application-specific timeout behavior can reduce reliability
  • Resource limits in containers or systemd units may cap the blast radius to one process
Detection/coverage: Runtime monitoring may show abnormal memory/CPU spikes or repeated failed syncs; signature-based scanners are unlikely to prove exploitability.
STEP 04

Crash or stall the calling service

Successful exploitation degrades or kills the process using go-git, disrupting sync, build, deployment, or automation workflows. The impact is availability loss in that service context, not code execution or cross-host compromise.
Conditions required:
  • The vulnerable client lacks sufficient timeouts, memory caps, or supervisor restart controls
  • The affected process is operationally important enough that repeated crashes matter
Where this breaks in practice:
  • Supervisors can restart the process automatically
  • Container quotas and cgroups often contain the damage
  • Most enterprises can reroute work or retry jobs, limiting business impact to a narrow workflow
Detection/coverage: Expect OOM kills, restart storms, failed repository syncs, and application-level errors; EDR sees the symptom, not the protocol bug.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo public evidence of active exploitation was found in the sources reviewed, and the CVE is not present in the CISA KEV catalog.
Proof-of-concept availabilityNo mainstream public PoC repo or exploit framework reference surfaced in the advisory chain. That said, exploitation only requires a malicious Git server, so bespoke abuse is plausible for a capable operator.
EPSSUser-supplied EPSS is 0.00228 (~0.228%), which is low. That aligns with a bug that is technically remote but operationally awkward to reach at scale.
KEV statusNot KEV-listed. No known CISA due date or federal emergency patch pressure tied to this CVE.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H scores it as server-to-client network DoS. The important missing context is that the *victim must initiate the Git session*.
Affected versionsgithub.com/go-git/go-git/v5 before v5.13.0; github.com/go-git/go-git/v4 and gopkg.in/src-d/go-git.v4 are listed as affected with no known fixed version in the Go vuln database.
Fixed versionsPatch path is go-git v5.13.0+. For v4, the practical fix is migrate off the unmaintained branch because the project states only the latest minor release is actively supported.
Exposure/scanning realityThis is not meaningfully internet-scannable via Shodan/Censys/FOFA because it is a library inside other software. Exposure is discovered through SBOM/SCA and by finding apps that pull from external or user-influenced Git endpoints.
DisclosurePublished 2025-01-06 via GitHub advisory / CVE record; Go vulnerability entry published 2025-01-07.
Reporter / notable downstream useReported by Ionut Lalu. The project README notes go-git is used by Keybase, Gitea, Pulumi, and as a dependency in major CNCF projects such as Kubernetes Prow and Flux, which matters more than raw host count because control-plane apps can centralize blast radius.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.3/10)

The decisive downgrade factor is attacker position: despite the network CVSS, the attacker usually needs the victim to initiate Git traffic to an attacker-controlled or already-compromised server. That turns a nominally remote DoS into a narrower application-workflow problem with availability-only impact and limited blast radius outside the affected service.

HIGH Affected version ranges and vendor baseline severity
MEDIUM Real-world exposure assumptions across enterprise deployments
MEDIUM Assessment that no public weaponized exploitation is broadly circulating

Why this verdict

  • Downward pressure: attacker-controlled server requirement. The victim has to talk to a hostile or compromised Git endpoint; this is not a spray-the-internet service exploit.
  • Downward pressure: post-initial-access or workflow influence in many environments. If repo URLs are allowlisted or centrally managed, exploitation often implies prior foothold, supply-chain control, or admin influence over CI/GitOps configuration.
  • Downward pressure: availability only. There is no confidentiality, integrity, or code-execution path in the published advisory; the result is process instability or outage.
  • Upward pressure: low protocol complexity once reachable. A malicious server can trigger the bug without credentials, so apps that ingest arbitrary repo URLs remain genuinely exposed.
  • Upward pressure: central tooling concentration. When go-git sits inside a GitOps controller, CI orchestrator, or automation hub, one vulnerable service can disrupt many downstream workflows even though it still does not become a fleet-takeover event.

Why not higher?

It is not HIGH in enterprise reality because the vulnerable component is usually not directly reachable by an unauthenticated internet attacker. The requirement that the victim initiate a Git session to an attacker-controlled server compounds with the availability-only impact and sharply reduces exploitable population versus edge-exposed RCEs or auth bypasses.

Why not lower?

It is not LOW because affected software often runs in important build, deployment, or GitOps paths where repeated crashes can halt delivery pipelines or control-plane sync. The exploit primitive is also straightforward for anyone who can influence repository endpoints, so this is more than theoretical hygiene.

05 · Compensating Control

What to do — in priority order.

  1. Allowlist Git remotes — Restrict vulnerable applications to approved Git hosts and mirrors only. For a MEDIUM verdict there is no mitigation SLA; use this as a standing control where patching or v4 migration will take time, and keep it in place until remediation lands within 365 days.
  2. Block user-supplied repo URLs — If your platform lets tenants, developers, or API callers register arbitrary repositories, add validation and policy gates so only sanctioned domains and protocols are accepted. There is no mitigation SLA for MEDIUM, but this is the highest-value friction reducer because it removes the attacker-controlled-server prerequisite.
  3. Enforce process resource limits — Put go-git consumers behind cgroup/container memory caps, CPU quotas, request timeouts, and supervisor restart thresholds. That will not fix the bug, but it turns a potential controller outage into a single-job or single-pod failure while you remediate within the 365-day window.
  4. Prioritize control-plane consumers — Patch or migrate the instances embedded in GitOps, CI, release automation, and internal developer portals before low-value utility tools. On a 10,000-host estate, the right unit of work is *business-critical workflows using go-git*, not raw endpoint count.
What doesn't work
  • A WAF does not help; the malicious content arrives over Git protocol flows to the client library, not HTTP requests to your web app.
  • EDR alone is not a preventive control here; it may show OOM/restart symptoms, but it does not stop a bad protocol reply from consuming resources.
  • Patching the upstream git CLI does not solve this specific issue because the advisory explicitly says it is a go-git implementation problem, not an upstream git CLI flaw.
06 · Verification

Crowdsourced verification payload.

Run this from an auditor workstation, CI job, or source checkout root of the application that may depend on go-git. Invoke it as python3 check_cve_2025_21614.py /path/to/repo; no admin privileges are needed because it only reads go.mod, go.sum, and vendor/modules.txt.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# check_cve_2025_21614.py
# Detect vulnerable go-git module versions in a Go source tree.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/runtime error

import os
import re
import sys
from typing import Optional, Tuple, List

VULN_MODULES = {
    'github.com/go-git/go-git/v5': ('v5', '5.13.0'),
    'github.com/go-git/go-git/v4': ('v4', None),
    'gopkg.in/src-d/go-git.v4': ('v4', None),
}

SEMVER_RE = re.compile(r'v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$')
REQ_RE = re.compile(r'^\s*(require|replace)\s+([^\s]+)\s+v?([^\s]+)')
MODTXT_RE = re.compile(r'^#\s+([^\s]+)\s+v?([^\s]+)')
GOSUM_RE = re.compile(r'^([^\s]+)\s+v?([^\s]+)')


def normalize_version(v: str) -> Optional[str]:
    v = v.strip()
    if v.endswith('/go.mod'):
        v = v[:-7]
    if not v.startswith('v'):
        v = 'v' + v
    if SEMVER_RE.match(v):
        return v
    return None


def semver_tuple(v: str) -> Optional[Tuple[int, int, int]]:
    m = SEMVER_RE.match(v)
    if not m:
        return None
    return tuple(int(x) for x in m.groups())


def lt(v1: str, v2: str) -> Optional[bool]:
    t1 = semver_tuple(v1)
    t2 = semver_tuple(v2)
    if t1 is None or t2 is None:
        return None
    return t1 < t2


def assess(module: str, version: str) -> Tuple[str, str]:
    nv = normalize_version(version)
    if module not in VULN_MODULES:
        return ('UNKNOWN', f'{module} {version} not in target set')
    branch, fixed = VULN_MODULES[module]
    if branch == 'v4':
        return ('VULNERABLE', f'{module} {version} is on vulnerable v4 line with no known fixed release')
    if nv is None:
        return ('UNKNOWN', f'{module} {version} could not be parsed')
    cmp = lt(nv, 'v' + fixed if not fixed.startswith('v') else fixed)
    if cmp is None:
        return ('UNKNOWN', f'{module} {version} could not be compared')
    if cmp:
        return ('VULNERABLE', f'{module} {nv} < v5.13.0')
    return ('PATCHED', f'{module} {nv} >= v5.13.0')


def parse_go_mod(path: str) -> List[Tuple[str, str, str]]:
    findings = []
    if not os.path.isfile(path):
        return findings
    with open(path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            m = REQ_RE.match(line)
            if m:
                module = m.group(2)
                version = m.group(3)
                if module in VULN_MODULES:
                    findings.append(('go.mod', module, version))
    return findings


def parse_go_sum(path: str) -> List[Tuple[str, str, str]]:
    findings = []
    if not os.path.isfile(path):
        return findings
    with open(path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            m = GOSUM_RE.match(line)
            if m:
                module = m.group(1)
                version = m.group(2)
                if module in VULN_MODULES:
                    findings.append(('go.sum', module, version))
    return findings


def parse_vendor_modules(path: str) -> List[Tuple[str, str, str]]:
    findings = []
    if not os.path.isfile(path):
        return findings
    with open(path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            m = MODTXT_RE.match(line)
            if m:
                module = m.group(1)
                version = m.group(2)
                if module in VULN_MODULES:
                    findings.append(('vendor/modules.txt', module, version))
    return findings


def main() -> int:
    if len(sys.argv) != 2:
        print('UNKNOWN: usage: python3 check_cve_2025_21614.py /path/to/repo')
        return 3

    root = sys.argv[1]
    if not os.path.isdir(root):
        print(f'UNKNOWN: path not found: {root}')
        return 3

    findings = []
    findings.extend(parse_go_mod(os.path.join(root, 'go.mod')))
    findings.extend(parse_go_sum(os.path.join(root, 'go.sum')))
    findings.extend(parse_vendor_modules(os.path.join(root, 'vendor', 'modules.txt')))

    if not findings:
        print('UNKNOWN: no target go-git modules found in go.mod, go.sum, or vendor/modules.txt')
        return 2

    verdicts = []
    for source, module, version in findings:
        status, detail = assess(module, version)
        verdicts.append((status, f'[{source}] {detail}'))

    vulnerable = [d for s, d in verdicts if s == 'VULNERABLE']
    patched = [d for s, d in verdicts if s == 'PATCHED']
    unknown = [d for s, d in verdicts if s == 'UNKNOWN']

    if vulnerable:
        print('VULNERABLE: ' + ' | '.join(vulnerable))
        return 1
    if patched and not unknown:
        print('PATCHED: ' + ' | '.join(patched))
        return 0
    print('UNKNOWN: ' + ' | '.join([d for _, d in verdicts]))
    return 2


if __name__ == '__main__':
    sys.exit(main())
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, do not treat this like an internet-fire RCE. First, use SBOM/SCA or repo inspection to find where go-git is embedded in GitOps, CI, release automation, and developer platforms; then patch v5 consumers to v5.13.0+ and open migration work for any v4 consumers that have no fixed branch. Because this reassessment is MEDIUM, there is noisgate mitigation SLA — go straight to the 365-day remediation window — but high-value control-plane apps that pull from external or user-influenced repositories should be moved to the front of the queue and completed inside the noisgate remediation SLA of <= 365 days.

Sources

  1. GitHub Advisory GHSA-r9px-m959-cxf4
  2. NVD CVE-2025-21614
  3. Go vulnerability database GO-2025-3367
  4. go-git repository README / ecosystem usage
  5. go-git security policy
  6. go-git releases
  7. CISA Known Exploited Vulnerabilities Catalog
  8. FIRST EPSS data documentation
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.