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.
4 steps from start to impact.
Land a client that uses go-git
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.- A deployed application includes
github.com/go-git/go-git/v5beforev5.13.0or a vulnerablev4line - That application performs clone/fetch/pull operations through
go-git
- Software-composition analysis usually identifies the dependency quickly
- Many hosts in a 10,000-endpoint estate will never run
go-gitat all - Library presence does not equal exposure; the Git client code path must actually be reachable
Get the victim to talk to a hostile Git server
- 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
- 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
Serve malicious protocol replies with a custom rogue Git server
- Attacker controls Git protocol responses
- Victim reaches the vulnerable code path during clone/fetch/pull
- 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
Crash or stall the calling service
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.- The vulnerable client lacks sufficient timeouts, memory caps, or supervisor restart controls
- The affected process is operationally important enough that repeated crashes matter
- 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
The supporting signals.
| In-the-wild status | No 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 availability | No 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. |
| EPSS | User-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 status | Not KEV-listed. No known CISA due date or federal emergency patch pressure tied to this CVE. |
| CVSS vector | CVSS: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 versions | github.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 versions | Patch 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 reality | This 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. |
| Disclosure | Published 2025-01-06 via GitHub advisory / CVE record; Go vulnerability entry published 2025-01-07. |
| Reporter / notable downstream use | Reported 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. |
noisgate verdict.
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.
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-gitsits 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.
What to do — in priority order.
- Allowlist Git remotes — Restrict vulnerable applications to approved Git hosts and mirrors only. For a
MEDIUMverdict there is no mitigation SLA; use this as a standing control where patching orv4migration will take time, and keep it in place until remediation lands within 365 days. - 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. - Enforce process resource limits — Put
go-gitconsumers 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. - 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.
- 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
gitCLI does not solve this specific issue because the advisory explicitly says it is ago-gitimplementation problem, not an upstreamgitCLI flaw.
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.
#!/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())
If you remember one thing.
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
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.