diff --git a/cmd/urunc/create.go b/cmd/urunc/create.go index 8911eae2..b016cb10 100644 --- a/cmd/urunc/create.go +++ b/cmd/urunc/create.go @@ -90,8 +90,7 @@ var createCommand = &cli.Command{ func createUnikontainer(cmd *cli.Command, uruncCfg *unikontainers.UruncConfig) (err error) { err = nil containerID := cmd.Args().First() - if containerID == "" { - err = fmt.Errorf("container id cannot be empty") + if err = unikontainers.ValidateID(containerID); err != nil { return err } metrics.SetLoggerContainerID(containerID) diff --git a/cmd/urunc/delete.go b/cmd/urunc/delete.go index 7ba25ec0..3817c64c 100644 --- a/cmd/urunc/delete.go +++ b/cmd/urunc/delete.go @@ -23,6 +23,7 @@ import ( "github.com/sirupsen/logrus" "github.com/urfave/cli/v3" + "github.com/urunc-dev/urunc/pkg/unikontainers" ) var deleteCommand = &cli.Command{ @@ -58,8 +59,8 @@ status of "ubuntu01" as "stopped" the following will delete resources held for if err != nil { if errors.Is(err, os.ErrNotExist) { containerID := cmd.Args().First() - if containerID == "" { - return ErrEmptyContainerID + if err = unikontainers.ValidateID(containerID); err != nil { + return err } rootDir := cmd.String("root") containerDir := filepath.Join(rootDir, containerID) diff --git a/cmd/urunc/utils.go b/cmd/urunc/utils.go index 2773d765..74a8ad28 100644 --- a/cmd/urunc/utils.go +++ b/cmd/urunc/utils.go @@ -36,8 +36,6 @@ const ( maxArgs // Checks for a maximum number of arguments. ) -var ErrEmptyContainerID = errors.New("container ID can not be empty") - // checkArgs checks the number of arguments provided in the command-line context // against the expected number, based on the specified checkType. func checkArgs(cmd *cli.Command, expected, checkType int) error { @@ -69,8 +67,8 @@ func checkArgs(cmd *cli.Command, expected, checkType int) error { func getUnikontainer(cmd *cli.Command) (*unikontainers.Unikontainer, error) { containerID := cmd.Args().First() - if containerID == "" { - return nil, ErrEmptyContainerID + if err := unikontainers.ValidateID(containerID); err != nil { + return nil, err } // We have already made sure in main.go that root is not nil diff --git a/pkg/unikontainers/unikontainers.go b/pkg/unikontainers/unikontainers.go index a3a00bb5..4d7b9069 100644 --- a/pkg/unikontainers/unikontainers.go +++ b/pkg/unikontainers/unikontainers.go @@ -51,6 +51,7 @@ var uniklog = logrus.WithField("subsystem", "unikontainers") var ErrQueueProxy = errors.New("this a queue proxy container") var ErrNotUnikernel = errors.New("this is not a unikernel container") var ErrNotExistingNS = errors.New("the namespace does not exist") +var ErrInvalidContainerID = errors.New("invalid container ID") // Unikontainer holds the data necessary to create, manage and delete unikernel containers type Unikontainer struct { @@ -63,8 +64,30 @@ type Unikontainer struct { Conn *net.UnixConn } +// ValidateID checks containerID against the allowed character set, +func ValidateID(id string) error { + if id == "" { + return ErrInvalidContainerID + } + for i := 0; i < len(id); i++ { + c := id[i] + switch { + case c >= 'a' && c <= 'z': + case c >= 'A' && c <= 'Z': + case c >= '0' && c <= '9': + case c == '_', c == '+', c == '-', c == '.': + default: + return fmt.Errorf("%w: invalid character %q in id %q", ErrInvalidContainerID, c, id) + } + } + return nil +} + // New parses the bundle and creates a new Unikontainer object func New(bundlePath string, containerID string, rootDir string, cfg *UruncConfig) (*Unikontainer, error) { + if err := ValidateID(containerID); err != nil { + return nil, err + } spec, err := loadSpec(bundlePath) if err != nil { return nil, err @@ -113,6 +136,9 @@ func New(bundlePath string, containerID string, rootDir string, cfg *UruncConfig // Get retrieves unikernel data from disk to create a Unikontainer object func Get(containerID string, rootDir string) (*Unikontainer, error) { + if err := ValidateID(containerID); err != nil { + return nil, err + } u := &Unikontainer{} containerDir := filepath.Join(rootDir, containerID) stateFilePath := filepath.Join(containerDir, stateFilename)