From 1a74307ba5a7a866d69cd6ec1ed5ed318a546adc Mon Sep 17 00:00:00 2001 From: AleksaC Date: Mon, 12 Jun 2023 23:43:47 +0200 Subject: [PATCH] [WIP] no s3 global endpoint rule --- rules/aws_s3_no_global_endpoint.go | 68 ++++++++++++++++++++ rules/aws_s3_no_global_endpoint_test.go | 82 +++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 rules/aws_s3_no_global_endpoint.go create mode 100644 rules/aws_s3_no_global_endpoint_test.go diff --git a/rules/aws_s3_no_global_endpoint.go b/rules/aws_s3_no_global_endpoint.go new file mode 100644 index 00000000..2111d930 --- /dev/null +++ b/rules/aws_s3_no_global_endpoint.go @@ -0,0 +1,68 @@ +package rules + +import ( + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + "github.com/terraform-linters/tflint-ruleset-aws/project" +) + +// AwsS3NoGlobalEndpointRule checks whether deprecated s3 global endpoint is used instead +// of the regional endoint +type AwsS3NoGlobalEndpointRule struct { + tflint.DefaultRule + + // TODO: do we need this, if so why + resourceType string + attributeName string +} + +// NewAwsS3NoGlobalEndpointRule returns new rule with default attributes +func NewAwsS3NoGlobalEndpointRule() *AwsS3NoGlobalEndpointRule { + return &AwsS3NoGlobalEndpointRule{ + resourceType: "aws_s3_bucket", + attributeName: "", + } +} + +// Name returns the rule name +func (r *AwsS3NoGlobalEndpointRule) Name() string { + return "aws_acm_certificate_lifecycle" +} + +// Enabled returns whether the rule is enabled by default +func (r *AwsS3NoGlobalEndpointRule) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AwsS3NoGlobalEndpointRule) Severity() tflint.Severity { + return tflint.WARNING +} + +// Link returns the rule reference link +func (r *AwsS3NoGlobalEndpointRule) Link() string { + return project.ReferenceLink(r.Name()) +} + +// Check checks whether the aws_acm_certificate resource contains create_before_destroy = true in lifecycle block +func (r *AwsS3NoGlobalEndpointRule) Check(runner tflint.Runner) error { + var err error + runner.WalkExpressions(tflint.ExprWalkFunc(func(expr hcl.Expression) hcl.Diagnostics { + vars := expr.Variables() + + if len(vars) == 0 { + return nil + } + + // is this ever greater than 0 + v := vars[0] + + if v.RootName() == "aws_s3_bucket" && len(v) == 3 && v[2].(hcl.TraverseAttr).Name == "bucket_domain_name" { + err = runner.EmitIssue(r, "`bucket_domain_name` returns the legacy s3 global endpoint, use `bucket_regional_domain_name` instead", v.SourceRange()) + } + + return nil + })) + + return err +} diff --git a/rules/aws_s3_no_global_endpoint_test.go b/rules/aws_s3_no_global_endpoint_test.go new file mode 100644 index 00000000..a5e71036 --- /dev/null +++ b/rules/aws_s3_no_global_endpoint_test.go @@ -0,0 +1,82 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AwsS3NoGlobalEndpoint(t *testing.T) { + cases := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "unrelated expression", + Content: ` +output "test" { + value = "testing" +}`, + Expected: helper.Issues{}, + }, + { + Name: "multiple unrelated expressions", + Content: ` +output "test1" { + value = var.whatever +} + +output "test2" { + value = "testing" +} + +output "test3" { + value = aws_iam_role.test.arn +} + `, + Expected: helper.Issues{}, + }, + // TODO: nested expressions ? + { + Name: "regional endpoint used", + Content: ` +output "test" { + value = aws_s3_bucket.test.bucket_regional_domain_name +}`, + Expected: helper.Issues{}, + }, + // TODO: strings of the form "bucket_name.s3.amazonaws.com" ? + { + Name: "legacy global endpoint used", + Content: ` +output "test" { + value = aws_s3_bucket.test.bucket_domain_name +}`, + Expected: helper.Issues{ + { + Rule: NewAwsS3NoGlobalEndpointRule(), + Message: "`bucket_domain_name` returns the legacy s3 global endpoint, use `bucket_regional_domain_name` instead", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 3, Column: 11}, + End: hcl.Pos{Line: 3, Column: 48}, + }, + }, + }, + }, + } + + rule := NewAwsS3NoGlobalEndpointRule() + + for _, tc := range cases { + runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content}) + + if err := rule.Check(runner); err != nil { + t.Fatalf("Unexpected error occurred: %s", err) + } + + helper.AssertIssues(t, tc.Expected, runner.Issues) + } +}