diff --git a/libcni/api.go b/libcni/api.go index e0642b940..fdaca17ef 100644 --- a/libcni/api.go +++ b/libcni/api.go @@ -25,6 +25,7 @@ import ( "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/utils" "github.com/containernetworking/cni/pkg/version" ) @@ -379,6 +380,12 @@ func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net if err != nil { return nil, err } + if err := utils.ValidateContainerID(rt.ContainerID); err != nil { + return nil, err + } + if err := utils.ValidateNetworkName(name); err != nil { + return nil, err + } newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) if err != nil { diff --git a/libcni/api_test.go b/libcni/api_test.go index 48f96c81b..073921455 100644 --- a/libcni/api_test.go +++ b/libcni/api_test.go @@ -1004,6 +1004,36 @@ var _ = Describe("Invoking plugins", func() { }) }) + Context("when there is an invalid containerID", func() { + BeforeEach(func() { + runtimeConfig.ContainerID = "some-%%container-id" + }) + + It("returns the error", func() { + _, err := cniConfig.AddNetworkList(ctx, netConfigList, runtimeConfig) + Expect(err).To(Equal(&types.Error{ + Code: 6, + Msg: "error: invalid characters in containerID", + Details: "some-%%container-id", + })) + }) + }) + + Context("when there is an invalid networkName", func() { + BeforeEach(func() { + netConfigList.Name = "invalid-%%-name" + }) + + It("returns the error", func() { + _, err := cniConfig.AddNetworkList(ctx, netConfigList, runtimeConfig) + Expect(err).To(Equal(&types.Error{ + Code: 7, + Msg: "error: invalid characters found in network name", + Details: "invalid-%%-name", + })) + }) + }) + Context("when the second plugin errors", func() { BeforeEach(func() { plugins[1].debug.ReportError = "plugin error: banana" diff --git a/pkg/skel/skel.go b/pkg/skel/skel.go index f8be8080e..4c15c46eb 100644 --- a/pkg/skel/skel.go +++ b/pkg/skel/skel.go @@ -27,6 +27,7 @@ import ( "strings" "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/utils" "github.com/containernetworking/cni/pkg/version" ) @@ -199,6 +200,9 @@ func validateConfig(jsonBytes []byte) *types.Error { if conf.Name == "" { return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "") } + if err := utils.ValidateNetworkName(conf.Name); err != nil { + return err + } return nil } @@ -218,6 +222,10 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, if err != nil { return err } + err = utils.ValidateContainerID(cmdArgs.ContainerID) + if err != nil { + return err + } } switch cmd { diff --git a/pkg/skel/skel_test.go b/pkg/skel/skel_test.go index d1d95c25a..facb7b2e5 100644 --- a/pkg/skel/skel_test.go +++ b/pkg/skel/skel_test.go @@ -114,6 +114,17 @@ var _ = Describe("dispatching to the correct callback", func() { Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs)) }) + It("returns an error when containerID has invalid characters", func() { + environment["CNI_CONTAINERID"] = "some-%%container-id" + err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(&types.Error{ + Code: 6, + Msg: "error: invalid characters in containerID", + Details: "some-%%container-id", + })) + }) + It("does not call cmdCheck or cmdDel", func() { err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") @@ -279,6 +290,18 @@ var _ = Describe("dispatching to the correct callback", func() { }) }) + Context("when the config has a bad name", func() { + It("immediately returns invalid network config", func() { + dispatch.Stdin = strings.NewReader(`{ "cniVersion": "0.4.0", "some": "config", "name": "te%%st" }`) + versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.4.0") + err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") + Expect(err.Code).To(Equal(uint(types.ErrInvalidNetworkConfig))) + Expect(cmdAdd.CallCount).To(Equal(0)) + Expect(cmdCheck.CallCount).To(Equal(0)) + Expect(cmdDel.CallCount).To(Equal(0)) + }) + }) + Context("when the plugin has a bad version", func() { It("immediately returns a useful error", func() { dispatch.Stdin = strings.NewReader(`{ "cniVersion": "0.4.0", "some": "config", "name": "test" }`) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 000000000..901af2d1f --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,51 @@ +// Copyright 2019 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "regexp" + + "github.com/containernetworking/cni/pkg/types" +) + +// ValidInputString is the regexp used to validate valid characters in +// containerID and networkName +const cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]` + +var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `+$`) + +// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters +func ValidateContainerID(containerID string) *types.Error { + + if containerID == "" { + return types.NewError(types.ErrUnknownContainer, "missing containerID", "") + } + if !cniReg.MatchString(containerID) { + return types.NewError(types.ErrDecodingFailure, "error: invalid characters in containerID", containerID) + } + return nil +} + +// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters +func ValidateNetworkName(networkName string) *types.Error { + + if networkName == "" { + return types.NewError(types.ErrInvalidNetworkConfig, "error: missing network name:", "") + } + if !cniReg.MatchString(networkName) { + return types.NewError(types.ErrInvalidNetworkConfig, "error: invalid characters found in network name", networkName) + } + return nil +}