Repository: aquasecurity/tfsec
Pull request #1031 (2021-08-16 15:40:09)

No public access to API Gateway methods
- allow access to gateway method if option or api key required



Patch:
diff --git a/internal/app/tfsec/rules/aws/apigateway/no_public_access_rule.go b/internal/app/tfsec/rules/aws/apigateway/no_public_access_rule.go
new file mode 100644
index 000000000..c9f9e5431
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/apigateway/no_public_access_rule.go
@@ -0,0 +1,85 @@
+package apigateway
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:  provider.AWSProvider,
+		Service:   "api-gateway",
+		ShortCode: "no-public-access",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "No public access to API Gateway methods",
+			Explanation: `API Gateway methods should be protected by authorization or api key. OPTION verb calls can be used without authorization`,
+			Impact:      "API gateway methods can be unauthorized accessed",
+			Resolution:  "Use and authorization method or require API Key",
+			BadExample: []string{`
+resource "aws_api_gateway_method" "bad_example" {
+  rest_api_id   = aws_api_gateway_rest_api.MyDemoAPI.id
+  resource_id   = aws_api_gateway_resource.MyDemoResource.id
+  http_method   = "GET"
+  authorization = "NONE"
+}
+`},
+			GoodExample: []string{`
+resource "aws_api_gateway_method" "good_example" {
+  rest_api_id   = aws_api_gateway_rest_api.MyDemoAPI.id
+  resource_id   = aws_api_gateway_resource.MyDemoResource.id
+  http_method   = "GET"
+  authorization = "AWS_IAM"
+}
+`,
+				`
+resource "aws_api_gateway_method" "good_example" {
+  rest_api_id      = aws_api_gateway_rest_api.MyDemoAPI.id
+  resource_id      = aws_api_gateway_resource.MyDemoResource.id
+  http_method      = "GET"
+  authorization    = "NONE"
+  api_key_required = true
+}
+`,
+				`
+resource "aws_api_gateway_method" "good_example" {
+  rest_api_id   = aws_api_gateway_rest_api.MyDemoAPI.id
+  resource_id   = aws_api_gateway_resource.MyDemoResource.id
+  http_method   = "OPTION"
+  authorization = "NONE"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method#authorization",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"aws_api_gateway_method",
+		},
+		DefaultSeverity: severity.Low,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, module block.Module) {
+			if authorizationAttr := resourceBlock.GetAttribute("authorization"); authorizationAttr.IsNotNil() && authorizationAttr.Equals("NONE") {
+				if verbAttr := resourceBlock.GetAttribute("http_method"); verbAttr.IsNotNil() && verbAttr.Equals("OPTION", block.IgnoreCase) {
+					return
+				}
+				if keyReqAttr := resourceBlock.GetAttribute("api_key_required"); keyReqAttr.IsNotNil() && keyReqAttr.IsTrue() {
+					return
+				}
+
+				set.AddResult().
+					WithDescription("Resource '%s' has authorization set to NONE", resourceBlock.FullName()).
+					WithAttribute(authorizationAttr)
+			}
+		},
+	})
+}


Test patch:
diff --git a/internal/app/tfsec/rules/aws/apigateway/no_public_access_rule_test.go b/internal/app/tfsec/rules/aws/apigateway/no_public_access_rule_test.go
new file mode 100644
index 000000000..be0e1d6b4
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/apigateway/no_public_access_rule_test.go
@@ -0,0 +1,53 @@
+package apigateway
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSNoPublicAccess_FailureExamples(t *testing.T) {
+	expectedCode := "aws-api-gateway-no-public-access"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSNoPublicAccess_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-api-gateway-no-public-access"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}