diff -Nru go-git.orig/v5/plumbing/format/idxfile/decoder.go go-git/v5/plumbing/format/idxfile/decoder.go
--- go-git.orig/v5/plumbing/format/idxfile/decoder.go	2020-10-14 14:58:29.000000000 +0200
+++ go-git/v5/plumbing/format/idxfile/decoder.go	2026-02-24 11:53:45.529730311 +0100
@@ -1,9 +1,11 @@
 package idxfile
 
 import (
-	"bufio"
 	"bytes"
+	"crypto"
+	"encoding/hex"
 	"errors"
+	"fmt"
 	"io"
 
 	"github.com/go-git/go-git/v5/utils/binary"
@@ -24,12 +26,15 @@
 
 // Decoder reads and decodes idx files from an input stream.
 type Decoder struct {
-	*bufio.Reader
+	io.Reader
+	h hash.Hash
 }
 
 // NewDecoder builds a new idx stream decoder, that reads from r.
 func NewDecoder(r io.Reader) *Decoder {
-	return &Decoder{bufio.NewReader(r)}
+	h := hash.New(crypto.SHA1)
+	tr := io.TeeReader(r, h)
+	return &Decoder{tr, h}
 }
 
 // Decode reads from the stream and decode the content into the MemoryIndex struct.
@@ -44,7 +49,7 @@
 		readObjectNames,
 		readCRC32,
 		readOffsets,
-		readChecksums,
+		readPackChecksum,
 	}
 
 	for _, f := range flow {
@@ -53,11 +58,21 @@
 		}
 	}
 
+	actual := d.h.Sum(nil)
+	if err := readIdxChecksum(idx, d); err != nil {
+		return err
+	}
+
+	if !bytes.Equal(actual, idx.IdxChecksum[:]) {
+		return fmt.Errorf("%w: checksum mismatch: %q instead of %q",
+			ErrMalformedIdxFile, hex.EncodeToString(idx.IdxChecksum[:]), hex.EncodeToString(actual))
+	}
+
 	return nil
 }
 
 func validateHeader(r io.Reader) error {
-	var h = make([]byte, 4)
+	h := make([]byte, 4)
 	if _, err := io.ReadFull(r, h); err != nil {
 		return err
 	}
@@ -164,11 +179,15 @@
 	return nil
 }
 
