Skip to content

Commit

Permalink
fix(terraform): fix policy document retrieval (aquasecurity#6276)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikpivkin authored Mar 12, 2024
1 parent aa19aaf commit 102b6df
Show file tree
Hide file tree
Showing 3 changed files with 314 additions and 39 deletions.
55 changes: 20 additions & 35 deletions pkg/iac/adapters/terraform/aws/iam/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,7 @@ type wrappedDocument struct {
}

func ParsePolicyFromAttr(attr *terraform.Attribute, owner *terraform.Block, modules terraform.Modules) (*iam.Document, error) {

documents := findAllPolicies(modules, owner, attr)
if len(documents) > 0 {
return &iam.Document{
Parsed: documents[0].Document,
Metadata: documents[0].Source.GetMetadata(),
IsOffset: true,
}, nil
}

if attr.IsString() {

dataBlock, err := modules.GetBlockById(attr.Value().AsString())
if err != nil {
parsed, err := iamgo.Parse([]byte(unescapeVars(attr.Value().AsString())))
Expand All @@ -40,7 +29,9 @@ func ParsePolicyFromAttr(attr *terraform.Attribute, owner *terraform.Block, modu
IsOffset: false,
HasRefs: len(attr.AllReferences()) > 0,
}, nil
} else if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" {
}

if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" {
if doc, err := ConvertTerraformDocument(modules, dataBlock); err == nil {
return &iam.Document{
Metadata: dataBlock.GetMetadata(),
Expand Down Expand Up @@ -75,7 +66,7 @@ func ConvertTerraformDocument(modules terraform.Modules, block *terraform.Block)
}

if sourceDocumentsAttr := block.GetAttribute("source_policy_documents"); sourceDocumentsAttr.IsIterable() {
docs := findAllPolicies(modules, block, sourceDocumentsAttr)
docs := findAllPolicies(modules, sourceDocumentsAttr)
for _, doc := range docs {
statements, _ := doc.Document.Statements()
for _, statement := range statements {
Expand All @@ -100,7 +91,7 @@ func ConvertTerraformDocument(modules terraform.Modules, block *terraform.Block)
}

if overrideDocumentsAttr := block.GetAttribute("override_policy_documents"); overrideDocumentsAttr.IsIterable() {
docs := findAllPolicies(modules, block, overrideDocumentsAttr)
docs := findAllPolicies(modules, overrideDocumentsAttr)
for _, doc := range docs {
statements, _ := doc.Document.Statements()
for _, statement := range statements {
Expand Down Expand Up @@ -208,30 +199,24 @@ func parseStatement(statementBlock *terraform.Block) iamgo.Statement {
return builder.Build()
}

func findAllPolicies(modules terraform.Modules, parentBlock *terraform.Block, attr *terraform.Attribute) []wrappedDocument {
func findAllPolicies(modules terraform.Modules, attr *terraform.Attribute) []wrappedDocument {
var documents []wrappedDocument
for _, ref := range attr.AllReferences() {
for _, b := range modules.GetBlocks() {
if b.Type() != "data" || b.TypeLabel() != "aws_iam_policy_document" {
continue
}
if ref.RefersTo(b.Reference()) {
document, err := ConvertTerraformDocument(modules, b)
if err != nil {
continue
}
documents = append(documents, *document)
continue
}
kref := *ref
kref.SetKey(parentBlock.Reference().RawKey())
if kref.RefersTo(b.Reference()) {
document, err := ConvertTerraformDocument(modules, b)
if err != nil {
continue
}

if !attr.IsIterable() {
return documents
}

policyDocIDs := attr.AsStringValues().AsStrings()
for _, policyDocID := range policyDocIDs {
if policyDoc, err := modules.GetBlockById(policyDocID); err == nil {
if document, err := ConvertTerraformDocument(modules, policyDoc); err == nil {
documents = append(documents, *document)
}
} else if parsed, err := iamgo.Parse([]byte(unescapeVars(policyDocID))); err == nil {
documents = append(documents, wrappedDocument{
Document: *parsed,
Source: attr,
})
}
}
return documents
Expand Down
240 changes: 237 additions & 3 deletions pkg/iac/adapters/terraform/aws/iam/policies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,10 @@ data "aws_iam_policy_document" "this" {
}
}
resource "aws_iam_policy" "this" {
for_each = local.sqs
name = "test-${each.key}"
policy = data.aws_iam_policy_document.this[each.key].json
name = "test-${each.key}"
policy = data.aws_iam_policy_document.this[each.key].json
}`,
expected: []iam.Policy{
{
Expand All @@ -169,6 +168,241 @@ resource "aws_iam_policy" "this" {
},
},
},
{
name: "policy_document with source_policy_documents",
terraform: `
data "aws_iam_policy_document" "source" {
statement {
actions = ["ec2:*"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "source_document_example" {
source_policy_documents = [data.aws_iam_policy_document.source.json]
statement {
actions = ["s3:*"]
resources = [
"arn:aws:s3:::somebucket",
"arn:aws:s3:::somebucket/*",
]
}
}
resource "aws_iam_policy" "this" {
name = "test-policy"
policy = data.aws_iam_policy_document.source_document_example.json
}`,
expected: []iam.Policy{
{
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
Builtin: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
Document: func() iam.Document {
builder := iamgo.NewPolicyBuilder()
firstStatement := iamgo.NewStatementBuilder().
WithActions([]string{"ec2:*"}).
WithResources([]string{"*"}).
WithEffect("Allow").
Build()

builder.WithStatement(firstStatement)

secondStatement := iamgo.NewStatementBuilder().
WithActions([]string{"s3:*"}).
WithResources([]string{"arn:aws:s3:::somebucket", "arn:aws:s3:::somebucket/*"}).
WithEffect("Allow").
Build()

builder.WithStatement(secondStatement)

return iam.Document{
Parsed: builder.Build(),
Metadata: iacTypes.NewTestMetadata(),
IsOffset: true,
HasRefs: false,
}
}(),
},
},
},
{
name: "source_policy_documents with for-each",
terraform: `
locals {
versions = ["2008-10-17", "2012-10-17"]
}
resource "aws_iam_policy" "test_policy" {
name = "test-policy"
policy = data.aws_iam_policy_document.policy.json
}
data "aws_iam_policy_document" "policy" {
source_policy_documents = [for s in data.aws_iam_policy_document.policy_source : s.json if s.version != "2008-10-17"]
statement {
actions = ["s3:*"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "policy_source" {
for_each = toset(local.versions)
version = each.value
statement {
actions = ["s3:PutObject"]
resources = ["*"]
}
}`,
expected: []iam.Policy{
{
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
Document: func() iam.Document {
builder := iamgo.NewPolicyBuilder().
WithStatement(
iamgo.NewStatementBuilder().
WithActions([]string{"s3:PutObject"}).
WithResources([]string{"*"}).
WithEffect("Allow").
Build(),
).
WithStatement(
iamgo.NewStatementBuilder().
WithActions([]string{"s3:*"}).
WithResources([]string{"*"}).
WithEffect("Allow").
Build(),
)

return iam.Document{
Parsed: builder.Build(),
Metadata: iacTypes.NewTestMetadata(),
IsOffset: true,
HasRefs: false,
}
}(),
},
},
},
{
name: "source_policy_documents with condition",
terraform: `
locals {
versions = ["2008-10-17", "2012-10-17"]
}
resource "aws_iam_policy" "test_policy" {
name = "test-policy"
policy = data.aws_iam_policy_document.policy.json
}
data "aws_iam_policy_document" "policy" {
source_policy_documents = true ? [data.aws_iam_policy_document.policy_source.json] : [data.aws_iam_policy_document.policy_source2.json]
}
data "aws_iam_policy_document" "policy_source" {
statement {
actions = ["s3:PutObject"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "policy_source2" {
statement {
actions = ["s3:PutObject2"]
resources = ["*"]
}
}
`,
expected: []iam.Policy{
{
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
Document: func() iam.Document {
builder := iamgo.NewPolicyBuilder().
WithStatement(
iamgo.NewStatementBuilder().
WithActions([]string{"s3:PutObject"}).
WithResources([]string{"*"}).
WithEffect("Allow").
Build(),
)

return iam.Document{
Parsed: builder.Build(),
Metadata: iacTypes.NewTestMetadata(),
IsOffset: true,
HasRefs: false,
}
}(),
},
},
},
{
name: "raw source policy",
terraform: `resource "aws_iam_policy" "test_policy" {
name = "test-policy"
policy = data.aws_iam_policy_document.policy.json
}
data "aws_iam_policy_document" "policy" {
source_policy_documents = [
jsonencode({
Statement = [
{
Action = [
"ec2:Describe*",
]
Effect = "Allow"
Resource = "*"
},
]
}),
]
}
`,
expected: []iam.Policy{
{
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
Document: func() iam.Document {
builder := iamgo.NewPolicyBuilder().
WithStatement(
iamgo.NewStatementBuilder().
WithActions([]string{"ec2:Describe*"}).
WithResources([]string{"*"}).
WithEffect("Allow").
Build(),
)

return iam.Document{
Parsed: builder.Build(),
Metadata: iacTypes.NewTestMetadata(),
IsOffset: true,
HasRefs: false,
}
}(),
},
},
},
{
name: "invalid `override_policy_documents` attribute",
terraform: `resource "aws_iam_policy" "test_policy" {
name = "test-policy"
policy = data.aws_iam_policy_document.policy.json
}
data "aws_iam_policy_document" "policy" {
source_policy_documents = data.aws_iam_policy_document.policy2.json
}`,
expected: []iam.Policy{
{
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
Document: iam.Document{
IsOffset: true,
},
},
},
},
}

for _, test := range tests {
Expand Down
Loading

0 comments on commit 102b6df

Please sign in to comment.