Reject names that aren't url-safe

Environment names can be used in a number of contexts, and should be
properly escaped for safety. Since most state names are store in path
structures, and often in a URL, use `url.PathEscape` to check for
disallowed characters
This commit is contained in:
James Bardin 2017-03-27 16:16:09 -04:00
parent 3e8a8c5e23
commit 9d118325b3
5 changed files with 68 additions and 1 deletions

View File

@ -1,6 +1,9 @@
package command package command
import "strings" import (
"net/url"
"strings"
)
// EnvCommand is a Command Implementation that manipulates local state // EnvCommand is a Command Implementation that manipulates local state
// environments. // environments.
@ -39,6 +42,13 @@ func (c *EnvCommand) Synopsis() string {
return "Environment management" return "Environment management"
} }
// validEnvName returns true is this name is valid to use as an environment name.
// Since most named states are accessed via a filesystem path or URL, check if
// escaping the name would be required.
func validEnvName(name string) bool {
return name == url.PathEscape(name)
}
const ( const (
envNotSupported = `Backend does not support environments` envNotSupported = `Backend does not support environments`
@ -81,5 +91,10 @@ Environment %[1]q is your active environment!
You cannot delete the currently active environment. Please switch You cannot delete the currently active environment. Please switch
to another environment and try again. to another environment and try again.
`
envInvalidName = `
The environment name %q is not allowed. The name must contain only URL safe
characters, and no path separators.
` `
) )

View File

@ -103,6 +103,44 @@ func TestEnv_createAndList(t *testing.T) {
} }
} }
// Don't allow names that aren't URL safe
func TestEnv_createInvalid(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
newCmd := &EnvNewCommand{}
envs := []string{"test_a*", "test_b/foo", "../../../test_c", "好_d"}
// create multiple envs
for _, env := range envs {
ui := new(cli.MockUi)
newCmd.Meta = Meta{Ui: ui}
if code := newCmd.Run([]string{env}); code == 0 {
t.Fatalf("expected failure: \n%s", ui.OutputWriter)
}
}
// list envs to make sure none were created
listCmd := &EnvListCommand{}
ui := new(cli.MockUi)
listCmd.Meta = Meta{Ui: ui}
if code := listCmd.Run(nil); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}
actual := strings.TrimSpace(ui.OutputWriter.String())
expected := "* default"
if actual != expected {
t.Fatalf("\nexpected: %q\nactual: %q", expected, actual)
}
}
func TestEnv_createWithState(t *testing.T) { func TestEnv_createWithState(t *testing.T) {
td := tempDir(t) td := tempDir(t)
os.MkdirAll(td, 0755) os.MkdirAll(td, 0755)

View File

@ -32,6 +32,11 @@ func (c *EnvDeleteCommand) Run(args []string) int {
delEnv := args[0] delEnv := args[0]
if !validEnvName(delEnv) {
c.Ui.Error(fmt.Sprintf(envInvalidName, delEnv))
return 1
}
configPath, err := ModulePath(args[1:]) configPath, err := ModulePath(args[1:])
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())

View File

@ -35,6 +35,11 @@ func (c *EnvNewCommand) Run(args []string) int {
newEnv := args[0] newEnv := args[0]
if !validEnvName(newEnv) {
c.Ui.Error(fmt.Sprintf(envInvalidName, newEnv))
return 1
}
configPath, err := ModulePath(args[1:]) configPath, err := ModulePath(args[1:])
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())

View File

@ -39,6 +39,10 @@ func (c *EnvSelectCommand) Run(args []string) int {
} }
name := args[0] name := args[0]
if !validEnvName(name) {
c.Ui.Error(fmt.Sprintf(envInvalidName, name))
return 1
}
states, err := b.States() states, err := b.States()
if err != nil { if err != nil {