mirror of https://github.com/docker/cli.git
context: Ensure context name is valid on import
Signed-off-by: Chris Crone <christopher.crone@docker.com>
(cherry picked from commit 9ecc69d17e
)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
a2f0cf527b
commit
8c2872d2a3
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue