[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1036078: unblock: docker-registry/2.8.2+ds1-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: docker-registry@packages.debian.org, zhsj@debian.org
Control: affects -1 + src:docker-registry

Please unblock package docker-registry

[ Reason ]
Upstream micro release for CVE-2023-2253 (Catalog API endpoint can lead to OOM
via malicious user input).

[ Impact ]
Fix security issue.

[ Tests ]
New unittest is added. The package has autopkgtest.

[ Risks ]

The debdiff contains some noise code style changes, otherwise they are trivial.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
I attached a filtered debdiff, with following command,

 filterdiff --exclude '*/releases/*' --exclude '*/script/*' --exclude '*/.github/*' \
   --exclude '*.hcl'  --exclude '*.yml' --exclude '*/Dockerfile' \
   --exclude '*/Makefile' --exclude '*/.mailmap'  --exclude '*_test.go'

unblock docker-registry/2.8.2+ds1-1
diff -Nru -w docker-registry-2.8.1+ds1/configuration/configuration.go docker-registry-2.8.2+ds1/configuration/configuration.go
--- docker-registry-2.8.1+ds1/configuration/configuration.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/configuration/configuration.go	2023-05-11 18:11:57.000000000 +0800
@@ -194,6 +194,7 @@
 	} `yaml:"redis,omitempty"`
 
 	Health Health `yaml:"health,omitempty"`
+	Catalog Catalog `yaml:"catalog,omitempty"`
 
 	Proxy Proxy `yaml:"proxy,omitempty"`
 
@@ -244,6 +245,16 @@
 	} `yaml:"policy,omitempty"`
 }
 
+// Catalog is composed of MaxEntries.
+// Catalog endpoint (/v2/_catalog) configuration, it provides the configuration
+// options to control the maximum number of entries returned by the catalog endpoint.
+type Catalog struct {
+	// Max number of entries returned by the catalog endpoint. Requesting n entries
+	// to the catalog endpoint will return at most MaxEntries entries.
+	// An empty or a negative value will set a default of 1000 maximum entries by default.
+	MaxEntries int `yaml:"maxentries,omitempty"`
+}
+
 // LogHook is composed of hook Level and Type.
 // After hooks configuration, it can execute the next handling automatically,
 // when defined levels of log message emitted.
@@ -670,6 +681,11 @@
 					if v0_1.Loglevel != Loglevel("") {
 						v0_1.Loglevel = Loglevel("")
 					}
+
+					if v0_1.Catalog.MaxEntries <= 0 {
+						v0_1.Catalog.MaxEntries = 1000
+					}
+
 					if v0_1.Storage.Type() == "" {
 						return nil, errors.New("no storage configuration provided")
 					}
diff -Nru -w docker-registry-2.8.1+ds1/context/doc.go docker-registry-2.8.2+ds1/context/doc.go
--- docker-registry-2.8.1+ds1/context/doc.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/context/doc.go	2023-05-11 18:11:57.000000000 +0800
@@ -15,7 +15,7 @@
 // The above will store the version in the context and will be available to
 // the logger.
 //
-// Logging
+// # Logging
 //
 // The most useful aspect of this package is GetLogger. This function takes
 // any context.Context interface and returns the current logger from the
@@ -65,7 +65,7 @@
 // added to the request context, is unique to that context and can have
 // request scoped variables.
 //
-// HTTP Requests
+// # HTTP Requests
 //
 // This package also contains several methods for working with http requests.
 // The concepts are very similar to those described above. We simply place the
diff -Nru -w docker-registry-2.8.1+ds1/context/http.go docker-registry-2.8.2+ds1/context/http.go
--- docker-registry-2.8.1+ds1/context/http.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/context/http.go	2023-05-11 18:11:57.000000000 +0800
@@ -246,11 +246,7 @@
 			return ctx.vars
 		}
 
-		if strings.HasPrefix(keyStr, "vars.") {
-			keyStr = strings.TrimPrefix(keyStr, "vars.")
-		}
-
-		if v, ok := ctx.vars[keyStr]; ok {
+		if v, ok := ctx.vars[strings.TrimPrefix(keyStr, "vars.")]; ok {
 			return v
 		}
 	}
