github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/internal/adapters/terraform/aws/s3/adapt_test.go (about) 1 package s3 2 3 import ( 4 "testing" 5 6 defsecTypes "github.com/khulnasoft-lab/defsec/pkg/types" 7 8 "github.com/khulnasoft-lab/defsec/internal/adapters/terraform/tftestutil" 9 "github.com/khulnasoft-lab/defsec/pkg/providers/aws/iam" 10 "github.com/khulnasoft-lab/defsec/pkg/providers/aws/s3" 11 "github.com/khulnasoft-lab/defsec/test/testutil" 12 "github.com/liamg/iamgo" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func Test_PublicAccessBlock(t *testing.T) { 18 testCases := []struct { 19 desc string 20 source string 21 expectedBuckets int 22 hasPublicAccess bool 23 }{ 24 { 25 desc: "public access block is found when using the bucket name as the lookup", 26 source: ` 27 resource "aws_s3_bucket" "example" { 28 bucket = "bucketname" 29 } 30 31 resource "aws_s3_bucket_public_access_block" "example_access_block"{ 32 bucket = "bucketname" 33 } 34 `, 35 expectedBuckets: 1, 36 hasPublicAccess: true, 37 }, 38 { 39 desc: "public access block is found when using the bucket name as the lookup", 40 source: ` 41 resource "aws_s3_bucket" "example" { 42 bucket = "bucketname" 43 } 44 45 resource "aws_s3_bucket_public_access_block" "example_access_block"{ 46 bucket = aws_s3_bucket.example.id 47 } 48 `, 49 expectedBuckets: 1, 50 hasPublicAccess: true, 51 }, 52 } 53 for _, tC := range testCases { 54 t.Run(tC.desc, func(t *testing.T) { 55 56 modules := tftestutil.CreateModulesFromSource(t, tC.source, ".tf") 57 s3Ctx := Adapt(modules) 58 59 assert.Equal(t, tC.expectedBuckets, len(s3Ctx.Buckets)) 60 61 for _, bucket := range s3Ctx.Buckets { 62 if tC.hasPublicAccess { 63 assert.NotNil(t, bucket.PublicAccessBlock) 64 } else { 65 assert.Nil(t, bucket.PublicAccessBlock) 66 } 67 } 68 69 bucket := s3Ctx.Buckets[0] 70 assert.NotNil(t, bucket.PublicAccessBlock) 71 72 }) 73 } 74 75 } 76 77 func Test_PublicAccessDoesNotReference(t *testing.T) { 78 testCases := []struct { 79 desc string 80 source string 81 }{ 82 { 83 desc: "just a bucket, no public access block", 84 source: ` 85 resource "aws_s3_bucket" "example" { 86 bucket = "bucketname" 87 } 88 `, 89 }, 90 { 91 desc: "bucket with unrelated public access block", 92 source: ` 93 resource "aws_s3_bucket" "example" { 94 bucket = "bucketname" 95 } 96 97 resource "aws_s3_bucket_public_access_block" "example_access_block"{ 98 bucket = aws_s3_bucket.other.id 99 } 100 `, 101 }, 102 { 103 desc: "bucket with unrelated public access block via name", 104 source: ` 105 resource "aws_s3_bucket" "example" { 106 bucket = "bucketname" 107 } 108 109 resource "aws_s3_bucket_public_access_block" "example_access_block"{ 110 bucket = "something" 111 } 112 `, 113 }, 114 } 115 for _, tC := range testCases { 116 t.Run(tC.desc, func(t *testing.T) { 117 modules := tftestutil.CreateModulesFromSource(t, tC.source, ".tf") 118 s3Ctx := Adapt(modules) 119 require.Len(t, s3Ctx.Buckets, 1) 120 assert.Nil(t, s3Ctx.Buckets[0].PublicAccessBlock) 121 122 }) 123 } 124 } 125 126 func Test_Adapt(t *testing.T) { 127 tests := []struct { 128 name string 129 terraform string 130 expected s3.S3 131 }{ 132 { 133 name: "basic", 134 terraform: ` 135 resource "aws_s3_bucket" "example" { 136 bucket = "bucket" 137 } 138 139 resource "aws_s3_bucket_public_access_block" "example" { 140 bucket = aws_s3_bucket.example.id 141 142 restrict_public_buckets = true 143 block_public_acls = true 144 block_public_policy = true 145 ignore_public_acls = true 146 147 } 148 149 resource "aws_s3_bucket_acl" "example" { 150 bucket = aws_s3_bucket.example.id 151 acl = "private" 152 } 153 154 resource "aws_s3_bucket_server_side_encryption_configuration" "example" { 155 bucket = aws_s3_bucket.example.bucket 156 157 rule { 158 apply_server_side_encryption_by_default { 159 kms_master_key_id = "string-key" 160 sse_algorithm = "aws:kms" 161 } 162 } 163 } 164 165 resource "aws_s3_bucket_logging" "example" { 166 bucket = aws_s3_bucket.example.id 167 168 target_bucket = aws_s3_bucket.example.id 169 target_prefix = "log/" 170 } 171 172 resource "aws_s3_bucket_versioning" "versioning_example" { 173 bucket = aws_s3_bucket.example.id 174 versioning_configuration { 175 status = "Enabled" 176 mfa_delete = "Enabled" 177 } 178 } 179 180 resource "aws_s3_bucket_policy" "allow_access_from_another_account" { 181 bucket = aws_s3_bucket.example.bucket 182 policy = data.aws_iam_policy_document.allow_access_from_another_account.json 183 } 184 185 data "aws_iam_policy_document" "allow_access_from_another_account" { 186 statement { 187 188 actions = [ 189 "s3:GetObject", 190 "s3:ListBucket", 191 ] 192 193 resources = [ 194 "arn:aws:s3:::*", 195 ] 196 } 197 } 198 `, 199 expected: s3.S3{ 200 Buckets: []s3.Bucket{ 201 { 202 Metadata: defsecTypes.NewTestMetadata(), 203 Name: defsecTypes.String("bucket", defsecTypes.NewTestMetadata()), 204 PublicAccessBlock: &s3.PublicAccessBlock{ 205 Metadata: defsecTypes.NewTestMetadata(), 206 BlockPublicACLs: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 207 BlockPublicPolicy: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 208 IgnorePublicACLs: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 209 RestrictPublicBuckets: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 210 }, 211 BucketPolicies: []iam.Policy{ 212 { 213 Metadata: defsecTypes.NewTestMetadata(), 214 Name: defsecTypes.String("", defsecTypes.NewTestMetadata()), 215 Document: func() iam.Document { 216 217 builder := iamgo.NewPolicyBuilder() 218 219 sb := iamgo.NewStatementBuilder() 220 sb.WithEffect(iamgo.EffectAllow) 221 sb.WithActions([]string{"s3:GetObject", "s3:ListBucket"}) 222 sb.WithResources([]string{"arn:aws:s3:::*"}) 223 224 builder.WithStatement(sb.Build()) 225 226 return iam.Document{ 227 Parsed: builder.Build(), 228 Metadata: defsecTypes.NewTestMetadata(), 229 IsOffset: true, 230 HasRefs: false, 231 } 232 }(), 233 Builtin: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()), 234 }, 235 }, 236 Encryption: s3.Encryption{ 237 Metadata: defsecTypes.NewTestMetadata(), 238 Enabled: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 239 Algorithm: defsecTypes.String("aws:kms", defsecTypes.NewTestMetadata()), 240 KMSKeyId: defsecTypes.String("string-key", defsecTypes.NewTestMetadata()), 241 }, 242 Versioning: s3.Versioning{ 243 Metadata: defsecTypes.NewTestMetadata(), 244 Enabled: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 245 MFADelete: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 246 }, 247 Logging: s3.Logging{ 248 Metadata: defsecTypes.NewTestMetadata(), 249 Enabled: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 250 TargetBucket: defsecTypes.String("aws_s3_bucket.example", defsecTypes.NewTestMetadata()), 251 }, 252 ACL: defsecTypes.String("private", defsecTypes.NewTestMetadata()), 253 }, 254 }, 255 }, 256 }, 257 } 258 259 for _, test := range tests { 260 t.Run(test.name, func(t *testing.T) { 261 modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") 262 adapted := Adapt(modules) 263 testutil.AssertDefsecEqual(t, test.expected, adapted) 264 }) 265 } 266 } 267 268 func TestLines(t *testing.T) { 269 src := ` 270 resource "aws_s3_bucket" "example" { 271 bucket = "bucket" 272 } 273 274 resource "aws_s3_bucket_public_access_block" "example" { 275 bucket = aws_s3_bucket.example.id 276 277 restrict_public_buckets = true 278 block_public_acls = true 279 block_public_policy = true 280 ignore_public_acls = true 281 } 282 283 resource "aws_s3_bucket_acl" "example" { 284 bucket = aws_s3_bucket.example.id 285 acl = "private" 286 } 287 288 resource "aws_s3_bucket_server_side_encryption_configuration" "example" { 289 bucket = aws_s3_bucket.example.bucket 290 291 rule { 292 apply_server_side_encryption_by_default { 293 kms_master_key_id = "string-key" 294 sse_algorithm = "aws:kms" 295 } 296 } 297 } 298 299 resource "aws_s3_bucket_logging" "example" { 300 bucket = aws_s3_bucket.example.id 301 302 target_bucket = aws_s3_bucket.example.id 303 target_prefix = "log/" 304 } 305 306 resource "aws_s3_bucket_versioning" "versioning_example" { 307 bucket = aws_s3_bucket.example.id 308 versioning_configuration { 309 status = "Enabled" 310 } 311 } 312 313 resource "aws_s3_bucket_policy" "allow_access_from_another_account" { 314 bucket = aws_s3_bucket.example.bucket 315 policy = data.aws_iam_policy_document.allow_access_from_another_account.json 316 } 317 318 data "aws_iam_policy_document" "allow_access_from_another_account" { 319 statement { 320 321 actions = [ 322 "s3:GetObject", 323 "s3:ListBucket", 324 ] 325 326 resources = [ 327 "arn:aws:s3:::*", 328 ] 329 } 330 }` 331 332 modules := tftestutil.CreateModulesFromSource(t, src, ".tf") 333 adapted := Adapt(modules) 334 335 require.Len(t, adapted.Buckets, 1) 336 bucket := adapted.Buckets[0] 337 338 assert.Equal(t, 2, bucket.Metadata.Range().GetStartLine()) 339 assert.Equal(t, 4, bucket.Metadata.Range().GetEndLine()) 340 341 assert.Equal(t, 6, bucket.PublicAccessBlock.Metadata.Range().GetStartLine()) 342 assert.Equal(t, 13, bucket.PublicAccessBlock.Metadata.Range().GetEndLine()) 343 344 assert.Equal(t, 9, bucket.PublicAccessBlock.RestrictPublicBuckets.GetMetadata().Range().GetStartLine()) 345 assert.Equal(t, 9, bucket.PublicAccessBlock.RestrictPublicBuckets.GetMetadata().Range().GetEndLine()) 346 347 assert.Equal(t, 10, bucket.PublicAccessBlock.BlockPublicACLs.GetMetadata().Range().GetStartLine()) 348 assert.Equal(t, 10, bucket.PublicAccessBlock.BlockPublicACLs.GetMetadata().Range().GetEndLine()) 349 350 assert.Equal(t, 11, bucket.PublicAccessBlock.BlockPublicPolicy.GetMetadata().Range().GetStartLine()) 351 assert.Equal(t, 11, bucket.PublicAccessBlock.BlockPublicPolicy.GetMetadata().Range().GetEndLine()) 352 353 assert.Equal(t, 12, bucket.PublicAccessBlock.IgnorePublicACLs.GetMetadata().Range().GetStartLine()) 354 assert.Equal(t, 12, bucket.PublicAccessBlock.IgnorePublicACLs.GetMetadata().Range().GetEndLine()) 355 356 assert.Equal(t, 17, bucket.ACL.GetMetadata().Range().GetStartLine()) 357 assert.Equal(t, 17, bucket.ACL.GetMetadata().Range().GetEndLine()) 358 359 assert.Equal(t, 20, bucket.Encryption.Metadata.Range().GetStartLine()) 360 assert.Equal(t, 29, bucket.Encryption.Metadata.Range().GetEndLine()) 361 362 assert.Equal(t, 25, bucket.Encryption.KMSKeyId.GetMetadata().Range().GetStartLine()) 363 assert.Equal(t, 25, bucket.Encryption.KMSKeyId.GetMetadata().Range().GetEndLine()) 364 365 assert.Equal(t, 26, bucket.Encryption.Algorithm.GetMetadata().Range().GetStartLine()) 366 assert.Equal(t, 26, bucket.Encryption.Algorithm.GetMetadata().Range().GetEndLine()) 367 368 assert.Equal(t, 31, bucket.Logging.Metadata.Range().GetStartLine()) 369 assert.Equal(t, 36, bucket.Logging.Metadata.Range().GetEndLine()) 370 371 assert.Equal(t, 34, bucket.Logging.TargetBucket.GetMetadata().Range().GetStartLine()) 372 assert.Equal(t, 34, bucket.Logging.TargetBucket.GetMetadata().Range().GetEndLine()) 373 374 assert.Equal(t, 38, bucket.Versioning.Metadata.Range().GetStartLine()) 375 assert.Equal(t, 43, bucket.Versioning.Metadata.Range().GetEndLine()) 376 377 assert.Equal(t, 41, bucket.Versioning.Enabled.GetMetadata().Range().GetStartLine()) 378 assert.Equal(t, 41, bucket.Versioning.Enabled.GetMetadata().Range().GetEndLine()) 379 380 assert.Equal(t, 47, bucket.BucketPolicies[0].Metadata.Range().GetStartLine()) 381 assert.Equal(t, 47, bucket.BucketPolicies[0].Metadata.Range().GetEndLine()) 382 383 assert.Equal(t, 50, bucket.BucketPolicies[0].Document.Metadata.Range().GetStartLine()) 384 assert.Equal(t, 62, bucket.BucketPolicies[0].Document.Metadata.Range().GetEndLine()) 385 }