From b6d58d749c6d671f0dc19a88b948b772272e145d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 8 Apr 2022 23:27:50 +0200 Subject: [PATCH] runconfig: ContainerDecoder(): fix handling of invalid JSON Implement similar logic as is used in httputils.ReadJSON(). Before this patch, endpoints using the ContainerDecoder would incorrectly return a 500 (internal server error) status. Signed-off-by: Sebastiaan van Stijn --- integration/container/container_test.go | 5 +++- runconfig/config.go | 17 +++++++++++- runconfig/config_test.go | 37 +++++++++++++------------ runconfig/hostconfig.go | 3 +- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/integration/container/container_test.go b/integration/container/container_test.go index 06dce0f776..81c5bb7685 100644 --- a/integration/container/container_test.go +++ b/integration/container/container_test.go @@ -17,6 +17,8 @@ func TestContainerInvalidJSON(t *testing.T) { // POST endpoints that accept / expect a JSON body; endpoints := []string{ + "/commit", + "/containers/create", "/containers/foobar/exec", "/containers/foobar/update", "/exec/foobar/start", @@ -26,7 +28,8 @@ func TestContainerInvalidJSON(t *testing.T) { if runtime.GOOS != "windows" { endpoints = append( endpoints, - "/v1.23/containers/foobar/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24) + "/v1.23/containers/foobar/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24) + "/v1.23/containers/foobar/start", // accepts a body on API < v1.24 ) } diff --git a/runconfig/config.go b/runconfig/config.go index 1b71b2042f..b25d1a8aa3 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -40,7 +40,7 @@ func (r ContainerDecoder) DecodeHostConfig(src io.Reader) (*container.HostConfig // it's your business to do so func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) { var w ContainerConfigWrapper - if err := json.NewDecoder(src).Decode(&w); err != nil { + if err := loadJSON(src, &w); err != nil { return nil, nil, nil, err } @@ -72,3 +72,18 @@ func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Confi } return w.Config, hc, w.NetworkingConfig, nil } + +// loadJSON is similar to api/server/httputils.ReadJSON() +func loadJSON(src io.Reader, out interface{}) error { + dec := json.NewDecoder(src) + if err := dec.Decode(&out); err != nil { + if err == io.EOF { + return validationError("invalid JSON: got EOF while reading request body") + } + return validationError("invalid JSON: " + err.Error()) + } + if dec.More() { + return validationError("unexpected content after JSON") + } + return nil +} diff --git a/runconfig/config_test.go b/runconfig/config_test.go index 5325f5a754..bbadcce297 100644 --- a/runconfig/config_test.go +++ b/runconfig/config_test.go @@ -42,27 +42,30 @@ func TestDecodeContainerConfig(t *testing.T) { } for _, f := range fixtures { - b, err := os.ReadFile(f.file) - if err != nil { - t.Fatal(err) - } + f := f + t.Run(f.file, func(t *testing.T) { + b, err := os.ReadFile(f.file) + if err != nil { + t.Fatal(err) + } - c, h, _, err := decodeContainerConfig(bytes.NewReader(b), sysinfo.New()) - if err != nil { - t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err)) - } + c, h, _, err := decodeContainerConfig(bytes.NewReader(b), sysinfo.New()) + if err != nil { + t.Fatal(err) + } - if c.Image != image { - t.Fatalf("Expected %s image, found %s\n", image, c.Image) - } + if c.Image != image { + t.Fatalf("Expected %s image, found %s", image, c.Image) + } - if len(c.Entrypoint) != len(f.entrypoint) { - t.Fatalf("Expected %v, found %v\n", f.entrypoint, c.Entrypoint) - } + if len(c.Entrypoint) != len(f.entrypoint) { + t.Fatalf("Expected %v, found %v", f.entrypoint, c.Entrypoint) + } - if h != nil && h.Memory != 1000 { - t.Fatalf("Expected memory to be 1000, found %d\n", h.Memory) - } + if h != nil && h.Memory != 1000 { + t.Fatalf("Expected memory to be 1000, found %d", h.Memory) + } + }) } } diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 1d6266d5f7..9603a94eca 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -1,7 +1,6 @@ package runconfig // import "github.com/docker/docker/runconfig" import ( - "encoding/json" "io" "strings" @@ -12,7 +11,7 @@ import ( // It assumes the content of the reader will be JSON, and decodes it. func decodeHostConfig(src io.Reader) (*container.HostConfig, error) { var w ContainerConfigWrapper - if err := json.NewDecoder(src).Decode(&w); err != nil { + if err := loadJSON(src, &w); err != nil { return nil, err } return w.getHostConfig(), nil