diff -Nru -w docker-registry-2.8.1+ds1/debian/changelog docker-registry-2.8.2+ds1/debian/changelog
--- docker-registry-2.8.1+ds1/debian/changelog	2022-06-29 20:32:34.000000000 +0800
+++ docker-registry-2.8.2+ds1/debian/changelog	2023-05-13 23:21:12.000000000 +0800
@@ -1,3 +1,14 @@
+docker-registry (2.8.2+ds1-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream version 2.8.2+ds1
+    + CVE-2023-2253: Catalog API endpoint can lead to OOM via malicious user
+      input (Closes: #1035956)
+  * Drop patch merged by upstream
+    + 0009-Fix-panic-in-inmemory-driver.patch
+
+ -- Shengjing Zhu <zhsj@debian.org>  Sat, 13 May 2023 23:21:12 +0800
+
 docker-registry (2.8.1+ds1-2) unstable; urgency=medium
 
   [ Debian Janitor ]
diff -Nru -w docker-registry-2.8.1+ds1/debian/patches/0007-Skip-TestRegistryAsCacheMutationAPIs.patch docker-registry-2.8.2+ds1/debian/patches/0007-Skip-TestRegistryAsCacheMutationAPIs.patch
--- docker-registry-2.8.1+ds1/debian/patches/0007-Skip-TestRegistryAsCacheMutationAPIs.patch	2022-06-29 20:32:34.000000000 +0800
+++ docker-registry-2.8.2+ds1/debian/patches/0007-Skip-TestRegistryAsCacheMutationAPIs.patch	2023-05-13 23:21:12.000000000 +0800
@@ -12,10 +12,10 @@
  1 file changed, 1 insertion(+)
 
 diff --git a/registry/handlers/api_test.go b/registry/handlers/api_test.go
-index 2d3edc7..a07184b 100644
+index bf037d4..207e167 100644
 --- a/registry/handlers/api_test.go
 +++ b/registry/handlers/api_test.go
-@@ -2468,6 +2468,7 @@ func createRepository(env *testEnv, t *testing.T, imageName string, tag string)
+@@ -2728,6 +2728,7 @@ func createRepository(env *testEnv, t *testing.T, imageName string, tag string)
  // Test mutation operations on a registry configured as a cache.  Ensure that they return
  // appropriate errors.
  func TestRegistryAsCacheMutationAPIs(t *testing.T) {
diff -Nru -w docker-registry-2.8.1+ds1/debian/patches/0009-Fix-panic-in-inmemory-driver.patch docker-registry-2.8.2+ds1/debian/patches/0009-Fix-panic-in-inmemory-driver.patch
--- docker-registry-2.8.1+ds1/debian/patches/0009-Fix-panic-in-inmemory-driver.patch	2022-06-29 20:32:34.000000000 +0800
+++ docker-registry-2.8.2+ds1/debian/patches/0009-Fix-panic-in-inmemory-driver.patch	1970-01-01 08:00:00.000000000 +0800
@@ -1,23 +0,0 @@
-From: Shengjing Zhu <zhsj@debian.org>
-Date: Sun, 27 Mar 2022 19:38:07 +0800
-Subject: Fix panic in inmemory driver
-
-Forwarded: https://github.com/distribution/distribution/pull/3615
----
- registry/storage/driver/inmemory/mfs.go | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/registry/storage/driver/inmemory/mfs.go b/registry/storage/driver/inmemory/mfs.go
-index 9a2865f..24eeef9 100644
---- a/registry/storage/driver/inmemory/mfs.go
-+++ b/registry/storage/driver/inmemory/mfs.go
-@@ -279,6 +279,9 @@ func (f *file) sectionReader(offset int64) io.Reader {
- }
- 
- func (f *file) ReadAt(p []byte, offset int64) (n int, err error) {
-+	if offset >= int64(len(f.data)) {
-+		return 0, io.EOF
-+	}
- 	return copy(p, f.data[offset:]), nil
- }
- 
diff -Nru -w docker-registry-2.8.1+ds1/debian/patches/series docker-registry-2.8.2+ds1/debian/patches/series
--- docker-registry-2.8.1+ds1/debian/patches/series	2022-06-29 20:32:34.000000000 +0800
+++ docker-registry-2.8.2+ds1/debian/patches/series	2023-05-13 23:21:12.000000000 +0800
@@ -6,4 +6,3 @@
 0006-Skip-TestHTTPChecker.patch
 0007-Skip-TestRegistryAsCacheMutationAPIs.patch
 0008-Skip-flaky-TestGracefulShutdown-and-TestRegistrySupp.patch
-0009-Fix-panic-in-inmemory-driver.patch
diff -Nru -w docker-registry-2.8.1+ds1/.dockerignore docker-registry-2.8.2+ds1/.dockerignore
--- docker-registry-2.8.1+ds1/.dockerignore	1970-01-01 08:00:00.000000000 +0800
+++ docker-registry-2.8.2+ds1/.dockerignore	2023-05-11 18:11:57.000000000 +0800
@@ -0,0 +1 @@
+bin/
diff -Nru -w docker-registry-2.8.1+ds1/health/doc.go docker-registry-2.8.2+ds1/health/doc.go
--- docker-registry-2.8.1+ds1/health/doc.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/health/doc.go	2023-05-11 18:11:57.000000000 +0800
@@ -13,7 +13,7 @@
 // particularly useful for checks that verify upstream connectivity or
 // database status, since they might take a long time to return/timeout.
 //
-// Installing
+// # Installing
 //
 // To install health, just import it in your application:
 //
@@ -35,7 +35,7 @@
 // After importing these packages to your main application, you can start
 // registering checks.
 //
-// Registering Checks
+// # Registering Checks
 //
 // The recommended way of registering checks is using a periodic Check.
 // PeriodicChecks run on a certain schedule and asynchronously update the
@@ -84,7 +84,7 @@
 //   return Errors.new("This is an error!")
 //  }))
 //
-// Examples
+// # Examples
 //
 // You could also use the health checker mechanism to ensure your application
 // only comes up if certain conditions are met, or to allow the developer to
diff -Nru -w docker-registry-2.8.1+ds1/registry/api/v2/descriptors.go docker-registry-2.8.2+ds1/registry/api/v2/descriptors.go
--- docker-registry-2.8.1+ds1/registry/api/v2/descriptors.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/api/v2/descriptors.go	2023-05-11 18:11:57.000000000 +0800
@@ -134,6 +134,19 @@
 		},
 	}
 
+	invalidPaginationResponseDescriptor = ResponseDescriptor{
+		Name:        "Invalid pagination number",
+		Description: "The received parameter n was invalid in some way, as described by the error code. The client should resolve the issue and retry the request.",
+		StatusCode:  http.StatusBadRequest,
+		Body: BodyDescriptor{
+			ContentType: "application/json",
+			Format:      errorsBody,
+		},
+		ErrorCodes: []errcode.ErrorCode{
+			ErrorCodePaginationNumberInvalid,
+		},
+	}
+
 	repositoryNotFoundResponseDescriptor = ResponseDescriptor{
 		Name:        "No Such Repository Error",
 		StatusCode:  http.StatusNotFound,
@@ -490,6 +503,7 @@
 							},
 						},
 						Failures: []ResponseDescriptor{
+							invalidPaginationResponseDescriptor,
 							unauthorizedResponseDescriptor,
 							repositoryNotFoundResponseDescriptor,
 							deniedResponseDescriptor,
@@ -1578,6 +1592,9 @@
 								},
 							},
 						},
