Skip to content

Commit

Permalink
Merge pull request #159 from liamg/google-user-iam-grants
Browse files Browse the repository at this point in the history
[GCP Check] Add IAM user grant check
  • Loading branch information
liamg committed Sep 22, 2020
2 parents 1ab384f + a6fccc0 commit 9eb8d19
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ there are also checks which are provider agnostic.
| GCP008 | google | Legacy client authentication methods utilized.
| GCP009 | google | Pod security policy enforcement not defined.
| GCP010 | google | Shielded GKE nodes not enabled.
| GCP011 | google | IAM granted directly to user.

## Running in CI

Expand Down
73 changes: 73 additions & 0 deletions internal/app/tfsec/checks/google_user_iam_grant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package checks

import (
"fmt"
"strings"
"github.com/liamg/tfsec/internal/app/tfsec/parser"
"github.com/liamg/tfsec/internal/app/tfsec/scanner"
"github.com/zclconf/go-cty/cty"
)

// GoogleUserIAMGrant See https://github.com/liamg/tfsec#included-checks for check info
const GoogleUserIAMGrant scanner.RuleID = "GCP011"

func init() {
scanner.RegisterCheck(scanner.Check{
Code: GoogleUserIAMGrant,
RequiredTypes: []string{"resource", "data"},
RequiredLabels: []string{
"google_cloud_run_service_iam_binding",
"google_cloud_run_service_iam_member",
"google_compute_instance_iam_binding",
"google_compute_instance_iam_member",
"google_compute_subnetwork_iam_binding",
"google_compute_subnetwork_iam_member",
"google_data_catalog_entry_group_iam_binding",
"google_data_catalog_entry_group_iam_member",
"google_folder_iam_member",
"google_folder_iam_binding",
"google_project_iam_member",
"google_project_iam_binding",
"google_pubsub_subscription_iam_binding",
"google_pubsub_subscription_iam_member",
"google_pubsub_topic_iam_binding",
"google_pubsub_topic_iam_member",
"google_sourcerepo_repository_iam_binding",
"google_sourcerepo_repository_iam_member",
"google_spanner_database_iam_binding",
"google_spanner_database_iam_member",
"google_spanner_instance_iam_binding",
"google_spanner_instance_iam_member",
"google_storage_bucket_iam_binding",
"google_storage_bucket_iam_member",
"google_iam_policy",
},
CheckFunc: func(check *scanner.Check, block *parser.Block, _ *scanner.Context) []scanner.Result {

var members []cty.Value
var attributes *parser.Attribute

if attributes = block.GetAttribute("member"); attributes != nil {
members = append(members, attributes.Value())
} else if attributes = block.GetAttribute("members"); attributes != nil {
members = attributes.Value().AsValueSlice()
} else if attributes = block.GetBlock("binding").GetAttribute("members"); attributes != nil {
members = attributes.Value().AsValueSlice()
}

for _, identities := range members {
if identities.Type() == cty.String && strings.HasPrefix(identities.AsString(), "user:") {
return []scanner.Result{
check.NewResult(
fmt.Sprintf("'%s' grants IAM to a user object. It is recommended to manage user permissions with groups.", block.Name()),
attributes.Range(),
scanner.SeverityWarning,
),
}
}
}

return nil
},
})
}
95 changes: 95 additions & 0 deletions internal/app/tfsec/google_user_iam_grant_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package tfsec

import (
"testing"

"github.com/liamg/tfsec/internal/app/tfsec/scanner"

"github.com/liamg/tfsec/internal/app/tfsec/checks"
)

func Test_GoogleUserIAMGrant(t *testing.T) {

var tests = []struct {
name string
source string
mustIncludeResultCode scanner.RuleID
mustExcludeResultCode scanner.RuleID
}{
{
name: "check google_project_iam_binding with grant to user identity",
source: `
resource "google_project_iam_binding" "project-binding" {
members = [
"user:test@example.com",
]
}`,
mustIncludeResultCode: checks.GoogleUserIAMGrant,
},
{
name: "check google_project_iam_binding with grant to user identity",
source: `
resource "google_project_iam_binding" "project-binding" {
members = [
"group:test@example.com",
]
}`,
mustExcludeResultCode: checks.GoogleUserIAMGrant,
},
{
name: "check google_project_iam_member with grant to user identity",
source: `
resource "google_project_iam_member" "project-member" {
member = "user:test@example.com"
}`,
mustIncludeResultCode: checks.GoogleUserIAMGrant,
},
{
name: "check google_storage_bucket_iam_binding with grant to user identity",
source: `
resource "google_storage_bucket_iam_binding" "bucket-binding" {
members = [
"user:test@example.com",
]
}`,
mustIncludeResultCode: checks.GoogleUserIAMGrant,
},
{
name: "check google_storage_bucket_iam_member with grant to user identity",
source: `
resource "google_storage_bucket_iam_member" "bucket-member" {
member = "user:test@example.com"
}`,
mustIncludeResultCode: checks.GoogleUserIAMGrant,
},
{
name: "check google_storage_bucket_iam_member with grant to service account identity",
source: `
resource "google_storage_bucket_iam_member" "bucket-member" {
member = "serviceAccount:test@example.com"
}`,
mustExcludeResultCode: checks.GoogleUserIAMGrant,
},
{
name: "check data.google_iam_policy with grant to user",
source: `
data "google_iam_policy" "test-policy" {
binding {
members = [
"group:test@example.com",
"user:test@example.com",
]
}
}`,
mustIncludeResultCode: checks.GoogleUserIAMGrant,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
results := scanSource(test.source)
assertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
})
}

}

0 comments on commit 9eb8d19

Please sign in to comment.