Skip to content

Commit 521ea3d

Browse files
josegomezrcorhere
andcommitted
Fix runaway allocation on /v2/_catalog
Introduced a Catalog entry in the configuration struct. With it, it's possible to control the maximum amount of entries returned by /v2/catalog (`GetCatalog` in registry/handlers/catalog.go). It's set to a default value of 1000. `GetCatalog` returns 100 entries by default if no `n` is provided. When provided it will be validated to be between `0` and `MaxEntries` defined in Configuration. When `n` is outside the aforementioned boundary, ErrorCodePaginationNumberInvalid is returned. `GetCatalog` now handles `n=0` gracefully with an empty response as well. Signed-off-by: José D. Gómez R. <1josegomezr@gmail.com> Co-authored-by: Cory Snider <corhere@gmail.com>
1 parent dc5b207 commit 521ea3d

File tree

6 files changed

+376
-42
lines changed

6 files changed

+376
-42
lines changed

configuration/configuration.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ type Configuration struct {
193193
} `yaml:"pool,omitempty"`
194194
} `yaml:"redis,omitempty"`
195195

196-
Health Health `yaml:"health,omitempty"`
196+
Health Health `yaml:"health,omitempty"`
197+
Catalog Catalog `yaml:"catalog,omitempty"`
197198

198199
Proxy Proxy `yaml:"proxy,omitempty"`
199200

@@ -244,6 +245,16 @@ type Configuration struct {
244245
} `yaml:"policy,omitempty"`
245246
}
246247

248+
// Catalog is composed of MaxEntries.
249+
// Catalog endpoint (/v2/_catalog) configuration, it provides the configuration
250+
// options to control the maximum number of entries returned by the catalog endpoint.
251+
type Catalog struct {
252+
// Max number of entries returned by the catalog endpoint. Requesting n entries
253+
// to the catalog endpoint will return at most MaxEntries entries.
254+
// An empty or a negative value will set a default of 1000 maximum entries by default.
255+
MaxEntries int `yaml:"maxentries,omitempty"`
256+
}
257+
247258
// LogHook is composed of hook Level and Type.
248259
// After hooks configuration, it can execute the next handling automatically,
249260
// when defined levels of log message emitted.
@@ -670,6 +681,11 @@ func Parse(rd io.Reader) (*Configuration, error) {
670681
if v0_1.Loglevel != Loglevel("") {
671682
v0_1.Loglevel = Loglevel("")
672683
}
684+
685+
if v0_1.Catalog.MaxEntries <= 0 {
686+
v0_1.Catalog.MaxEntries = 1000
687+
}
688+
673689
if v0_1.Storage.Type() == "" {
674690
return nil, errors.New("no storage configuration provided")
675691
}

configuration/configuration_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ var configStruct = Configuration{
7171
},
7272
},
7373
},
74+
Catalog: Catalog{
75+
MaxEntries: 1000,
76+
},
7477
HTTP: struct {
7578
Addr string `yaml:"addr,omitempty"`
7679
Net string `yaml:"net,omitempty"`
@@ -524,6 +527,7 @@ func copyConfig(config Configuration) *Configuration {
524527
configCopy.Version = MajorMinorVersion(config.Version.Major(), config.Version.Minor())
525528
configCopy.Loglevel = config.Loglevel
526529
configCopy.Log = config.Log
530+
configCopy.Catalog = config.Catalog
527531
configCopy.Log.Fields = make(map[string]interface{}, len(config.Log.Fields))
528532
for k, v := range config.Log.Fields {
529533
configCopy.Log.Fields[k] = v

registry/api/v2/descriptors.go

+17
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,19 @@ var (
134134
},
135135
}
136136

137+
invalidPaginationResponseDescriptor = ResponseDescriptor{
138+
Name: "Invalid pagination number",
139+
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.",
140+
StatusCode: http.StatusBadRequest,
141+
Body: BodyDescriptor{
142+
ContentType: "application/json",
143+
Format: errorsBody,
144+
},
145+
ErrorCodes: []errcode.ErrorCode{
146+
ErrorCodePaginationNumberInvalid,
147+
},
148+
}
149+
137150
repositoryNotFoundResponseDescriptor = ResponseDescriptor{
138151
Name: "No Such Repository Error",
139152
StatusCode: http.StatusNotFound,
@@ -490,6 +503,7 @@ var routeDescriptors = []RouteDescriptor{
490503
},
491504
},
492505
Failures: []ResponseDescriptor{
506+
invalidPaginationResponseDescriptor,
493507
unauthorizedResponseDescriptor,
494508
repositoryNotFoundResponseDescriptor,
495509
deniedResponseDescriptor,
@@ -1578,6 +1592,9 @@ var routeDescriptors = []RouteDescriptor{
15781592
},
15791593
},
15801594
},
1595+
Failures: []ResponseDescriptor{
1596+
invalidPaginationResponseDescriptor,
1597+
},
15811598
},
15821599
},
15831600
},

registry/api/v2/errors.go

+9
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,13 @@ var (
133133
longer proceed.`,
134134
HTTPStatusCode: http.StatusNotFound,
135135
})
136+
137+
ErrorCodePaginationNumberInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
138+
Value: "PAGINATION_NUMBER_INVALID",
139+
Message: "invalid number of results requested",
140+
Description: `Returned when the "n" parameter (number of results
141+
to return) is not an integer, "n" is negative or "n" is bigger than
142+
the maximum allowed.`,
143+
HTTPStatusCode: http.StatusBadRequest,
144+
})
136145
)

0 commit comments

Comments
 (0)