Skip to content

Commit 2128cc5

Browse files
committed
fix(ssm_document): bounds-check, typed error, pattern coverage, comments
- Add bounds-check on CommandInvocations[0] in commandOutputContains and VerifyAgentAction to prevent panics on empty invocations slice. - Replace string matching with errors.As(smithy.APIError) + ErrorCode() in VerifySSMSendCommandRejection for robust error detection. - Add cross-reference comment on WaitForCommandCompletion string coupling in RunAndVerifySSMActionFailure. - Add allowedPattern-rejection cases for backtick value and malformed key (digit prefix) via VerifySSMSendCommandRejection. - Add clarifying comments on setEnvEmptyTest omitted status fields and update Validate doc comment to mention both envConfigPath and platformSetup.
1 parent d4efafa commit 2128cc5

4 files changed

Lines changed: 58 additions & 5 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
github.com/aws/aws-sdk-go-v2/service/sts v1.26.2
2626
github.com/aws/aws-sdk-go-v2/service/xray v1.23.2
2727
github.com/aws/aws-xray-sdk-go v1.8.3
28+
github.com/aws/smithy-go v1.18.1
2829
github.com/cenkalti/backoff/v4 v4.2.1
2930
github.com/google/uuid v1.4.0
3031
github.com/mitchellh/mapstructure v1.5.0
@@ -64,7 +65,6 @@ require (
6465
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.8 // indirect
6566
github.com/aws/aws-sdk-go-v2/service/sso v1.18.2 // indirect
6667
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.2 // indirect
67-
github.com/aws/smithy-go v1.18.1 // indirect
6868
github.com/davecgh/go-spew v1.1.1 // indirect
6969
github.com/go-logr/logr v1.3.0 // indirect
7070
github.com/go-logr/stdr v1.2.2 // indirect

test/ssm_document/constants.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ const (
1818
actionSetEnvEmpty = "set-env (empty)"
1919
actionSetEnvInvalidPattern = "set-env (invalid pattern)"
2020

21+
// actionSetEnvInvalidBacktick exercises rejection of a value containing a backtick.
22+
actionSetEnvInvalidBacktick = "set-env (invalid backtick)"
23+
24+
// actionSetEnvInvalidKey exercises rejection of a malformed key starting with a digit.
25+
actionSetEnvInvalidKey = "set-env (invalid key)"
26+
2127
// Parameters
2228
paramAction = "action"
2329
paramOptionalConfigurationSource = "optionalConfigurationSource"
@@ -40,6 +46,14 @@ const (
4046
// optionalEnvironmentVariable allowedPattern and is rejected by SSM at SendCommand.
4147
setEnvInvalidPatternValue = "CWA_TEST_INVALID=$not_allowed"
4248

49+
// setEnvInvalidBacktickValue contains a backtick which violates the document's
50+
// optionalEnvironmentVariable allowedPattern and is rejected by SSM at SendCommand.
51+
setEnvInvalidBacktickValue = "CWA_TEST_BACKTICK=val`ue"
52+
53+
// setEnvInvalidKeyValue has a malformed key starting with a digit, violating the
54+
// allowedPattern's key portion ([A-Za-z_][A-Za-z0-9_]*=...) and rejected at SendCommand.
55+
setEnvInvalidKeyValue = "1FOO=bar"
56+
4357
// setEnvOutputPrefix is printed by the ctl on a successful set-env ("Set <KEY>").
4458
setEnvOutputPrefix = "Set "
4559

test/ssm_document/helper.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ package ssm_document
55

66
import (
77
"encoding/json"
8+
"errors"
89
"fmt"
910
"log"
1011
"os"
1112
"strings"
1213

1314
"github.com/aws/aws-sdk-go-v2/service/ssm"
1415
"github.com/aws/aws-sdk-go-v2/service/ssm/types"
16+
"github.com/aws/smithy-go"
1517

1618
"github.com/aws/amazon-cloudwatch-agent-test/util/awsservice"
1719
)
@@ -65,6 +67,10 @@ func VerifyAgentAction(out *ssm.SendCommandOutput, instanceId, documentName stri
6567
return fmt.Errorf("failed to get status result: %v", err)
6668
}
6769

70+
if len(statusResult.CommandInvocations) == 0 {
71+
return fmt.Errorf("no command invocations returned for status check")
72+
}
73+
6874
for _, plugin := range statusResult.CommandInvocations[0].CommandPlugins {
6975
if plugin.Status == types.CommandPluginStatusFailed {
7076
return fmt.Errorf("command plugin failed: %s", *plugin.Name)
@@ -138,6 +144,7 @@ func RunAndVerifySSMActionFailure(documentName string, instanceIds []string, tc
138144
return fmt.Errorf("%s action was expected to fail but succeeded\nCommand output:\n%s", tc.actionName, commandOutput)
139145
}
140146
// WaitForCommandCompletion also errors on Cancelled/TimedOut/deadline; require Failed specifically.
147+
// The "terminal status " prefix is produced by awsservice.WaitForCommandCompletion.
141148
if !strings.Contains(err.Error(), "terminal status "+string(types.CommandInvocationStatusFailed)) {
142149
return fmt.Errorf("%s action reached an unexpected terminal state: %v\nCommand output:\n%s", tc.actionName, err, commandOutput)
143150
}
@@ -181,6 +188,9 @@ func VerifyEnvConfigContent(expected map[string]string) error {
181188

182189
// commandOutputContains reports whether any command plugin's output contains expected.
183190
func commandOutputContains(result *ssm.ListCommandInvocationsOutput, expected string) bool {
191+
if len(result.CommandInvocations) == 0 {
192+
return false
193+
}
184194
for _, plugin := range result.CommandInvocations[0].CommandPlugins {
185195
if plugin.Output != nil && strings.Contains(*plugin.Output, expected) {
186196
return true
@@ -200,9 +210,10 @@ func VerifySSMSendCommandRejection(documentName string, instanceIds []string, tc
200210
return fmt.Errorf("%s action was expected to be rejected at SendCommand but succeeded", tc.actionName)
201211
}
202212

203-
// SSM returns an InvalidParameters error when allowedPattern validation fails.
204-
if !strings.Contains(err.Error(), "InvalidParameters") {
205-
return fmt.Errorf("%s action failed with unexpected error (expected InvalidParameters): %v", tc.actionName, err)
213+
// SSM returns an InvalidParameters API error when allowedPattern validation fails.
214+
var apiErr smithy.APIError
215+
if !errors.As(err, &apiErr) || apiErr.ErrorCode() != "InvalidParameters" {
216+
return fmt.Errorf("%s action failed with unexpected error (expected InvalidParameters API error): %v", tc.actionName, err)
206217
}
207218

208219
log.Printf("%s action rejected at SendCommand as expected: %v", tc.actionName, err)

test/ssm_document/validate.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import (
1616
)
1717

1818
// Validate runs the full SSM Document integration test sequence. Platform-specific
19-
// constants (envConfigPath) are defined in the build-tagged platform files.
19+
// constants (envConfigPath) and initialization (platformSetup) are defined in the
20+
// build-tagged platform files (ssm_document_unix.go, ssm_document_windows.go).
2021
func Validate() error {
2122
log.Println("Starting SSM Document validation tests")
2223

@@ -207,8 +208,35 @@ func Validate() error {
207208
return err
208209
}
209210

211+
// Test set-env action (allowedPattern rejection): a value containing a backtick violates
212+
// the allowedPattern and is rejected by SSM at SendCommand time.
213+
if err := VerifySSMSendCommandRejection(documentName, instanceIds, testCase{
214+
parameters: map[string][]string{
215+
paramAction: {actionSetEnv},
216+
paramOptionalEnvironmentVariable: {setEnvInvalidBacktickValue},
217+
},
218+
actionName: actionSetEnvInvalidBacktick,
219+
}); err != nil {
220+
return err
221+
}
222+
223+
// Test set-env action (allowedPattern rejection): a key starting with a digit violates
224+
// the [A-Za-z_] prefix requirement and is rejected by SSM at SendCommand time.
225+
if err := VerifySSMSendCommandRejection(documentName, instanceIds, testCase{
226+
parameters: map[string][]string{
227+
paramAction: {actionSetEnv},
228+
paramOptionalEnvironmentVariable: {setEnvInvalidKeyValue},
229+
},
230+
actionName: actionSetEnvInvalidKey,
231+
}); err != nil {
232+
return err
233+
}
234+
210235
// Test set-env action (error path): empty optionalEnvironmentVariable must fail
211236
// with the document-level error message.
237+
// expectedAgentStatus and expectedConfigStatus are intentionally omitted: the action
238+
// fails at the document level (RunAndVerifySSMActionFailure), so VerifyAgentAction
239+
// is never called and those fields are unused.
212240
setEnvEmptyTest := testCase{
213241
parameters: map[string][]string{
214242
paramAction: {actionSetEnv},

0 commit comments

Comments
 (0)