+						Failures: []ResponseDescriptor{
+							invalidPaginationResponseDescriptor,
+						},
 					},
 				},
 			},
diff -Nru -w docker-registry-2.8.1+ds1/registry/api/v2/errors.go docker-registry-2.8.2+ds1/registry/api/v2/errors.go
--- docker-registry-2.8.1+ds1/registry/api/v2/errors.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/api/v2/errors.go	2023-05-11 18:11:57.000000000 +0800
@@ -133,4 +133,13 @@
 		longer proceed.`,
 		HTTPStatusCode: http.StatusNotFound,
 	})
+
+	ErrorCodePaginationNumberInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:   "PAGINATION_NUMBER_INVALID",
+		Message: "invalid number of results requested",
+		Description: `Returned when the "n" parameter (number of results
+		to return) is not an integer, "n" is negative or "n" is bigger than
+		the maximum allowed.`,
+		HTTPStatusCode: http.StatusBadRequest,
+	})
 )
diff -Nru -w docker-registry-2.8.1+ds1/registry/auth/auth.go docker-registry-2.8.2+ds1/registry/auth/auth.go
--- docker-registry-2.8.1+ds1/registry/auth/auth.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/auth/auth.go	2023-05-11 18:11:57.000000000 +0800
@@ -29,7 +29,6 @@
 //				}
 //			}
 // 		}
-//
 package auth
 
 import (
diff -Nru -w docker-registry-2.8.1+ds1/registry/auth/token/token.go docker-registry-2.8.2+ds1/registry/auth/token/token.go
--- docker-registry-2.8.1+ds1/registry/auth/token/token.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/auth/token/token.go	2023-05-11 18:11:57.000000000 +0800
@@ -185,6 +185,7 @@
 
 // VerifySigningKey attempts to get the key which was used to sign this token.
 // The token header should contain either of these 3 fields:
+//
 //      `x5c` - The x509 certificate chain for the signing key. Needs to be
 //              verified.
 //      `jwk` - The JSON Web Key representation of the signing key.
@@ -192,6 +193,7 @@
 //      `kid` - The unique identifier for the key. This library interprets it
 //              as a libtrust fingerprint. The key itself can be looked up in
 //              the trustedKeys field of the given verify options.
+//
 // Each of these methods are tried in that order of preference until the
 // signing key is found or an error is returned.
 func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey libtrust.PublicKey, err error) {
diff -Nru -w docker-registry-2.8.1+ds1/registry/client/errors.go docker-registry-2.8.2+ds1/registry/client/errors.go
--- docker-registry-2.8.1+ds1/registry/client/errors.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/client/errors.go	2023-05-11 18:11:57.000000000 +0800
@@ -55,6 +55,8 @@
 		switch statusCode {
 		case http.StatusUnauthorized:
 			return errcode.ErrorCodeUnauthorized.WithMessage(detailsErr.Details)
+		case http.StatusForbidden:
+			return errcode.ErrorCodeDenied.WithMessage(detailsErr.Details)
 		case http.StatusTooManyRequests:
 			return errcode.ErrorCodeTooManyRequests.WithMessage(detailsErr.Details)
 		default:
diff -Nru -w docker-registry-2.8.1+ds1/registry/client/repository.go docker-registry-2.8.2+ds1/registry/client/repository.go
--- docker-registry-2.8.1+ds1/registry/client/repository.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/client/repository.go	2023-05-11 18:11:57.000000000 +0800
@@ -114,9 +114,7 @@
 			return 0, err
 		}
 
-		for cnt := range ctlg.Repositories {
-			entries[cnt] = ctlg.Repositories[cnt]
-		}
+		copy(entries, ctlg.Repositories)
 		numFilled = len(ctlg.Repositories)
 
 		link := resp.Header.Get("Link")
diff -Nru -w docker-registry-2.8.1+ds1/registry/client/transport/http_reader.go docker-registry-2.8.2+ds1/registry/client/transport/http_reader.go
--- docker-registry-2.8.1+ds1/registry/client/transport/http_reader.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/client/transport/http_reader.go	2023-05-11 18:11:57.000000000 +0800
@@ -180,7 +180,6 @@
 		// context.GetLogger(hrs.context).Infof("Range: %s", req.Header.Get("Range"))
 	}
 
-	req.Header.Add("Accept-Encoding", "identity")
 	resp, err := hrs.client.Do(req)
 	if err != nil {
 		return nil, err
diff -Nru -w docker-registry-2.8.1+ds1/registry/handlers/basicauth.go docker-registry-2.8.2+ds1/registry/handlers/basicauth.go
--- docker-registry-2.8.1+ds1/registry/handlers/basicauth.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/handlers/basicauth.go	2023-05-11 18:11:57.000000000 +0800
@@ -1,3 +1,4 @@
+//go:build go1.4
 // +build go1.4
 
 package handlers
diff -Nru -w docker-registry-2.8.1+ds1/registry/handlers/basicauth_prego14.go docker-registry-2.8.2+ds1/registry/handlers/basicauth_prego14.go
--- docker-registry-2.8.1+ds1/registry/handlers/basicauth_prego14.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/handlers/basicauth_prego14.go	2023-05-11 18:11:57.000000000 +0800
@@ -1,3 +1,4 @@
+//go:build !go1.4
 // +build !go1.4
 
 package handlers
diff -Nru -w docker-registry-2.8.1+ds1/registry/handlers/catalog.go docker-registry-2.8.2+ds1/registry/handlers/catalog.go
--- docker-registry-2.8.1+ds1/registry/handlers/catalog.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/handlers/catalog.go	2023-05-11 18:11:57.000000000 +0800
@@ -9,11 +9,13 @@
 	"strconv"
 
 	"github.com/docker/distribution/registry/api/errcode"
+	v2 "github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/storage/driver"
+
 	"github.com/gorilla/handlers"
 )
 
-const maximumReturnedEntries = 100
+const defaultReturnedEntries = 100
 
 func catalogDispatcher(ctx *Context, r *http.Request) http.Handler {
 	catalogHandler := &catalogHandler{
@@ -38,29 +40,55 @@
 
 	q := r.URL.Query()
 	lastEntry := q.Get("last")
-	maxEntries, err := strconv.Atoi(q.Get("n"))
-	if err != nil || maxEntries < 0 {
-		maxEntries = maximumReturnedEntries
+
+	entries := defaultReturnedEntries
+	maximumConfiguredEntries := ch.App.Config.Catalog.MaxEntries
+
+	// parse n, if n unparseable, or negative assign it to defaultReturnedEntries
+	if n := q.Get("n"); n != "" {
+		parsedMax, err := strconv.Atoi(n)
+		if err == nil {
+			if parsedMax > maximumConfiguredEntries {
+				ch.Errors = append(ch.Errors, v2.ErrorCodePaginationNumberInvalid.WithDetail(map[string]int{"n": parsedMax}))
+				return
+			} else if parsedMax >= 0 {
+				entries = parsedMax
+			}
+		}
 	}
 
-	repos := make([]string, maxEntries)
+	// then enforce entries to be between 0 & maximumConfiguredEntries
+	// max(0, min(entries, maximumConfiguredEntries))
+	if entries < 0 || entries > maximumConfiguredEntries {
+		entries = maximumConfiguredEntries
+	}
 
-	filled, err := ch.App.registry.Repositories(ch.Context, repos, lastEntry)
-	_, pathNotFound := err.(driver.PathNotFoundError)
+	repos := make([]string, entries)
+	filled := 0
 
-	if err == io.EOF || pathNotFound {
+	// entries is guaranteed to be >= 0 and < maximumConfiguredEntries
+	if entries == 0 {
 		moreEntries = false
-	} else if err != nil {
+	} else {
+		returnedRepositories, err := ch.App.registry.Repositories(ch.Context, repos, lastEntry)
+		if err != nil {
+			_, pathNotFound := err.(driver.PathNotFoundError)
+			if err != io.EOF && !pathNotFound {
 		ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
 		return
 	}
+			// err is either io.EOF or not PathNotFoundError
+			moreEntries = false
+		}
+		filled = returnedRepositories
+	}
 
 	w.Header().Set("Content-Type", "application/json; charset=utf-8")
 
 	// Add a link header if there are more entries to retrieve
 	if moreEntries {
-		lastEntry = repos[len(repos)-1]
-		urlStr, err := createLinkEntry(r.URL.String(), maxEntries, lastEntry)
+		lastEntry = repos[filled-1]
+		urlStr, err := createLinkEntry(r.URL.String(), entries, lastEntry)
 		if err != nil {
 			ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
 			return
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/blobwriter_nonresumable.go docker-registry-2.8.2+ds1/registry/storage/blobwriter_nonresumable.go
--- docker-registry-2.8.1+ds1/registry/storage/blobwriter_nonresumable.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/blobwriter_nonresumable.go	2023-05-11 18:11:57.000000000 +0800
@@ -1,3 +1,4 @@
+//go:build noresumabledigest
 // +build noresumabledigest
 
 package storage
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/blobwriter_resumable.go docker-registry-2.8.2+ds1/registry/storage/blobwriter_resumable.go
--- docker-registry-2.8.1+ds1/registry/storage/blobwriter_resumable.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/blobwriter_resumable.go	2023-05-11 18:11:57.000000000 +0800
@@ -1,3 +1,4 @@
+//go:build !noresumabledigest
 // +build !noresumabledigest
 
 package storage
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/driver/gcs/gcs.go docker-registry-2.8.2+ds1/registry/storage/driver/gcs/gcs.go
--- docker-registry-2.8.1+ds1/registry/storage/driver/gcs/gcs.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/driver/gcs/gcs.go	2023-05-11 18:11:57.000000000 +0800
@@ -10,6 +10,7 @@
 // Note that the contents of incomplete uploads are not accessible even though
 // Stat returns their length
 //
+//go:build include_gcs
 // +build include_gcs
 
 package gcs
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/driver/inmemory/mfs.go docker-registry-2.8.2+ds1/registry/storage/driver/inmemory/mfs.go
--- docker-registry-2.8.1+ds1/registry/storage/driver/inmemory/mfs.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/driver/inmemory/mfs.go	2023-05-11 18:11:57.000000000 +0800
@@ -279,6 +279,9 @@
 }
 
 func (f *file) ReadAt(p []byte, offset int64) (n int, err error) {
+	if offset >= int64(len(f.data)) {
+		return 0, io.EOF
+	}
 	return copy(p, f.data[offset:]), nil
 }
 
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/driver/middleware/cloudfront/middleware.go docker-registry-2.8.2+ds1/registry/storage/driver/middleware/cloudfront/middleware.go
--- docker-registry-2.8.1+ds1/registry/storage/driver/middleware/cloudfront/middleware.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/driver/middleware/cloudfront/middleware.go	2023-05-11 18:11:57.000000000 +0800
@@ -1,6 +1,5 @@
 // Package middleware - cloudfront wrapper for storage libs
 // N.B. currently only works with S3, not arbitrary sites
-//
 package middleware
 
 import (
@@ -38,7 +37,9 @@
 
 // Optional options: ipFilteredBy, awsregion
 // ipfilteredby: valid value "none|aws|awsregion". "none", do not filter any IP, default value. "aws", only aws IP goes
+//
 //               to S3 directly. "awsregion", only regions listed in awsregion options goes to S3 directly
+//
 // awsregion: a comma separated string of AWS regions.
 func newCloudFrontStorageMiddleware(storageDriver storagedriver.StorageDriver, options map[string]interface{}) (storagedriver.StorageDriver, error) {
 	// parse baseurl
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/driver/oss/oss.go docker-registry-2.8.2+ds1/registry/storage/driver/oss/oss.go
--- docker-registry-2.8.1+ds1/registry/storage/driver/oss/oss.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/driver/oss/oss.go	2023-05-11 18:11:57.000000000 +0800
@@ -7,6 +7,7 @@
 // Because OSS is a key, value store the Stat call does not support last modification
 // time for directories (directories are an abstraction for key, value stores)
 //
+//go:build include_oss
 // +build include_oss
 
 package oss
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/driver/s3-aws/s3.go docker-registry-2.8.2+ds1/registry/storage/driver/s3-aws/s3.go
--- docker-registry-2.8.1+ds1/registry/storage/driver/s3-aws/s3.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/driver/s3-aws/s3.go	2023-05-11 18:11:57.000000000 +0800
@@ -549,9 +549,9 @@
 
 // Writer returns a FileWriter which will store the content written to it
 // at the location designated by "path" after the call to Commit.
-func (d *driver) Writer(ctx context.Context, path string, append bool) (storagedriver.FileWriter, error) {
+func (d *driver) Writer(ctx context.Context, path string, appendParam bool) (storagedriver.FileWriter, error) {
 	key := d.s3Path(path)
-	if !append {
+	if !appendParam {
 		// TODO (brianbland): cancel other uploads at this path
 		resp, err := d.S3.CreateMultipartUpload(&s3.CreateMultipartUploadInput{
 			Bucket:               aws.String(d.Bucket),
@@ -574,7 +574,7 @@
 	if err != nil {
 		return nil, parseError(path, err)
 	}
-
+	var allParts []*s3.Part
 	for _, multi := range resp.Uploads {
 		if key != *multi.Key {
 			continue
@@ -587,11 +587,20 @@
 		if err != nil {
 			return nil, parseError(path, err)
 		}
-		var multiSize int64
-		for _, part := range resp.Parts {
-			multiSize += *part.Size
+		allParts = append(allParts, resp.Parts...)
+		for *resp.IsTruncated {
+			resp, err = d.S3.ListParts(&s3.ListPartsInput{
+				Bucket:           aws.String(d.Bucket),
+				Key:              aws.String(key),
+				UploadId:         multi.UploadId,
+				PartNumberMarker: resp.NextPartNumberMarker,
+			})
+			if err != nil {
+				return nil, parseError(path, err)
+			}
+			allParts = append(allParts, resp.Parts...)
 		}
-		return d.newWriter(key, *multi.UploadId, resp.Parts), nil
+		return d.newWriter(key, *multi.UploadId, allParts), nil
 	}
 	return nil, storagedriver.PathNotFoundError{Path: path}
 }
diff -Nru -w docker-registry-2.8.1+ds1/registry/storage/paths.go docker-registry-2.8.2+ds1/registry/storage/paths.go
--- docker-registry-2.8.1+ds1/registry/storage/paths.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/registry/storage/paths.go	2023-05-11 18:11:57.000000000 +0800
@@ -435,7 +435,6 @@
 // groups of digest folder. It will be as follows:
 //
 // 	<algorithm>/<first two bytes of digest>/<full digest>
-//
 func digestPathComponents(dgst digest.Digest, multilevel bool) ([]string, error) {
 	if err := dgst.Validate(); err != nil {
 		return nil, err
diff -Nru -w docker-registry-2.8.1+ds1/version/print.go docker-registry-2.8.2+ds1/version/print.go
--- docker-registry-2.8.1+ds1/version/print.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/version/print.go	2023-05-11 18:11:57.000000000 +0800
@@ -15,7 +15,6 @@
 // with version "v2.0" would print the following:
 //
 // 	registry github.com/docker/distribution v2.0
-//
 func FprintVersion(w io.Writer) {
 	fmt.Fprintln(w, os.Args[0], Package, Version)
 }
diff -Nru -w docker-registry-2.8.1+ds1/version/version.go docker-registry-2.8.2+ds1/version/version.go
--- docker-registry-2.8.1+ds1/version/version.go	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/version/version.go	2023-05-11 18:11:57.000000000 +0800
@@ -8,7 +8,7 @@
 // the latest release tag by hand, always suffixed by "+unknown". During
 // build, it will be replaced by the actual version. The value here will be
 // used if the registry is run after a go get based install.
-var Version = "v2.8.1+unknown"
+var Version = "v2.8.2+unknown"
 
 // Revision is filled with the VCS (e.g. git) revision being used to build
 // the program at linking time.
diff -Nru -w docker-registry-2.8.1+ds1/version/version.sh docker-registry-2.8.2+ds1/version/version.sh
--- docker-registry-2.8.1+ds1/version/version.sh	2022-03-09 01:52:36.000000000 +0800
+++ docker-registry-2.8.2+ds1/version/version.sh	2023-05-11 18:11:57.000000000 +0800
@@ -17,7 +17,7 @@
 // Version indicates which version of the binary is running. This is set to
 // the latest release tag by hand, always suffixed by "+unknown". During
 // build, it will be replaced by the actual version. The value here will be
-// used if the registry is run after a go get based install.
+// used if the registry is run after a go install based install.
 var Version = "$(git describe --match 'v[0-9]*' --dirty='.m' --always)+unknown"
 
 // Revision is filled with the VCS (e.g. git) revision being used to build

Reply to: