github.com/aquasecurity/trivy-iac@v0.8.1-0.20240127024015-3d8e412cf0ab/internal/adapters/terraform/aws/s3/bucket.go (about)

     1  package s3
     2  
     3  import (
     4  	"github.com/aquasecurity/defsec/pkg/providers/aws/s3"
     5  	"github.com/aquasecurity/defsec/pkg/terraform"
     6  	defsecTypes "github.com/aquasecurity/defsec/pkg/types"
     7  )
     8  
     9  type adapter struct {
    10  	modules   terraform.Modules
    11  	bucketMap map[string]*s3.Bucket
    12  }
    13  
    14  func (a *adapter) adaptBuckets() []s3.Bucket {
    15  	for _, block := range a.modules.GetResourcesByType("aws_s3_bucket") {
    16  		bucket := &s3.Bucket{
    17  			Metadata:                      block.GetMetadata(),
    18  			Name:                          block.GetAttribute("bucket").AsStringValueOrDefault("", block),
    19  			PublicAccessBlock:             nil,
    20  			BucketPolicies:                nil,
    21  			Encryption:                    getEncryption(block, a),
    22  			Versioning:                    getVersioning(block, a),
    23  			Logging:                       getLogging(block, a),
    24  			ACL:                           getBucketAcl(block, a),
    25  			AccelerateConfigurationStatus: getAccelerateStatus(block, a),
    26  			BucketLocation:                block.GetAttribute("region").AsStringValueOrDefault("", block),
    27  			LifecycleConfiguration:        getLifecycle(block, a),
    28  			Website:                       getWebsite(block, a),
    29  			Objects:                       getObject(block, a),
    30  		}
    31  		a.bucketMap[block.ID()] = bucket
    32  	}
    33  
    34  	a.adaptBucketPolicies()
    35  	a.adaptPublicAccessBlocks()
    36  
    37  	var buckets []s3.Bucket
    38  	for _, bucket := range a.bucketMap {
    39  		buckets = append(buckets, *bucket)
    40  	}
    41  
    42  	return buckets
    43  }
    44  
    45  func getEncryption(block *terraform.Block, a *adapter) s3.Encryption {
    46  	if sseConfgihuration := block.GetBlock("server_side_encryption_configuration"); sseConfgihuration != nil {
    47  		return newS3Encryption(block, sseConfgihuration)
    48  	}
    49  	if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_server_side_encryption_configuration", func(resource *terraform.Block) s3.Encryption {
    50  		return newS3Encryption(resource, resource)
    51  	}); ok {
    52  		return val
    53  	}
    54  	return s3.Encryption{
    55  		Metadata:  block.GetMetadata(),
    56  		Enabled:   defsecTypes.BoolDefault(false, block.GetMetadata()),
    57  		KMSKeyId:  defsecTypes.StringDefault("", block.GetMetadata()),
    58  		Algorithm: defsecTypes.StringDefault("", block.GetMetadata()),
    59  	}
    60  }
    61  
    62  func newS3Encryption(root *terraform.Block, sseConfgihuration *terraform.Block) s3.Encryption {
    63  	return s3.Encryption{
    64  		Metadata: root.GetMetadata(),
    65  		Enabled:  isEncrypted(sseConfgihuration),
    66  		Algorithm: terraform.MapNestedAttribute(
    67  			sseConfgihuration,
    68  			"rule.apply_server_side_encryption_by_default.sse_algorithm",
    69  			func(attr *terraform.Attribute, parent *terraform.Block) defsecTypes.StringValue {
    70  				return attr.AsStringValueOrDefault("", parent)
    71  			},
    72  		),
    73  		KMSKeyId: terraform.MapNestedAttribute(
    74  			sseConfgihuration,
    75  			"rule.apply_server_side_encryption_by_default.kms_master_key_id",
    76  			func(attr *terraform.Attribute, parent *terraform.Block) defsecTypes.StringValue {
    77  				return attr.AsStringValueOrDefault("", parent)
    78  			},
    79  		),
    80  	}
    81  }
    82  
    83  func getVersioning(block *terraform.Block, a *adapter) s3.Versioning {
    84  	versioning := s3.Versioning{
    85  		Metadata:  block.GetMetadata(),
    86  		Enabled:   defsecTypes.BoolDefault(false, block.GetMetadata()),
    87  		MFADelete: defsecTypes.BoolDefault(false, block.GetMetadata()),
    88  	}
    89  	if lockBlock := block.GetBlock("object_lock_configuration"); lockBlock != nil {
    90  		if enabled := isObjeckLockEnabled(lockBlock); enabled != nil {
    91  			versioning.Enabled = *enabled
    92  		}
    93  	}
    94  	if vBlock := block.GetBlock("versioning"); vBlock != nil {
    95  		versioning.Enabled = vBlock.GetAttribute("enabled").AsBoolValueOrDefault(true, vBlock)
    96  		versioning.MFADelete = vBlock.GetAttribute("mfa_delete").AsBoolValueOrDefault(false, vBlock)
    97  	}
    98  
    99  	if enabled, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_object_lock_configuration", func(resource *terraform.Block) *defsecTypes.BoolValue {
   100  		if block.GetAttribute("object_lock_enabled").IsTrue() {
   101  			return isObjeckLockEnabled(resource)
   102  		}
   103  		return nil
   104  	}); ok && enabled != nil {
   105  		versioning.Enabled = *enabled
   106  	}
   107  
   108  	if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_versioning", getVersioningFromResource); ok {
   109  		return val
   110  	}
   111  	return versioning
   112  }
   113  
   114  func isObjeckLockEnabled(resource *terraform.Block) *defsecTypes.BoolValue {
   115  	var val defsecTypes.BoolValue
   116  	attr := resource.GetAttribute("object_lock_enabled")
   117  	switch {
   118  	case attr.IsNil(): // enabled by default
   119  		val = defsecTypes.BoolDefault(true, resource.GetMetadata())
   120  	case attr.Equals("Enabled"):
   121  		val = defsecTypes.Bool(true, attr.GetMetadata())
   122  	}
   123  	return &val
   124  }
   125  
   126  // from aws_s3_bucket_versioning
   127  func getVersioningFromResource(block *terraform.Block) s3.Versioning {
   128  	versioning := s3.Versioning{
   129  		Metadata:  block.GetMetadata(),
   130  		Enabled:   defsecTypes.BoolDefault(false, block.GetMetadata()),
   131  		MFADelete: defsecTypes.BoolDefault(false, block.GetMetadata()),
   132  	}
   133  	if config := block.GetBlock("versioning_configuration"); config != nil {
   134  		if status := config.GetAttribute("status"); status.IsNotNil() {
   135  			versioning.Enabled = defsecTypes.Bool(status.Equals("Enabled", terraform.IgnoreCase), status.GetMetadata())
   136  		}
   137  		if mfa := config.GetAttribute("mfa_delete"); mfa.IsNotNil() {
   138  			versioning.MFADelete = defsecTypes.Bool(mfa.Equals("Enabled", terraform.IgnoreCase), mfa.GetMetadata())
   139  		}
   140  	}
   141  	return versioning
   142  }
   143  
   144  func getLogging(block *terraform.Block, a *adapter) s3.Logging {
   145  	if loggingBlock := block.GetBlock("logging"); loggingBlock.IsNotNil() {
   146  		targetBucket := loggingBlock.GetAttribute("target_bucket").AsStringValueOrDefault("", loggingBlock)
   147  		if referencedBlock, err := a.modules.GetReferencedBlock(loggingBlock.GetAttribute("target_bucket"), loggingBlock); err == nil {
   148  			targetBucket = defsecTypes.String(referencedBlock.FullName(), loggingBlock.GetAttribute("target_bucket").GetMetadata())
   149  		}
   150  		return s3.Logging{
   151  			Metadata:     loggingBlock.GetMetadata(),
   152  			Enabled:      defsecTypes.Bool(true, loggingBlock.GetMetadata()),
   153  			TargetBucket: targetBucket,
   154  		}
   155  	}
   156  
   157  	if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_logging", func(resource *terraform.Block) s3.Logging {
   158  		targetBucket := resource.GetAttribute("target-bucket").AsStringValueOrDefault("", resource)
   159  		if referencedBlock, err := a.modules.GetReferencedBlock(resource.GetAttribute("target_bucket"), resource); err == nil {
   160  			targetBucket = defsecTypes.String(referencedBlock.FullName(), resource.GetAttribute("target_bucket").GetMetadata())
   161  		}
   162  		return s3.Logging{
   163  			Metadata:     resource.GetMetadata(),
   164  			Enabled:      hasLogging(resource),
   165  			TargetBucket: targetBucket,
   166  		}
   167  	}); ok {
   168  		return val
   169  	}
   170  
   171  	return s3.Logging{
   172  		Metadata:     block.GetMetadata(),
   173  		Enabled:      defsecTypes.Bool(false, block.GetMetadata()),
   174  		TargetBucket: defsecTypes.StringDefault("", block.GetMetadata()),
   175  	}
   176  }
   177  
   178  func getBucketAcl(block *terraform.Block, a *adapter) defsecTypes.StringValue {
   179  	aclAttr := block.GetAttribute("acl")
   180  	if aclAttr.IsString() {
   181  		return aclAttr.AsStringValueOrDefault("private", block)
   182  	}
   183  
   184  	if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_acl", func(resource *terraform.Block) defsecTypes.StringValue {
   185  		return resource.GetAttribute("acl").AsStringValueOrDefault("private", resource)
   186  	}); ok {
   187  		return val
   188  	}
   189  	return defsecTypes.StringDefault("private", block.GetMetadata())
   190  }
   191  
   192  func isEncrypted(sseConfgihuration *terraform.Block) defsecTypes.BoolValue {
   193  	return terraform.MapNestedAttribute(
   194  		sseConfgihuration,
   195  		"rule.apply_server_side_encryption_by_default.sse_algorithm",
   196  		func(attr *terraform.Attribute, parent *terraform.Block) defsecTypes.BoolValue {
   197  			if attr.IsNil() {
   198  				return defsecTypes.BoolDefault(false, parent.GetMetadata())
   199  			}
   200  			return defsecTypes.Bool(
   201  				true,
   202  				attr.GetMetadata(),
   203  			)
   204  		},
   205  	)
   206  }
   207  
   208  func hasLogging(b *terraform.Block) defsecTypes.BoolValue {
   209  	if loggingBlock := b.GetBlock("logging"); loggingBlock.IsNotNil() {
   210  		if targetAttr := loggingBlock.GetAttribute("target_bucket"); targetAttr.IsNotNil() && targetAttr.IsNotEmpty() {
   211  			return defsecTypes.Bool(true, targetAttr.GetMetadata())
   212  		}
   213  		return defsecTypes.BoolDefault(false, loggingBlock.GetMetadata())
   214  	}
   215  	if targetBucket := b.GetAttribute("target_bucket"); targetBucket.IsNotNil() {
   216  		return defsecTypes.Bool(true, targetBucket.GetMetadata())
   217  	}
   218  	return defsecTypes.BoolDefault(false, b.GetMetadata())
   219  }
   220  
   221  func getLifecycle(b *terraform.Block, a *adapter) []s3.Rules {
   222  
   223  	var rules []s3.Rules
   224  	for _, r := range a.modules.GetReferencingResources(b, "aws_s3_bucket_lifecycle_configuration", "bucket") {
   225  		ruleblock := r.GetBlocks("rule")
   226  		for _, rule := range ruleblock {
   227  			rules = append(rules, s3.Rules{
   228  				Metadata: rule.GetMetadata(),
   229  				Status:   rule.GetAttribute("status").AsStringValueOrDefault("Enabled", rule),
   230  			})
   231  		}
   232  	}
   233  	return rules
   234  }
   235  
   236  func getWebsite(b *terraform.Block, a *adapter) (website *s3.Website) {
   237  	for _, r := range a.modules.GetReferencingResources(b, "aws_s3_bucket_website_configuration", "bucket") {
   238  		website = &s3.Website{
   239  			Metadata: r.GetMetadata(),
   240  		}
   241  	}
   242  	return website
   243  }
   244  
   245  func getObject(b *terraform.Block, a *adapter) []s3.Contents {
   246  	var object []s3.Contents
   247  	for _, r := range a.modules.GetReferencingResources(b, "aws_s3_object", "bucket") {
   248  		object = append(object, s3.Contents{
   249  			Metadata: r.GetMetadata(),
   250  		})
   251  	}
   252  	return object
   253  }
   254  
   255  func getAccelerateStatus(b *terraform.Block, a *adapter) defsecTypes.StringValue {
   256  	var status defsecTypes.StringValue
   257  	for _, r := range a.modules.GetReferencingResources(b, " aws_s3_bucket_accelerate_configuration", "bucket") {
   258  		status = r.GetAttribute("status").AsStringValueOrDefault("Enabled", r)
   259  	}
   260  	return status
   261  }
   262  
   263  func applyForBucketRelatedResource[T any](a *adapter, block *terraform.Block, resType string, fn func(resource *terraform.Block) T) (T, bool) {
   264  	for _, resource := range a.modules.GetResourcesByType(resType) {
   265  		bucketAttr := resource.GetAttribute("bucket")
   266  		if bucketAttr.IsNotNil() {
   267  			if bucketAttr.IsString() {
   268  				actualBucketName := block.GetAttribute("bucket").AsStringValueOrDefault("", block).Value()
   269  				if bucketAttr.Equals(block.ID()) || bucketAttr.Equals(actualBucketName) {
   270  					return fn(resource), true
   271  				}
   272  			}
   273  			if referencedBlock, err := a.modules.GetReferencedBlock(bucketAttr, resource); err == nil {
   274  				if referencedBlock.ID() == block.ID() {
   275  					return fn(resource), true
   276  				}
   277  			}
   278  		}
   279  
   280  	}
   281  	var res T
   282  	return res, false
   283  }