github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/cloudformation/parser/parser_test.go (about) 1 package parser 2 3 import ( 4 "context" 5 "os" 6 "path/filepath" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 ) 12 13 func parseFile(t *testing.T, source string, name string) (FileContexts, error) { 14 tmp, err := os.MkdirTemp(os.TempDir(), "defsec") 15 require.NoError(t, err) 16 defer func() { _ = os.RemoveAll(tmp) }() 17 require.NoError(t, os.WriteFile(filepath.Join(tmp, name), []byte(source), 0600)) 18 fs := os.DirFS(tmp) 19 return New().ParseFS(context.TODO(), fs, ".") 20 } 21 22 func Test_parse_yaml(t *testing.T) { 23 24 source := `--- 25 Parameters: 26 BucketName: 27 Type: String 28 Default: naughty 29 EncryptBucket: 30 Type: Boolean 31 Default: false 32 Resources: 33 S3Bucket: 34 Type: 'AWS::S3::Bucket' 35 Properties: 36 BucketName: naughty 37 BucketEncryption: 38 ServerSideEncryptionConfiguration: 39 - BucketKeyEnabled: 40 Ref: EncryptBucket` 41 42 files, err := parseFile(t, source, "cf.yaml") 43 require.NoError(t, err) 44 assert.Len(t, files, 1) 45 file := files[0] 46 47 assert.Len(t, file.Resources, 1) 48 assert.Len(t, file.Parameters, 2) 49 50 bucket, ok := file.Resources["S3Bucket"] 51 require.True(t, ok, "S3Bucket resource should be available") 52 assert.Equal(t, "cf.yaml", bucket.Range().GetFilename()) 53 assert.Equal(t, 10, bucket.Range().GetStartLine()) 54 assert.Equal(t, 17, bucket.Range().GetEndLine()) 55 } 56 57 func Test_parse_json(t *testing.T) { 58 source := `{ 59 "Parameters": { 60 "BucketName": { 61 "Type": "String", 62 "Default": "naughty" 63 }, 64 "BucketKeyEnabled": { 65 "Type": "Boolean", 66 "Default": false 67 } 68 }, 69 "Resources": { 70 "S3Bucket": { 71 "Type": "AWS::S3::Bucket", 72 "properties": { 73 "BucketName": { 74 "Ref": "BucketName" 75 }, 76 "BucketEncryption": { 77 "ServerSideEncryptionConfiguration": [ 78 { 79 "BucketKeyEnabled": { 80 "Ref": "BucketKeyEnabled" 81 } 82 } 83 ] 84 } 85 } 86 } 87 } 88 } 89 ` 90 91 files, err := parseFile(t, source, "cf.json") 92 require.NoError(t, err) 93 assert.Len(t, files, 1) 94 file := files[0] 95 96 assert.Len(t, file.Resources, 1) 97 assert.Len(t, file.Parameters, 2) 98 } 99 100 func Test_parse_yaml_with_map_ref(t *testing.T) { 101 102 source := `--- 103 Parameters: 104 BucketName: 105 Type: String 106 Default: referencedBucket 107 EncryptBucket: 108 Type: Boolean 109 Default: false 110 Resources: 111 S3Bucket: 112 Type: 'AWS::S3::Bucket' 113 Properties: 114 BucketName: 115 Ref: BucketName 116 BucketEncryption: 117 ServerSideEncryptionConfiguration: 118 - BucketKeyEnabled: 119 Ref: EncryptBucket` 120 121 files, err := parseFile(t, source, "cf.yaml") 122 require.NoError(t, err) 123 assert.Len(t, files, 1) 124 file := files[0] 125 126 assert.Len(t, file.Resources, 1) 127 assert.Len(t, file.Parameters, 2) 128 129 res := file.GetResourceByLogicalID("S3Bucket") 130 assert.NotNil(t, res) 131 132 refProp := res.GetProperty("BucketName") 133 assert.False(t, refProp.IsNil()) 134 assert.Equal(t, "referencedBucket", refProp.AsString()) 135 } 136 137 func Test_parse_yaml_with_intrinsic_functions(t *testing.T) { 138 139 source := `--- 140 Parameters: 141 BucketName: 142 Type: String 143 Default: somebucket 144 EncryptBucket: 145 Type: Boolean 146 Default: false 147 Resources: 148 S3Bucket: 149 Type: 'AWS::S3::Bucket' 150 Properties: 151 BucketName: !Ref BucketName 152 BucketEncryption: 153 ServerSideEncryptionConfiguration: 154 - BucketKeyEnabled: false 155 ` 156 157 files, err := parseFile(t, source, "cf.yaml") 158 require.NoError(t, err) 159 assert.Len(t, files, 1) 160 ctx := files[0] 161 162 assert.Len(t, ctx.Resources, 1) 163 assert.Len(t, ctx.Parameters, 2) 164 165 res := ctx.GetResourceByLogicalID("S3Bucket") 166 assert.NotNil(t, res) 167 168 refProp := res.GetProperty("BucketName") 169 assert.False(t, refProp.IsNil()) 170 assert.Equal(t, "somebucket", refProp.AsString()) 171 } 172 173 func createTestFileContext(t *testing.T, source string) *FileContext { 174 contexts, err := parseFile(t, source, "main.yaml") 175 require.NoError(t, err) 176 require.Len(t, contexts, 1) 177 return contexts[0] 178 } 179 180 func Test_parse_yaml_use_condition_in_resource(t *testing.T) { 181 source := `--- 182 AWSTemplateFormatVersion: "2010-09-09" 183 Description: some description 184 Parameters: 185 ServiceName: 186 Type: String 187 Description: The service name 188 EnvName: 189 Type: String 190 Description: Optional environment name to prefix all resources with 191 Default: "" 192 193 Conditions: 194 SuffixResources: !Not [!Equals [!Ref EnvName, ""]] 195 196 Resources: 197 ErrorTimedOutMetricFilter: 198 Type: AWS::Logs::MetricFilter 199 Properties: 200 FilterPattern: '?ERROR ?error ?Error ?"timed out"' # If log contains one of these error words or timed out 201 LogGroupName: 202 !If [ 203 SuffixResources, 204 !Sub "/aws/lambda/${ServiceName}-${EnvName}", 205 !Sub "/aws/lambda/${ServiceName}", 206 ] 207 MetricTransformations: 208 - MetricName: !Sub "${ServiceName}-ErrorLogCount" 209 MetricNamespace: market-LogMetrics 210 MetricValue: 1 211 DefaultValue: 0 212 ` 213 214 files, err := parseFile(t, source, "cf.yaml") 215 require.NoError(t, err) 216 assert.Len(t, files, 1) 217 ctx := files[0] 218 219 assert.Len(t, ctx.Parameters, 2) 220 assert.Len(t, ctx.Conditions, 1) 221 assert.Len(t, ctx.Resources, 1) 222 223 res := ctx.GetResourceByLogicalID("ErrorTimedOutMetricFilter") 224 assert.NotNil(t, res) 225 226 refProp := res.GetProperty("LogGroupName") 227 assert.False(t, refProp.IsNil()) 228 assert.Equal(t, "/aws/lambda/${ServiceName}", refProp.AsString()) 229 }