context: Ensure context name is valid on import

Signed-off-by: Chris Crone <christopher.crone@docker.com>
This commit is contained in:
Chris Crone 2020-09-24 16:24:24 +02:00 committed by Tibor Vass
parent 6f49197cab
commit 9ecc69d17e
7 changed files with 40 additions and 25 deletions

View File

@ -1,10 +1,6 @@
package context package context
import ( import (
"errors"
"fmt"
"regexp"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -30,20 +26,3 @@ func NewContextCommand(dockerCli command.Cli) *cobra.Command {
) )
return cmd return cmd
} }
const restrictedNamePattern = "^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$"
var restrictedNameRegEx = regexp.MustCompile(restrictedNamePattern)
func validateContextName(name string) error {
if name == "" {
return errors.New("context name cannot be empty")
}
if name == "default" {
return errors.New(`"default" is a reserved context name`)
}
if !restrictedNameRegEx.MatchString(name) {
return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
}
return nil
}

View File

@ -137,7 +137,7 @@ func createNewContext(o *CreateOptions, stackOrchestrator command.Orchestrator,
} }
func checkContextNameForCreation(s store.Reader, name string) error { func checkContextNameForCreation(s store.Reader, name string) error {
if err := validateContextName(name); err != nil { if err := store.ValidateContextName(name); err != nil {
return err return err
} }
if _, err := s.GetMetadata(name); !store.IsErrContextDoesNotExist(err) { if _, err := s.GetMetadata(name); !store.IsErrContextDoesNotExist(err) {

View File

@ -77,7 +77,7 @@ func writeTo(dockerCli command.Cli, reader io.Reader, dest string) error {
// RunExport exports a Docker context // RunExport exports a Docker context
func RunExport(dockerCli command.Cli, opts *ExportOptions) error { func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
if err := validateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName { if err := store.ValidateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName {
return err return err
} }
ctxMeta, err := dockerCli.ContextStore().GetMetadata(opts.ContextName) ctxMeta, err := dockerCli.ContextStore().GetMetadata(opts.ContextName)

View File

@ -68,7 +68,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
// RunUpdate updates a Docker context // RunUpdate updates a Docker context
func RunUpdate(cli command.Cli, o *UpdateOptions) error { func RunUpdate(cli command.Cli, o *UpdateOptions) error {
if err := validateContextName(o.Name); err != nil { if err := store.ValidateContextName(o.Name); err != nil {
return err return err
} }
s := cli.ContextStore() s := cli.ContextStore()

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context/store"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -23,7 +24,7 @@ func newUseCommand(dockerCli command.Cli) *cobra.Command {
// RunUse set the current Docker context // RunUse set the current Docker context
func RunUse(dockerCli command.Cli, name string) error { func RunUse(dockerCli command.Cli, name string) error {
if err := validateContextName(name); err != nil && name != "default" { if err := store.ValidateContextName(name); err != nil && name != "default" {
return err return err
} }
if _, err := dockerCli.ContextStore().GetMetadata(name); err != nil && name != "default" { if _, err := dockerCli.ContextStore().GetMetadata(name); err != nil && name != "default" {

View File

@ -13,6 +13,7 @@ import (
"net/http" "net/http"
"path" "path"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
@ -20,6 +21,10 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
const restrictedNamePattern = "^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$"
var restrictedNameRegEx = regexp.MustCompile(restrictedNamePattern)
// Store provides a context store for easily remembering endpoints configuration // Store provides a context store for easily remembering endpoints configuration
type Store interface { type Store interface {
Reader Reader
@ -184,6 +189,20 @@ func (s *store) GetStorageInfo(contextName string) StorageInfo {
} }
} }
// ValidateContextName checks a context name is valid.
func ValidateContextName(name string) error {
if name == "" {
return errors.New("context name cannot be empty")
}
if name == "default" {
return errors.New(`"default" is a reserved context name`)
}
if !restrictedNameRegEx.MatchString(name) {
return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
}
return nil
}
// Export exports an existing namespace into an opaque data stream // Export exports an existing namespace into an opaque data stream
// This stream is actually a tarball containing context metadata and TLS materials, but it does // This stream is actually a tarball containing context metadata and TLS materials, but it does
// not map 1:1 the layout of the context store (don't try to restore it manually without calling store.Import) // not map 1:1 the layout of the context store (don't try to restore it manually without calling store.Import)
@ -427,6 +446,9 @@ func parseMetadata(data []byte, name string) (Metadata, error) {
if err := json.Unmarshal(data, &meta); err != nil { if err := json.Unmarshal(data, &meta); err != nil {
return meta, err return meta, err
} }
if err := ValidateContextName(name); err != nil {
return Metadata{}, err
}
meta.Name = name meta.Name = name
return meta, nil return meta, nil
} }

View File

@ -45,3 +45,16 @@ func TestValidFilePaths(t *testing.T) {
assert.Equal(t, err == nil, expectedValid, "%q should report valid as: %v", p, expectedValid) assert.Equal(t, err == nil, expectedValid, "%q should report valid as: %v", p, expectedValid)
} }
} }
func TestValidateContextName(t *testing.T) {
names := map[string]bool{
"../../invalid/escape": false,
"/invalid/absolute": false,
`\invalid\windows`: false,
"validname": true,
}
for n, expectedValid := range names {
err := ValidateContextName(n)
assert.Equal(t, err == nil, expectedValid, "%q should report valid as: %v", n, expectedValid)
}
}