From a817fae85b7272b391b737ec86673a7cab722bae Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Wed, 7 Aug 2024 06:42:31 +0700 Subject: [PATCH] feat(misconf): support for policy and bucket grants (#7284) Signed-off-by: nikpivkin --- .../adapters/cloudformation/aws/s3/bucket.go | 35 ++++++++++++ .../adapters/cloudformation/aws/s3/s3_test.go | 33 ++++++++++++ .../adapters/terraform/aws/s3/adapt_test.go | 54 +++++++++++++++++++ pkg/iac/adapters/terraform/aws/s3/bucket.go | 50 +++++++++++++++++ pkg/iac/providers/aws/s3/bucket.go | 13 +++++ pkg/iac/rego/schemas/cloud.json | 44 +++++++++++++++ 6 files changed, 229 insertions(+) diff --git a/pkg/iac/adapters/cloudformation/aws/s3/bucket.go b/pkg/iac/adapters/cloudformation/aws/s3/bucket.go index 5f5329fc6714..ec56894dbee4 100644 --- a/pkg/iac/adapters/cloudformation/aws/s3/bucket.go +++ b/pkg/iac/adapters/cloudformation/aws/s3/bucket.go @@ -7,7 +7,9 @@ import ( "strings" s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/liamg/iamgo" + "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" @@ -37,6 +39,7 @@ func getBuckets(cfFile parser.FileContext) []s3.Bucket { Website: getWebsite(r), BucketLocation: iacTypes.String("", r.Metadata()), Objects: nil, + BucketPolicies: getBucketPolicies(cfFile, r), } buckets = append(buckets, s3b) @@ -156,3 +159,35 @@ func getWebsite(r *parser.Resource) *s3.Website { } } } + +func getBucketPolicies(fctx parser.FileContext, r *parser.Resource) []iam.Policy { + + var policies []iam.Policy + for _, bucketPolicy := range fctx.GetResourcesByType("AWS::S3::BucketPolicy") { + bucket := bucketPolicy.GetStringProperty("Bucket") + if bucket.NotEqualTo(r.GetStringProperty("BucketName").Value()) && bucket.NotEqualTo(r.ID()) { + continue + } + + doc := bucketPolicy.GetProperty("PolicyDocument") + if doc.IsNil() { + continue + } + + parsed, err := iamgo.Parse(doc.GetJsonBytes()) + if err != nil { + continue + } + policies = append(policies, iam.Policy{ + Metadata: doc.Metadata(), + Name: iacTypes.StringDefault("", doc.Metadata()), + Document: iam.Document{ + Metadata: doc.Metadata(), + Parsed: *parsed, + }, + Builtin: iacTypes.Bool(false, doc.Metadata()), + }) + } + + return policies +} diff --git a/pkg/iac/adapters/cloudformation/aws/s3/s3_test.go b/pkg/iac/adapters/cloudformation/aws/s3/s3_test.go index ee8fffb39f75..707aae028f7f 100644 --- a/pkg/iac/adapters/cloudformation/aws/s3/s3_test.go +++ b/pkg/iac/adapters/cloudformation/aws/s3/s3_test.go @@ -3,7 +3,10 @@ package s3 import ( "testing" + "github.com/liamg/iamgo" + "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" + "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" "github.com/aquasecurity/trivy/pkg/iac/types" ) @@ -57,6 +60,19 @@ Resources: Status: Enabled WebsiteConfiguration: IndexDocument: index.html + SampleBucketPolicy: + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref Bucket + PolicyDocument: + Version: 2012-10-17 + Statement: + - Action: + - 's3:GetObject' + Effect: Allow + Resource: !Join + - 'arn:aws:s3:::testbucket/*' + Principal: '*' `, expected: s3.S3{ Buckets: []s3.Bucket{ @@ -91,6 +107,23 @@ Resources: Enabled: types.BoolTest(true), }, Website: &s3.Website{}, + BucketPolicies: []iam.Policy{ + { + Document: iam.Document{ + Parsed: iamgo.NewPolicyBuilder(). + WithStatement( + iamgo.NewStatementBuilder(). + WithActions([]string{"s3:GetObject"}). + WithAllPrincipals(true). + WithEffect("Allow"). + WithResources([]string{"arn:aws:s3:::testbucket/*"}). + Build(), + ). + WithVersion("2012-10-17T00:00:00Z"). + Build(), + }, + }, + }, }, }, }, diff --git a/pkg/iac/adapters/terraform/aws/s3/adapt_test.go b/pkg/iac/adapters/terraform/aws/s3/adapt_test.go index 1cd1dec76270..ea12206f312b 100644 --- a/pkg/iac/adapters/terraform/aws/s3/adapt_test.go +++ b/pkg/iac/adapters/terraform/aws/s3/adapt_test.go @@ -149,6 +149,15 @@ func Test_Adapt(t *testing.T) { resource "aws_s3_bucket_acl" "example" { bucket = aws_s3_bucket.example.id acl = "private" + access_control_policy { + grant { + grantee { + type = "Group" + uri = "http://acs.amazonaws.com/groups/s3/LogDelivery" + } + permission = "READ_ACP" + } + } } resource "aws_s3_bucket_server_side_encryption_configuration" "example" { @@ -250,6 +259,51 @@ func Test_Adapt(t *testing.T) { TargetBucket: iacTypes.String("aws_s3_bucket.example", iacTypes.NewTestMetadata()), }, ACL: iacTypes.String("private", iacTypes.NewTestMetadata()), + Grants: []s3.Grant{ + { + Metadata: iacTypes.NewTestMetadata(), + Grantee: s3.Grantee{ + Type: iacTypes.StringTest("Group"), + URI: iacTypes.StringTest("http://acs.amazonaws.com/groups/s3/LogDelivery"), + }, + Permissions: iacTypes.StringValueList{ + iacTypes.StringTest("READ_ACP"), + }, + }, + }, + }, + }, + }, + }, + { + name: "bucket with grants", + terraform: ` +resource "aws_s3_bucket" "this" { + bucket = "test" + + grant { + type = "Group" + uri = "http://acs.amazonaws.com/groups/s3/LogDelivery" + permissions = ["FULL_CONTROL"] + } +} +`, + expected: s3.S3{ + Buckets: []s3.Bucket{ + { + Name: iacTypes.StringTest("test"), + ACL: iacTypes.StringTest("private"), + Grants: []s3.Grant{ + { + Grantee: s3.Grantee{ + Type: iacTypes.StringTest("Group"), + URI: iacTypes.StringTest("http://acs.amazonaws.com/groups/s3/LogDelivery"), + }, + Permissions: iacTypes.StringValueList{ + iacTypes.StringTest("FULL_CONTROL"), + }, + }, + }, }, }, }, diff --git a/pkg/iac/adapters/terraform/aws/s3/bucket.go b/pkg/iac/adapters/terraform/aws/s3/bucket.go index 5ecf7e9ba21b..206b0cbdabdf 100644 --- a/pkg/iac/adapters/terraform/aws/s3/bucket.go +++ b/pkg/iac/adapters/terraform/aws/s3/bucket.go @@ -26,6 +26,7 @@ func (a *adapter) adaptBuckets() []s3.Bucket { Versioning: getVersioning(block, a), Logging: getLogging(block, a), ACL: getBucketAcl(block, a), + Grants: getGrants(block, a), AccelerateConfigurationStatus: getAccelerateStatus(block, a), BucketLocation: block.GetAttribute("region").AsStringValueOrDefault("", block), LifecycleConfiguration: getLifecycle(block, a), @@ -193,6 +194,55 @@ func getBucketAcl(block *terraform.Block, a *adapter) iacTypes.StringValue { return iacTypes.StringDefault("private", block.GetMetadata()) } +func getGrants(block *terraform.Block, a *adapter) []s3.Grant { + if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_acl", func(resource *terraform.Block) []s3.Grant { + var grants []s3.Grant + + if acessControlPolicy := resource.GetBlock("access_control_policy"); acessControlPolicy.IsNotNil() { + for _, grantBlock := range acessControlPolicy.GetBlocks("grant") { + grant := s3.Grant{ + Metadata: grantBlock.GetMetadata(), + Permissions: iacTypes.StringValueList{ + grantBlock.GetAttribute("permission").AsStringValueOrDefault("", grantBlock), + }, + } + + if granteeBlock := grantBlock.GetBlock("grantee"); granteeBlock.IsNotNil() { + grant.Grantee = s3.Grantee{ + Metadata: granteeBlock.GetMetadata(), + Type: granteeBlock.GetAttribute("type").AsStringValueOrDefault("", granteeBlock), + URI: granteeBlock.GetAttribute("uri").AsStringValueOrDefault("", granteeBlock), + } + } + + grants = append(grants, grant) + } + } + + return grants + + }); ok { + return val + } + + var grants []s3.Grant + for _, grantBlock := range block.GetBlocks("grant") { + grant := s3.Grant{ + Metadata: grantBlock.GetMetadata(), + Permissions: grantBlock.GetAttribute("permissions").AsStringValueSliceOrEmpty(), + Grantee: s3.Grantee{ + Metadata: grantBlock.GetMetadata(), + Type: grantBlock.GetAttribute("type").AsStringValueOrDefault("", grantBlock), + URI: grantBlock.GetAttribute("uri").AsStringValueOrDefault("", grantBlock), + }, + } + + grants = append(grants, grant) + } + + return grants +} + func isEncrypted(sseConfgihuration *terraform.Block) iacTypes.BoolValue { return terraform.MapNestedAttribute( sseConfgihuration, diff --git a/pkg/iac/providers/aws/s3/bucket.go b/pkg/iac/providers/aws/s3/bucket.go index e884bbd4a7ff..ccabe6974c99 100755 --- a/pkg/iac/providers/aws/s3/bucket.go +++ b/pkg/iac/providers/aws/s3/bucket.go @@ -14,6 +14,7 @@ type Bucket struct { Versioning Versioning Logging Logging ACL iacTypes.StringValue + Grants []Grant BucketLocation iacTypes.StringValue AccelerateConfigurationStatus iacTypes.StringValue LifecycleConfiguration []Rules @@ -65,3 +66,15 @@ type Contents struct { type Website struct { Metadata iacTypes.Metadata } + +type Grant struct { + Metadata iacTypes.Metadata + Grantee Grantee + Permissions iacTypes.StringValueList +} + +type Grantee struct { + Metadata iacTypes.Metadata + URI iacTypes.StringValue + Type iacTypes.StringValue +} diff --git a/pkg/iac/rego/schemas/cloud.json b/pkg/iac/rego/schemas/cloud.json index ad81b6488ae8..a4bab9423d38 100644 --- a/pkg/iac/rego/schemas/cloud.json +++ b/pkg/iac/rego/schemas/cloud.json @@ -3647,6 +3647,13 @@ "type": "object", "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.aws.s3.Encryption" }, + "grants": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grant" + } + }, "lifecycleconfiguration": { "type": "array", "items": { @@ -3713,6 +3720,43 @@ } } }, + "github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grant": { + "type": "object", + "properties": { + "__defsec_metadata": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.Metadata" + }, + "grantee": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grantee" + }, + "permissions": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.StringValue" + } + } + } + }, + "github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grantee": { + "type": "object", + "properties": { + "__defsec_metadata": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.Metadata" + }, + "type": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.StringValue" + }, + "uri": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.StringValue" + } + } + }, "github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.aws.s3.Logging": { "type": "object", "properties": {