-func readChecksums(idx *MemoryIndex, r io.Reader) error {
+func readPackChecksum(idx *MemoryIndex, r io.Reader) error {
 	if _, err := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil {
 		return err
 	}
 
+	return nil
+}
+
+func readIdxChecksum(idx *MemoryIndex, r io.Reader) error {
 	if _, err := io.ReadFull(r, idx.IdxChecksum[:]); err != nil {
 		return err
 	}
diff -Nru go-git.orig/v5/plumbing/format/packfile/parser.go go-git/v5/plumbing/format/packfile/parser.go
--- go-git.orig/v5/plumbing/format/packfile/parser.go	2020-10-14 14:58:29.000000000 +0200
+++ go-git/v5/plumbing/format/packfile/parser.go	2026-02-24 11:57:58.804195446 +0100
@@ -47,7 +47,6 @@
 	oiByHash   map[plumbing.Hash]*objectInfo
 	oiByOffset map[int64]*objectInfo
 	hashOffset map[plumbing.Hash]int64
-	checksum   plumbing.Hash
 
 	cache *cache.BufferLRU
 	// delta content by offset, only used if source is not seekable
@@ -133,28 +132,27 @@
 // Parse start decoding phase of the packfile.
 func (p *Parser) Parse() (plumbing.Hash, error) {
 	if err := p.init(); err != nil {
-		return plumbing.ZeroHash, err
+		return plumbing.ZeroHash, wrapEOF(err)
 	}
 
 	if err := p.indexObjects(); err != nil {
-		return plumbing.ZeroHash, err
+		return plumbing.ZeroHash, wrapEOF(err)
 	}
 
-	var err error
-	p.checksum, err = p.scanner.Checksum()
+	checksum, err := p.scanner.Checksum()
 	if err != nil && err != io.EOF {
-		return plumbing.ZeroHash, err
+		return plumbing.ZeroHash, wrapEOF(err)
 	}
 
 	if err := p.resolveDeltas(); err != nil {
-		return plumbing.ZeroHash, err
+		return plumbing.ZeroHash, wrapEOF(err)
 	}
 
-	if err := p.onFooter(p.checksum); err != nil {
-		return plumbing.ZeroHash, err
+	if err := p.onFooter(checksum); err != nil {
+		return plumbing.ZeroHash, wrapEOF(err)
 	}
 
-	return p.checksum, nil
+	return checksum, nil
 }
 
 func (p *Parser) init() error {
@@ -205,7 +203,7 @@
 			if !ok {
 				// can't find referenced object in this pack file
 				// this must be a "thin" pack.
-				parent = &objectInfo{ //Placeholder parent
+				parent = &objectInfo{ // Placeholder parent
 					SHA1:        oh.Reference,
 					ExternalRef: true, // mark as an external reference that must be resolved
 					Type:        plumbing.AnyObject,
@@ -422,6 +420,13 @@
 	return nil
 }
 
+func wrapEOF(err error) error {
+	if err == io.ErrUnexpectedEOF || err == io.EOF {
+		return fmt.Errorf("%w: %w", ErrMalformedPackFile, err)
+	}
+	return err
+}
+
 func applyPatchBase(ota *objectInfo, data, base []byte) ([]byte, error) {
 	patched, err := PatchDelta(base, data)
 	if err != nil {
@@ -442,15 +447,6 @@
 	return patched, nil
 }
 
-func getSHA1(t plumbing.ObjectType, data []byte) (plumbing.Hash, error) {
-	hasher := plumbing.NewHasher(t, int64(len(data)))
-	if _, err := hasher.Write(data); err != nil {
-		return plumbing.ZeroHash, err
-	}
-
-	return hasher.Sum(), nil
-}
-
 type objectInfo struct {
 	Offset      int64
 	Length      int64
diff -Nru go-git.orig/v5/plumbing/format/packfile/scanner.go go-git/v5/plumbing/format/packfile/scanner.go
--- go-git.orig/v5/plumbing/format/packfile/scanner.go	2020-10-14 14:58:29.000000000 +0200
+++ go-git/v5/plumbing/format/packfile/scanner.go	2026-02-24 12:22:55.261158564 +0100
@@ -3,6 +3,8 @@
 import (
 	"bufio"
 	"bytes"
+	"crypto"
+	"errors"
 	"compress/zlib"
 	"fmt"
 	"hash"
@@ -12,6 +14,7 @@
 	"sync"
 
 	"github.com/go-git/go-git/v5/plumbing"
+	"github.com/go-git/go-git/v5/plumbing/hash"
 	"github.com/go-git/go-git/v5/utils/binary"
 	"github.com/go-git/go-git/v5/utils/ioutil"
 )
@@ -26,6 +29,8 @@
 	ErrUnsupportedVersion = NewError("unsupported packfile version")
 	// ErrSeekNotSupported returned if seek is not support
 	ErrSeekNotSupported = NewError("not seek support")
+	// ErrMalformedPackFile is returned by the parser when the pack file is corrupted.
+	ErrMalformedPackFile = errors.New("malformed PACK file")
 )
 
 // ObjectHeader contains the information related to the object, this information
@@ -39,8 +44,9 @@
 }
 
 type Scanner struct {
-	r   *scannerReader
-	crc hash.Hash32
+	r          *scannerReader
+	crc        gohash.Hash32
+	packHasher hash.Hash
 
 	// pendingObject is used to detect if an object has been read, or still
 	// is waiting to be read
@@ -58,10 +64,12 @@
 	_, ok := r.(io.ReadSeeker)
 
 	crc := crc32.NewIEEE()
+	hasher := hash.New(crypto.SHA1)
 	return &Scanner{
-		r:          newScannerReader(r, crc),
+		r:          newScannerReader(r, io.MultiWriter(crc, hasher)),
 		crc:        crc,
 		IsSeekable: ok,
+		packHasher: hasher,
 	}
 }
 
@@ -70,6 +78,7 @@
 
 	s.r.Reset(r)
 	s.crc.Reset()
+	s.packHasher.Reset()
 	s.IsSeekable = ok
 	s.pendingObject = nil
 	s.version = 0
@@ -116,7 +125,7 @@
 
 // readSignature reads an returns the signature field in the packfile.
 func (s *Scanner) readSignature() ([]byte, error) {
-	var sig = make([]byte, 4)
+	sig := make([]byte, 4)
 	if _, err := io.ReadFull(s.r, sig); err != nil {
 		return []byte{}, err
 	}
@@ -367,7 +376,18 @@
 		return plumbing.ZeroHash, err
 	}
 
-	return binary.ReadHash(s.r)
+	s.r.Flush()
+	actual := plumbing.Hash(s.packHasher.Sum(nil))
+	packChecksum, err := binary.ReadHash(s.r)
+	if err != nil {
+		return plumbing.ZeroHash, err
+	}
+
+	if actual != packChecksum {
+		return plumbing.ZeroHash, fmt.Errorf("%w: checksum mismatch: %q instead of %q", ErrMalformedPackFile, packChecksum, actual)
+	}
+
+	return packChecksum, nil
 }
 
 // Close reads the reader until io.EOF
@@ -393,17 +413,17 @@
 //   to the crc32 hash writer.
 type scannerReader struct {
 	reader io.Reader
-	crc    io.Writer
+	writer io.Writer
 	rbuf   *bufio.Reader
 	wbuf   *bufio.Writer
 	offset int64
 }
 
-func newScannerReader(r io.Reader, h io.Writer) *scannerReader {
+func newScannerReader(r io.Reader, w io.Writer) *scannerReader {
 	sr := &scannerReader{
-		rbuf: bufio.NewReader(nil),
-		wbuf: bufio.NewWriterSize(nil, 64),
-		crc:  h,
+		rbuf:   bufio.NewReader(nil),
+		wbuf:   bufio.NewWriterSize(nil, 64),
+		writer: w,
 	}
 	sr.Reset(r)
 
@@ -413,7 +433,7 @@
 func (r *scannerReader) Reset(reader io.Reader) {
 	r.reader = reader
 	r.rbuf.Reset(r.reader)
-	r.wbuf.Reset(r.crc)
+	r.wbuf.Reset(r.writer)
 
 	r.offset = 0
 	if seeker, ok := r.reader.(io.ReadSeeker); ok {
diff -Nru go-git.orig/v5/storage/filesystem/object.go go-git/v5/storage/filesystem/object.go
--- go-git.orig/v5/storage/filesystem/object.go	2020-10-14 14:58:29.000000000 +0200
+++ go-git/v5/storage/filesystem/object.go	2026-02-24 11:55:39.814372490 +0100
@@ -2,6 +2,8 @@
 
 import (
 	"io"
+	"encoding/hex"
+	"fmt"
 	"os"
 	"time"
 
@@ -85,6 +87,11 @@
 		return err
 	}
 
+	if !bytes.Equal(idxf.PackfileChecksum[:], h[:]) {
+		return fmt.Errorf("%w: packfile mismatch: target is %q not %q",
+			idxfile.ErrMalformedIdxFile, hex.EncodeToString(idxf.PackfileChecksum[:]), h.String())
+	}
+
 	s.index[h] = idxf
 	return err
 }
@@ -171,7 +178,8 @@
 }
 
 func (s *ObjectStorage) encodedObjectSizeFromUnpacked(h plumbing.Hash) (
-	size int64, err error) {
+	size int64, err error,
+) {
 	f, err := s.dir.Object(h)
 	if err != nil {
 		if os.IsNotExist(err) {
@@ -259,7 +267,8 @@
 }
 
 func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
-	size int64, err error) {
+	size int64, err error,
+) {
 	if err := s.requireIndex(); err != nil {
 		return 0, err
 	}
@@ -295,7 +304,8 @@
 // EncodedObjectSize returns the plaintext size of the given object,
 // without actually reading the full object data from storage.
 func (s *ObjectStorage) EncodedObjectSize(h plumbing.Hash) (
-	size int64, err error) {
+	size int64, err error,
+) {
 	size, err = s.encodedObjectSizeFromUnpacked(h)
 	if err != nil && err != plumbing.ErrObjectNotFound {
 		return 0, err
@@ -356,7 +366,8 @@
 // DeltaObject returns the object with the given hash, by searching for
 // it in the packfile and the git object directories.
 func (s *ObjectStorage) DeltaObject(t plumbing.ObjectType,
-	h plumbing.Hash) (plumbing.EncodedObject, error) {
+	h plumbing.Hash,
+) (plumbing.EncodedObject, error) {
 	obj, err := s.getFromUnpacked(h)
 	if err == plumbing.ErrObjectNotFound {
 		obj, err = s.getFromPackfile(h, true)
@@ -419,8 +430,8 @@
 // Get returns the object with the given hash, by searching for it in
 // the packfile.
 func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) (
-	plumbing.EncodedObject, error) {
-
+	plumbing.EncodedObject, error,
+) {
 	if err := s.requireIndex(); err != nil {
 		return nil, err
 	}
@@ -477,9 +488,7 @@
 		return nil, err
 	}
 
-	var (
-		base plumbing.Hash
-	)
+	var base plumbing.Hash
 
 	switch header.Type {
 	case plumbing.REFDeltaObject:
