Skip to content

Commit

Permalink
Validate that SSM params have leading '/'
Browse files Browse the repository at this point in the history
A value like 'ssm://foo/bar' will currently get picked up as a parameter
named 'foo/bar', but then the lookup in AWS will fail. These changes add
a simple validation that the path has a leading '/'.
  • Loading branch information
aengelas committed Jun 6, 2024
1 parent 70be7a2 commit 4cbfae7
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 29 deletions.
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ func (e *expander) expandEnviron(decrypt bool, nofail bool) error {
}

if parameter != nil {
// Ensure that this is a valid SSM parameter that we can actually resolve.
if !strings.HasPrefix(*parameter, "/") {
return fmt.Errorf("SSM parameters must have a leading '/' (ssm:///<path>): %s", envvar)
}

uniqNames[*parameter] = true
ssmVars = append(ssmVars, ssmVar{k, *parameter})
}
Expand Down
76 changes: 47 additions & 29 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ func TestExpandEnviron_SimpleSSMParameter(t *testing.T) {
batchSize: defaultBatchSize,
}

os.Setenv("SUPER_SECRET", "ssm://secret")
os.Setenv("SUPER_SECRET", "ssm:///secret")

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret")},
Names: []*string{aws.String("/secret")},
WithDecryption: aws.Bool(true),
}).Return(&ssm.GetParametersOutput{
Parameters: []*ssm.Parameter{
{Name: aws.String("secret"), Value: aws.String("hehe")},
{Name: aws.String("/secret"), Value: aws.String("hehe")},
},
}, nil)

Expand Down Expand Up @@ -80,14 +80,14 @@ func TestExpandEnviron_VersionedSSMParameter(t *testing.T) {
batchSize: defaultBatchSize,
}

os.Setenv("SUPER_SECRET", "ssm://secret:1")
os.Setenv("SUPER_SECRET", "ssm:///secret:1")

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret:1")},
Names: []*string{aws.String("/secret:1")},
WithDecryption: aws.Bool(true),
}).Return(&ssm.GetParametersOutput{
Parameters: []*ssm.Parameter{
{Name: aws.String("secret"), Value: aws.String("versioned"), Selector: aws.String(":1")},
{Name: aws.String("/secret"), Value: aws.String("versioned"), Selector: aws.String(":1")},
},
}, nil)

Expand All @@ -109,20 +109,20 @@ func TestExpandEnviron_CustomTemplate(t *testing.T) {
os := newFakeEnviron()
c := new(mockSSM)
e := expander{
t: template.Must(parseTemplate(`{{ if eq .Name "SUPER_SECRET" }}secret{{end}}`)),
t: template.Must(parseTemplate(`{{ if eq .Name "SUPER_SECRET" }}/secret{{end}}`)),
os: os,
ssm: c,
batchSize: defaultBatchSize,
}

os.Setenv("SUPER_SECRET", "ssm://secret")
os.Setenv("SUPER_SECRET", "ssm:///secret")

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret")},
Names: []*string{aws.String("/secret")},
WithDecryption: aws.Bool(true),
}).Return(&ssm.GetParametersOutput{
Parameters: []*ssm.Parameter{
{Name: aws.String("secret"), Value: aws.String("hehe")},
{Name: aws.String("/secret"), Value: aws.String("hehe")},
},
}, nil)

Expand Down Expand Up @@ -150,15 +150,15 @@ func TestExpandEnviron_DuplicateSSMParameter(t *testing.T) {
batchSize: defaultBatchSize,
}

os.Setenv("SUPER_SECRET_A", "ssm://secret")
os.Setenv("SUPER_SECRET_B", "ssm://secret")
os.Setenv("SUPER_SECRET_A", "ssm:///secret")
os.Setenv("SUPER_SECRET_B", "ssm:///secret")

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret")},
Names: []*string{aws.String("/secret")},
WithDecryption: aws.Bool(false),
}).Return(&ssm.GetParametersOutput{
Parameters: []*ssm.Parameter{
{Name: aws.String("secret"), Value: aws.String("hehe")},
{Name: aws.String("/secret"), Value: aws.String("hehe")},
},
}, nil)

Expand All @@ -177,7 +177,7 @@ func TestExpandEnviron_DuplicateSSMParameter(t *testing.T) {
c.AssertExpectations(t)
}

func TestExpandEnviron_InvalidParameters(t *testing.T) {
func TestExpandEnviron_MalformedParameters(t *testing.T) {
os := newFakeEnviron()
c := new(mockSSM)
e := expander{
Expand All @@ -189,17 +189,35 @@ func TestExpandEnviron_InvalidParameters(t *testing.T) {

os.Setenv("SUPER_SECRET", "ssm://secret")

decrypt := false
nofail := false
err := e.expandEnviron(decrypt, nofail)
assert.Containsf(t, err.Error(), "SSM parameters must have a leading '/'", "")
}

func TestExpandEnviron_InvalidParameters(t *testing.T) {
os := newFakeEnviron()
c := new(mockSSM)
e := expander{
t: template.Must(parseTemplate(DefaultTemplate)),
os: os,
ssm: c,
batchSize: defaultBatchSize,
}

os.Setenv("SUPER_SECRET", "ssm:///bad.secret")

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret")},
Names: []*string{aws.String("/bad.secret")},
WithDecryption: aws.Bool(false),
}).Return(&ssm.GetParametersOutput{
InvalidParameters: []*string{aws.String("secret")},
InvalidParameters: []*string{aws.String("/bad.secret")},
}, nil)

decrypt := false
nofail := false
err := e.expandEnviron(decrypt, nofail)
assert.Equal(t, &invalidParametersError{InvalidParameters: []string{"secret"}}, err)
assert.Equal(t, &invalidParametersError{InvalidParameters: []string{"/bad.secret"}}, err)

c.AssertExpectations(t)
}
Expand All @@ -214,23 +232,23 @@ func TestExpandEnviron_InvalidParametersNoFail(t *testing.T) {
batchSize: defaultBatchSize,
}

os.Setenv("SUPER_SECRET", "ssm://secret")
os.Setenv("SUPER_SECRET", "ssm:///secret")

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret")},
Names: []*string{aws.String("/secret")},
WithDecryption: aws.Bool(false),
}).Return(&ssm.GetParametersOutput{
InvalidParameters: []*string{aws.String("secret")},
InvalidParameters: []*string{aws.String("/secret")},
}, nil)

decrypt := false
nofail := true
err := e.expandEnviron(decrypt, nofail)

assert.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, []string{
"SHELL=/bin/bash",
"SUPER_SECRET=ssm://secret",
"SUPER_SECRET=ssm:///secret",
"TERM=screen-256color",
}, os.Environ())

Expand All @@ -247,24 +265,24 @@ func TestExpandEnviron_BatchParameters(t *testing.T) {
batchSize: 1,
}

os.Setenv("SUPER_SECRET_A", "ssm://secret-a")
os.Setenv("SUPER_SECRET_B", "ssm://secret-b")
os.Setenv("SUPER_SECRET_A", "ssm:///secret-a")
os.Setenv("SUPER_SECRET_B", "ssm:///secret-b")

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret-a")},
Names: []*string{aws.String("/secret-a")},
WithDecryption: aws.Bool(false),
}).Return(&ssm.GetParametersOutput{
Parameters: []*ssm.Parameter{
{Name: aws.String("secret-a"), Value: aws.String("val-a")},
{Name: aws.String("/secret-a"), Value: aws.String("val-a")},
},
}, nil)

c.On("GetParameters", &ssm.GetParametersInput{
Names: []*string{aws.String("secret-b")},
Names: []*string{aws.String("/secret-b")},
WithDecryption: aws.Bool(false),
}).Return(&ssm.GetParametersOutput{
Parameters: []*ssm.Parameter{
{Name: aws.String("secret-b"), Value: aws.String("val-b")},
{Name: aws.String("/secret-b"), Value: aws.String("val-b")},
},
}, nil)

Expand Down

0 comments on commit 4cbfae7

Please sign in to comment.