Limit amount of data fetched from git repository.

Like limiting the size of an archive, it is a supplementary check meant
to limit resource consumption prior to the final check done in
`StoreManifest()`.
This commit is contained in:
Catherine
2025-12-14 19:39:19 +00:00
parent 7655400560
commit 30b6db2758
2 changed files with 20 additions and 5 deletions

View File

@@ -23,6 +23,8 @@ import (
"google.golang.org/protobuf/proto"
)
var ErrRepositoryTooLarge = errors.New("repository too large")
func FetchRepository(
ctx context.Context, repoURL string, branch string, oldManifest *Manifest,
) (
@@ -152,9 +154,10 @@ func FetchRepository(
// This will only succeed if a `blob:none` filter isn't supported and we got a full
// clone despite asking for a partial clone.
for hash, manifestEntry := range blobsNeeded {
if err := readGitBlob(repo, hash, manifestEntry); err == nil {
dataBytesTransferred += manifestEntry.GetOriginalSize()
if err := readGitBlob(repo, hash, manifestEntry, &dataBytesTransferred); err == nil {
delete(blobsNeeded, hash)
} else if errors.Is(err, ErrRepositoryTooLarge) {
return nil, err
}
}
@@ -193,10 +196,9 @@ func FetchRepository(
// All remaining blobs should now be available.
for hash, manifestEntry := range blobsNeeded {
if err := readGitBlob(repo, hash, manifestEntry); err != nil {
if err := readGitBlob(repo, hash, manifestEntry, &dataBytesTransferred); err != nil {
return nil, err
}
dataBytesTransferred += manifestEntry.GetOriginalSize()
delete(blobsNeeded, hash)
}
}
@@ -210,7 +212,9 @@ func FetchRepository(
return manifest, nil
}
func readGitBlob(repo *git.Repository, hash plumbing.Hash, entry *Entry) error {
func readGitBlob(
repo *git.Repository, hash plumbing.Hash, entry *Entry, bytesTransferred *int64,
) error {
blob, err := repo.BlobObject(hash)
if err != nil {
return fmt.Errorf("git blob %s: %w", hash, err)
@@ -239,5 +243,14 @@ func readGitBlob(repo *git.Repository, hash plumbing.Hash, entry *Entry) error {
entry.Transform = Transform_Identity.Enum()
entry.OriginalSize = proto.Int64(blob.Size)
entry.CompressedSize = proto.Int64(blob.Size)
*bytesTransferred += blob.Size
if uint64(*bytesTransferred) > config.Limits.MaxSiteSize.Bytes() {
return fmt.Errorf("%w: fetch exceeds %s limit",
ErrRepositoryTooLarge,
config.Limits.MaxSiteSize.HR(),
)
}
return nil
}

View File

@@ -607,6 +607,8 @@ func reportUpdateResult(w http.ResponseWriter, r *http.Request, result UpdateRes
w.WriteHeader(http.StatusUnsupportedMediaType)
} else if errors.Is(result.err, ErrArchiveTooLarge) {
w.WriteHeader(http.StatusRequestEntityTooLarge)
} else if errors.Is(result.err, ErrRepositoryTooLarge) {
w.WriteHeader(http.StatusUnprocessableEntity)
} else if errors.Is(result.err, ErrMalformedPatch) {
w.WriteHeader(http.StatusUnprocessableEntity)
} else if errors.Is(result.err, ErrPreconditionFailed) {