ListDeployments panics when statuses or kinds contain only empty values
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 1.4k
- Forks
- 364
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 84
Description
What happened:
APIService.ListDeployments panics with index out of range [0] with length 0 when the request has statuses (or kinds) that are not empty as a list, but every value inside is an empty string.
In pkg/app/server/grpcapi/api.go the loop skips empty values, but statuses[0] is read after it without checking that anything was actually added:
if len(req.Statuses) > 0 {
statuses := []model.DeploymentStatus{}
for _, s := range req.Statuses {
if s != "" { // empty values are skipped here
...
statuses = append(statuses, model.DeploymentStatus(depstatus))
}
}
filters = append(filters, datastore.ListFilter{
Field: "Status",
Operator: datastore.OperatorEqual,
Value: statuses[0], // <- panics when the slice stayed empty
})
}
The same pattern is in the kinds block right below it.
I think this is worse than a normal error because the API server is started without any recovery interceptor (cmd/pipecd/server.go only adds log, api key auth, request validation and prometheus), so the panic is not caught and it stops the whole control plane process, not only that one request.
What you expected to happen:
If all given values are empty, no status/kind filter should be added and the request should be handled normally, like when no filter is given at all.
How to reproduce it:
The shortest way is with pipectl:
pipectl deployment list --status=,
--status=, looks strange but it is what a user gets from an empty item in a comma separated list. pflag parses the value as CSV, so , becomes two empty strings.
I did not want to guess how pflag behaves, so I checked it with a small test that declares the flag the same way pkg/app/pipectl/cmd/deployment/list.go does:
var statuses []string
fs := pflag.NewFlagSet("probe", pflag.ContinueOnError)
fs.StringSliceVar(&statuses, "status", nil, "")
fs.Parse([]string{arg})
Result:
--status= -> [] len=0 ok
--status=, -> ["" ""] len=2, none usable panics
--status="" -> [""] len=1, none usable panics
--status=,, -> ["" "" ""] len=3, none usable panics
--status=SUCCESS, -> ["SUCCESS" ""] len=2, one usable ok
pipectl lets those empty values through on purpose. In pkg/app/pipectl/cmd/deployment/list.go the validation skips them:
for _, status := range c.statuses {
if status != "" {
if _, ok := model.DeploymentStatus_value[status]; !ok {
return errors.Errorf("%s is invalid deployment status", status)
}
}
}
so they pass client side validation and reach the server as they are.
Any other gRPC client can also send statuses: [""] directly. The field has no validate rule in service.proto, so WithRequestValidationUnaryInterceptor does not reject it either.
To see the panic itself I called the handler directly:
ctx := rpcauth.ContextWithAPIKey(context.Background(), &model.APIKey{
ProjectId: "p", Role: model.APIKey_READ_ONLY,
})
api := &API{deploymentStore: someStore, logger: zap.NewNop()}
api.ListDeployments(ctx, &apiservice.ListDeploymentsRequest{
Statuses: []string{""},
Limit: 10,
})
panic: runtime error: index out of range [0] with length 0
github.com/pipe-cd/pipecd/pkg/app/server/grpcapi.(*API).ListDeployments(...)
/pkg/app/server/grpcapi/api.go:553
It never reaches the datastore, the panic happens while the filters are being built.
Environment:
pipedversion: not relatedcontrol-planeversion:masterat commit56fb80e7- Others:
pipectlon the same commit
I have a small fix and a regression test for this in #7099.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in pkg/app/server/grpcapi/api.go at API.ListDeployments and review the status and kind filter construction. Reproduce with pipectl deployment list --status=, or the direct handler call, then add or run a regression test covering only empty values. Done means the request reaches normal handling without a panic and no empty filter is added.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100