// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License. // Package ssmparameterresolver contains types and methods for resolving SSM Parameter references. package ssmparameterresolver import ( "reflect" "sort" "testing" "github.com/aws/amazon-ssm-agent/agent/log/logger" "github.com/stretchr/testify/assert" ) func TestExtractParametersFromText(t *testing.T) { expectedResult := map[string]SsmParameterInfo{ "ssm:/a/b/c/param1": {Name: "/a/b/c/param1", Type: stringType, Value: "value_/a/b/c/param1"}, "ssm-secure:param2": {Name: "param2", Type: secureStringType, Value: "value_param2"}, } serviceObject := newServiceMockedObjectWithExtraRecords(expectedResult) log := logger.DefaultLogger() text := "Some text {{ ssm:/a/b/c/param1}}, some more text {{ssm-secure:param2}}." resolvedParameters, err := ExtractParametersFromText(&serviceObject, log, text, ResolveOptions{ IgnoreSecureParameters: false, }) assert.Nil(t, err) assert.NotNil(t, resolvedParameters) assert.True(t, reflect.DeepEqual(resolvedParameters, expectedResult)) } func TestExtractParametersFromTextIgnoreSecureParams(t *testing.T) { serviceObject := newServiceMockedObjectWithExtraRecords(map[string]SsmParameterInfo{ "ssm:/a/b/c/param1": {Name: "/a/b/c/param1", Type: stringType, Value: "value_/a/b/c/param1"}, "ssm-secure:/a/b/c/param1": {Name: "param2", Type: secureStringType, Value: "value_/a/b/c/param1"}, "ssm-secure:param2": {Name: "param2", Type: secureStringType, Value: "value_param2"}, }) log := logger.DefaultLogger() text := "Some text {{ ssm:/a/b/c/param1}}, some more text {{ssm-secure:param2}} - {{ ssm-secure:/a/b/c/param1}}." resolvedParameters, err := ExtractParametersFromText(&serviceObject, log, text, ResolveOptions{ IgnoreSecureParameters: true, }) expectedResult := map[string]SsmParameterInfo{ "ssm:/a/b/c/param1": {Name: "/a/b/c/param1", Type: stringType, Value: "value_/a/b/c/param1"}, } assert.Nil(t, err) assert.NotNil(t, resolvedParameters) assert.True(t, reflect.DeepEqual(resolvedParameters, expectedResult)) } func TestExtractParametersFromTextWrongPrefix(t *testing.T) { expectedResult := map[string]SsmParameterInfo{ "ssm:/a/b/c/param1": {Name: "/a/b/c/param1", Type: stringType, Value: "value_/a/b/c/param1"}, "ssm:param2": {Name: "param2", Type: secureStringType, Value: "value_param2"}, // wrong prefix } serviceObject := newServiceMockedObjectWithExtraRecords(expectedResult) log := logger.DefaultLogger() text := "Some text {{ ssm:/a/b/c/param1}}, some more text {{ssm:param2}}." _, err := ExtractParametersFromText(&serviceObject, log, text, ResolveOptions{ IgnoreSecureParameters: false, }) assert.NotNil(t, err) } func TestResolveParameterReferenceList(t *testing.T) { expectedResult := map[string]SsmParameterInfo{ "ssm:param1": {Name: "param1", Type: stringType, Value: "value_param1"}, "ssm:param2": {Name: "param2", Type: stringType, Value: "value_param2"}, "ssm-secure:/a/b/param1": {Name: "/a/b/param1", Type: secureStringType, Value: "value_/a/b/param1"}, "ssm-secure:param4": {Name: "param4", Type: secureStringType, Value: "value_param4"}, } serviceObject := newServiceMockedObjectWithExtraRecords(expectedResult) parameterReferences := []string{ "ssm:param1", "ssm:param2", "ssm-secure:/a/b/param1", "ssm-secure:param4", } log := logger.DefaultLogger() resolvedParameters, err := ResolveParameterReferenceList(&serviceObject, log, parameterReferences, ResolveOptions{ IgnoreSecureParameters: false, }) assert.Nil(t, err) assert.NotNil(t, resolvedParameters) assert.True(t, reflect.DeepEqual(resolvedParameters, expectedResult)) } func TestResolveParameterReferenceListIgnoreSecureParams(t *testing.T) { serviceObject := newServiceMockedObjectWithExtraRecords(map[string]SsmParameterInfo{ "ssm:param1": {Name: "param1", Type: stringType, Value: "value_param1"}, "ssm:param2": {Name: "param2", Type: stringType, Value: "value_param2"}, "ssm-secure:/a/b/param1": {Name: "/a/b/param1", Type: secureStringType, Value: "value_/a/b/param1"}, "ssm-secure:param4": {Name: "param4", Type: secureStringType, Value: "value_param4"}, }) parameterReferences := []string{ "ssm:param1", "ssm:param2", "ssm-secure:/a/b/param1", "ssm-secure:param4", "ssm:param2", } log := logger.DefaultLogger() resolvedParameters, err := ResolveParameterReferenceList(&serviceObject, log, parameterReferences, ResolveOptions{ IgnoreSecureParameters: true, }) expectedResult := map[string]SsmParameterInfo{ "ssm:param1": {Name: "param1", Type: stringType, Value: "value_param1"}, "ssm:param2": {Name: "param2", Type: stringType, Value: "value_param2"}, } assert.Nil(t, err) assert.NotNil(t, resolvedParameters) assert.True(t, reflect.DeepEqual(resolvedParameters, expectedResult)) } func TestParseParametersFromTextIntoMapSecureAllowed(t *testing.T) { text := "Some text {{ ssm:/a/b/c/param1}}, some more text {{ssm-secure:param2}}, {{ ssm-secure:/a/b/c/param1 }}." expectedList := []string{"ssm:/a/b/c/param1", "ssm-secure:param2", "ssm-secure:/a/b/c/param1"} list, err := parseParametersFromTextIntoDedupedSlice(text, false) assert.Nil(t, err) assert.NotNil(t, list) sort.Slice(expectedList, func(i, j int) bool { return expectedList[i] < expectedList[j] }) sort.Slice(list, func(i, j int) bool { return list[i] < list[j] }) assert.True(t, reflect.DeepEqual(list, expectedList)) } func TestParseParametersFromTextIntoMapSecureNotAllowed(t *testing.T) { text := "Some text {{ ssm:/a/b/c/param1}}, some more text {{ssm-secure:param2}}, {{ ssm-secure:/a/b/c/param1 }}." expectedList := []string{"ssm:/a/b/c/param1"} list, err := parseParametersFromTextIntoDedupedSlice(text, true) assert.Nil(t, err) assert.NotNil(t, list) sort.Slice(expectedList, func(i, j int) bool { return expectedList[i] < expectedList[j] }) sort.Slice(list, func(i, j int) bool { return list[i] < list[j] }) assert.True(t, reflect.DeepEqual(list, expectedList)) } func TestResolveParametersInText(t *testing.T) { serviceObject := newServiceMockedObjectWithExtraRecords(map[string]SsmParameterInfo{ "ssm:/a/b/c/param1": {Name: "/a/b/c/param1", Type: stringType, Value: "value_/a/b/c/param1"}, "ssm-secure:param2": {Name: "param2", Type: secureStringType, Value: "value_param2"}, }) log := logger.DefaultLogger() text := "Some text {{ ssm:/a/b/c/param1}}, some more text {{ssm-secure:param2}}. {{ ssm:/a/b/c/param1 }};" output, err := ResolveParametersInText(&serviceObject, log, text, ResolveOptions{ IgnoreSecureParameters: false, }) expectedOutput := `Some text value_/a/b/c/param1, some more text value_param2. value_/a/b/c/param1;` assert.Nil(t, err) assert.NotNil(t, output) assert.True(t, expectedOutput == output) } func TestResolveParametersInTextIgnoreSecureParams(t *testing.T) { serviceObject := newServiceMockedObjectWithExtraRecords(map[string]SsmParameterInfo{ "ssm:/a/b/c/param1": {Name: "/a/b/c/param1", Type: stringType, Value: "value_/a/b/c/param1"}, "ssm-secure:param2": {Name: "param2", Type: secureStringType, Value: "value_param2"}, }) log := logger.DefaultLogger() text := "Some text {{ ssm:/a/b/c/param1}}, some more text {{ssm-secure:param2}}." output, err := ResolveParametersInText(&serviceObject, log, text, ResolveOptions{ IgnoreSecureParameters: true, }) expectedOutput := `Some text value_/a/b/c/param1, some more text {{ssm-secure:param2}}.` assert.Nil(t, err) assert.NotNil(t, output) assert.True(t, expectedOutput == output) } func TestTextContainsSsmParameters(t *testing.T) { type testCase struct { Input string Output bool } testCases := []testCase{ { Input: "", Output: false, }, { Input: "a string without a parameter", Output: false, }, { Input: "a string with an {{ invalid parameter name}}", Output: false, }, { Input: "a string with a {{ parameter }}", Output: false, }, { Input: "a string with a {{ ssm:a/b/c/parameter }}", Output: true, }, { Input: "a string with a {{ ssm-secure:parameter }}", Output: false, }, } for _, tst := range testCases { actual := TextContainsSsmParameters(tst.Input) assert.Equal(t, tst.Output, actual) } } func TestTextContainsSsmSecureParameters(t *testing.T) { type testCase struct { Input string Output bool } testCases := []testCase{ { Input: "", Output: false, }, { Input: "a string without a parameter", Output: false, }, { Input: "a string with an {{ invalid parameter name}}", Output: false, }, { Input: "a string with a {{ parameter }}", Output: false, }, { Input: "a string with a {{ ssm:parameter }}", Output: false, }, { Input: "a string with a {{ ssm-secure:parameter }}", Output: true, }, } for _, tst := range testCases { actual := TextContainsSecureSsmParameters(tst.Input) assert.Equal(t, tst.Output, actual) } }