Feb 10, 2026·6 min read·9 visits
The `go-git` library, used extensively in the Go ecosystem for Git operations, failed to validate SHA-1 checksums when processing packfiles. This means an attacker (or a bad disk sector) could feed the library corrupted data, and `go-git` would happily commit it to your object database without raising an error. Fixed in v5.16.5.
A fundamental data integrity flaw in the popular `go-git` library allowed for the consumption of corrupted or malicious Git packfiles without detection. By failing to verify checksums in `.pack` and `.idx` files, the library broke the core promise of Git's content-addressable storage model.
If you are writing Go code and need to interact with a Git repository, you aren't going to exec.Command("git", ...) like a barbarian. You use go-git. It's the de facto standard, a pure Go implementation of the Git protocol. It powers everything from Kubernetes GitOps controllers (like Flux) to vulnerability scanners and CI/CD pipelines. It is the silent engine ensuring that the code you think you are deploying is the code you actually have.
But here is the thing about engines: we trust them to verify the fuel quality. Git, at its core, is a content-addressable filesystem. Everything is defined by its hash. If you change a single bit of a file, its hash changes, and the Git Merkle tree rejects it. It is the cryptographic guarantee that makes Git, well, Git.
CVE-2026-25934 is a story about what happens when that guarantee is implemented with a generic TODO where the security check should be. For versions prior to 5.16.5, go-git was essentially accepting packages from strangers without checking the seal. It turns out, reading the checksum at the end of a file and actually comparing it are two very different things.
To understand the gravity of this screw-up, you have to understand the anatomy of a Git transfer. When you run git clone or git fetch, you aren't downloading individual files one by one. You are downloading a Packfile (.pack). This is a compressed stream of objects (commits, trees, blobs).
To keep things speedy, Git also uses an Index file (.idx) to map object IDs to their offsets within that massive packfile. Both of these formats—.pack and .idx—end with a 20-byte SHA-1 trailer. This trailer is the checksum of the entire file's content. It is the digital seal. The protocol dictates: read the data, hash it as you go, and when you hit the end, compare your calculated hash with the trailer.
The vulnerability in go-git was embarrassingly simple: it parsed the file, it read the objects, it may have even read the trailer bytes into a buffer... but it failed to validate that the data actually matched the checksum. It was the security equivalent of a bouncer looking at your ID, seeing it is written in Crayon, and letting you into the club anyway because he didn't feel like reading.
Let's look at the logic flow. In a proper implementation, the pseudo-code for reading a packfile looks something like this:
// The way it SHOULD be
func ReadPackfile(r io.Reader) error {
hasher := sha1.New()
tee := io.TeeReader(r, hasher)
// Process objects from the stream
processObjects(tee)
// Read the 20-byte trailer from the stream
expectedChecksum := make([]byte, 20)
io.ReadFull(r, expectedChecksum)
// The critical step go-git missed:
calculatedChecksum := hasher.Sum(nil)
if !bytes.Equal(calculatedChecksum, expectedChecksum) {
return ErrIntegrityViolation
}
return nil
}In the vulnerable versions of go-git, the logic effectively skipped the bytes.Equal check or the hashing process during specific ingest operations (like fetch or local .idx generation). The code would consume the stream, populate the internal object database, and return nil (success), even if the underlying bytes were garbled.
This isn't just a memory safety issue; it's a logic error that undermines the application's trust model. If your GitOps controller pulls a manifest, and the network corrupts a bit flipping replicas: 1 to replicas: 0 (unlikely but possible), or more maliciously, if an attacker injects a modified object stream, go-git wouldn't notice. The patch in v5.16.5 forces a rigorous checksum comparison for both Packfiles and Index files before finalizing the transaction.
How do we weaponize this? We don't need a complex buffer overflow or ROP chain. We just need to lie.
Imagine a scenario where a developer (or a CI pipeline) clones a repository from an HTTP server. An attacker performs a Man-in-the-Middle (MITM) attack. Usually, this is hard because Git over HTTPS validates the connection, and Git itself validates the data integrity. If the attacker modifies the stream, the checksum fails, and the clone aborts.
With CVE-2026-25934, the data integrity check is gone. The attacker can modify the packfile in transit.
git clone http://insecure-repo.local/app.git using a tool built on go-git..pack file.main.go is malicious, but the Packfile header and structure remain valid enough to parse.go-git reads the corrupted packfile. It ignores the fact that the SHA-1 checksum at the end does not match the hashed content. The malicious main.go is written to the object store.This allows for Cache Poisoning. If go-git caches this repository, subsequent builds will use the corrupted code, even if the network connection is later secured. The repository is now in a zombie state: valid according to go-git, but corrupt according to reality.
While the CVSS score is a modest 4.3, do not let that fool you. In the world of supply chain security, integrity is everything. The impact here falls into two buckets:
git fsck on the server, which nobody does until it is too late.go-git to scan repositories for secrets or vulnerabilities. If an attacker can feed a corrupted packfile that hides the secrets from the scanner (by corrupting the specific object structure) but allows the scanner to finish without error, the tool reports "Clean" when the repo is actually "Dirty".It is a "Low" severity for a web server, but a "High" anxiety inducing bug for a Release Engineer.
The fix is straightforward: upgrade. The maintainers released v5.16.5 which re-introduces the sanity checks that should have been there all along.
go.mod:
go get github.com/go-git/go-git/v5@v5.16.5go-git, the update won't magically fix already-corrupted data on disk. You need to verify them:
# Run this in your git directories
git fsck --full> [!NOTE]
> If git fsck screams at you about "bad sha1" or "corrupt pack", delete the repo and re-clone it with the patched library. Do not try to surgically repair it.
Developers relying on go-git should also consider adding application-level checksum verification if they are handling high-value artifacts, because as we learned today, you can't always trust the library to read the fine print.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
go-git/go-git go-git | < 5.16.5 | 5.16.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-354 |
| Attack Vector | Network |
| CVSS Score | 4.3 (Medium) |
| Impact | Data Integrity Loss |
| Exploit Status | Theoretical / PoC |
| Patch Date | 2026-02-09 |
The product does not validate or incorrectly validates the integrity check value of a message or file.