Skip to content

Commit 102b6df

Browse files
authored
fix(terraform): fix policy document retrieval (aquasecurity#6276)
1 parent aa19aaf commit 102b6df

File tree

3 files changed

+314
-39
lines changed

3 files changed

+314
-39
lines changed

pkg/iac/adapters/terraform/aws/iam/convert.go

+20-35
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,7 @@ type wrappedDocument struct {
1616
}
1717

1818
func ParsePolicyFromAttr(attr *terraform.Attribute, owner *terraform.Block, modules terraform.Modules) (*iam.Document, error) {
19-
20-
documents := findAllPolicies(modules, owner, attr)
21-
if len(documents) > 0 {
22-
return &iam.Document{
23-
Parsed: documents[0].Document,
24-
Metadata: documents[0].Source.GetMetadata(),
25-
IsOffset: true,
26-
}, nil
27-
}
28-
2919
if attr.IsString() {
30-
3120
dataBlock, err := modules.GetBlockById(attr.Value().AsString())
3221
if err != nil {
3322
parsed, err := iamgo.Parse([]byte(unescapeVars(attr.Value().AsString())))
@@ -40,7 +29,9 @@ func ParsePolicyFromAttr(attr *terraform.Attribute, owner *terraform.Block, modu
4029
IsOffset: false,
4130
HasRefs: len(attr.AllReferences()) > 0,
4231
}, nil
43-
} else if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" {
32+
}
33+
34+
if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" {
4435
if doc, err := ConvertTerraformDocument(modules, dataBlock); err == nil {
4536
return &iam.Document{
4637
Metadata: dataBlock.GetMetadata(),
@@ -75,7 +66,7 @@ func ConvertTerraformDocument(modules terraform.Modules, block *terraform.Block)
7566
}
7667

7768
if sourceDocumentsAttr := block.GetAttribute("source_policy_documents"); sourceDocumentsAttr.IsIterable() {
78-
docs := findAllPolicies(modules, block, sourceDocumentsAttr)
69+
docs := findAllPolicies(modules, sourceDocumentsAttr)
7970
for _, doc := range docs {
8071
statements, _ := doc.Document.Statements()
8172
for _, statement := range statements {
@@ -100,7 +91,7 @@ func ConvertTerraformDocument(modules terraform.Modules, block *terraform.Block)
10091
}
10192

10293
if overrideDocumentsAttr := block.GetAttribute("override_policy_documents"); overrideDocumentsAttr.IsIterable() {
103-
docs := findAllPolicies(modules, block, overrideDocumentsAttr)
94+
docs := findAllPolicies(modules, overrideDocumentsAttr)
10495
for _, doc := range docs {
10596
statements, _ := doc.Document.Statements()
10697
for _, statement := range statements {
@@ -208,30 +199,24 @@ func parseStatement(statementBlock *terraform.Block) iamgo.Statement {
208199
return builder.Build()
209200
}
210201

211-
func findAllPolicies(modules terraform.Modules, parentBlock *terraform.Block, attr *terraform.Attribute) []wrappedDocument {
202+
func findAllPolicies(modules terraform.Modules, attr *terraform.Attribute) []wrappedDocument {
212203
var documents []wrappedDocument
213-
for _, ref := range attr.AllReferences() {
214-
for _, b := range modules.GetBlocks() {
215-
if b.Type() != "data" || b.TypeLabel() != "aws_iam_policy_document" {
216-
continue
217-
}
218-
if ref.RefersTo(b.Reference()) {
219-
document, err := ConvertTerraformDocument(modules, b)
220-
if err != nil {
221-
continue
222-
}
223-
documents = append(documents, *document)
224-
continue
225-
}
226-
kref := *ref
227-
kref.SetKey(parentBlock.Reference().RawKey())
228-
if kref.RefersTo(b.Reference()) {
229-
document, err := ConvertTerraformDocument(modules, b)
230-
if err != nil {
231-
continue
232-
}
204+
205+
if !attr.IsIterable() {
206+
return documents
207+
}
208+
209+
policyDocIDs := attr.AsStringValues().AsStrings()
210+
for _, policyDocID := range policyDocIDs {
211+
if policyDoc, err := modules.GetBlockById(policyDocID); err == nil {
212+
if document, err := ConvertTerraformDocument(modules, policyDoc); err == nil {
233213
documents = append(documents, *document)
234214
}
215+
} else if parsed, err := iamgo.Parse([]byte(unescapeVars(policyDocID))); err == nil {
216+
documents = append(documents, wrappedDocument{
217+
Document: *parsed,
218+
Source: attr,
219+
})
235220
}
236221
}
237222
return documents

pkg/iac/adapters/terraform/aws/iam/policies_test.go

+237-3
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,10 @@ data "aws_iam_policy_document" "this" {
138138
}
139139
}
140140
141-
142141
resource "aws_iam_policy" "this" {
143142
for_each = local.sqs
144-
name = "test-${each.key}"
145-
policy = data.aws_iam_policy_document.this[each.key].json
143+
name = "test-${each.key}"
144+
policy = data.aws_iam_policy_document.this[each.key].json
146145
}`,
147146
expected: []iam.Policy{
148147
{
@@ -169,6 +168,241 @@ resource "aws_iam_policy" "this" {
169168
},
170169
},
171170
},
171+
{
172+
name: "policy_document with source_policy_documents",
173+
terraform: `
174+
data "aws_iam_policy_document" "source" {
175+
statement {
176+
actions = ["ec2:*"]
177+
resources = ["*"]
178+
}
179+
}
180+
181+
data "aws_iam_policy_document" "source_document_example" {
182+
source_policy_documents = [data.aws_iam_policy_document.source.json]
183+
184+
statement {
185+
actions = ["s3:*"]
186+
187+
resources = [
188+
"arn:aws:s3:::somebucket",
189+
"arn:aws:s3:::somebucket/*",
190+
]
191+
}
192+
}
193+
194+
resource "aws_iam_policy" "this" {
195+
name = "test-policy"
196+
policy = data.aws_iam_policy_document.source_document_example.json
197+
}`,
198+
expected: []iam.Policy{
199+
{
200+
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
201+
Builtin: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
202+
Document: func() iam.Document {
203+
builder := iamgo.NewPolicyBuilder()
204+
firstStatement := iamgo.NewStatementBuilder().
205+
WithActions([]string{"ec2:*"}).
206+
WithResources([]string{"*"}).
207+
WithEffect("Allow").
208+
Build()
209+
210+
builder.WithStatement(firstStatement)
211+
212+
secondStatement := iamgo.NewStatementBuilder().
213+
WithActions([]string{"s3:*"}).
214+
WithResources([]string{"arn:aws:s3:::somebucket", "arn:aws:s3:::somebucket/*"}).
215+
WithEffect("Allow").
216+
Build()
217+
218+
builder.WithStatement(secondStatement)
219+
220+
return iam.Document{
221+
Parsed: builder.Build(),
222+
Metadata: iacTypes.NewTestMetadata(),
223+
IsOffset: true,
224+
HasRefs: false,
225+
}
226+
}(),
227+
},
228+
},
229+
},
230+
{
231+
name: "source_policy_documents with for-each",
232+
terraform: `
233+
locals {
234+
versions = ["2008-10-17", "2012-10-17"]
235+
}
236+
237+
resource "aws_iam_policy" "test_policy" {
238+
name = "test-policy"
239+
policy = data.aws_iam_policy_document.policy.json
240+
}
241+
242+
data "aws_iam_policy_document" "policy" {
243+
source_policy_documents = [for s in data.aws_iam_policy_document.policy_source : s.json if s.version != "2008-10-17"]
244+
statement {
245+
actions = ["s3:*"]
246+
resources = ["*"]
247+
}
248+
}
249+
250+
data "aws_iam_policy_document" "policy_source" {
251+
for_each = toset(local.versions)
252+
version = each.value
253+
statement {
254+
actions = ["s3:PutObject"]
255+
resources = ["*"]
256+
}
257+
}`,
258+
expected: []iam.Policy{
259+
{
260+
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
261+
Document: func() iam.Document {
262+
builder := iamgo.NewPolicyBuilder().
263+
WithStatement(
264+
iamgo.NewStatementBuilder().
265+
WithActions([]string{"s3:PutObject"}).
266+
WithResources([]string{"*"}).
267+
WithEffect("Allow").
268+
Build(),
269+
).
270+
WithStatement(
271+
iamgo.NewStatementBuilder().
272+
WithActions([]string{"s3:*"}).
273+
WithResources([]string{"*"}).
274+
WithEffect("Allow").
275+
Build(),
276+
)
277+
278+
return iam.Document{
279+
Parsed: builder.Build(),
280+
Metadata: iacTypes.NewTestMetadata(),
281+
IsOffset: true,
282+
HasRefs: false,
283+
}
284+
}(),
285+
},
286+
},
287+
},
288+
{
289+
name: "source_policy_documents with condition",
290+
terraform: `
291+
locals {
292+
versions = ["2008-10-17", "2012-10-17"]
293+
}
294+
295+
resource "aws_iam_policy" "test_policy" {
296+
name = "test-policy"
297+
policy = data.aws_iam_policy_document.policy.json
298+
}
299+
300+
data "aws_iam_policy_document" "policy" {
301+
source_policy_documents = true ? [data.aws_iam_policy_document.policy_source.json] : [data.aws_iam_policy_document.policy_source2.json]
302+
}
303+
304+
data "aws_iam_policy_document" "policy_source" {
305+
statement {
306+
actions = ["s3:PutObject"]
307+
resources = ["*"]
308+
}
309+
}
310+
311+
data "aws_iam_policy_document" "policy_source2" {
312+
statement {
313+
actions = ["s3:PutObject2"]
314+
resources = ["*"]
315+
}
316+
}
317+
`,
318+
expected: []iam.Policy{
319+
{
320+
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
321+
Document: func() iam.Document {
322+
builder := iamgo.NewPolicyBuilder().
323+
WithStatement(
324+
iamgo.NewStatementBuilder().
325+
WithActions([]string{"s3:PutObject"}).
326+
WithResources([]string{"*"}).
327+
WithEffect("Allow").
328+
Build(),
329+
)
330+
331+
return iam.Document{
332+
Parsed: builder.Build(),
333+
Metadata: iacTypes.NewTestMetadata(),
334+
IsOffset: true,
335+
HasRefs: false,
336+
}
337+
}(),
338+
},
339+
},
340+
},
341+
{
342+
name: "raw source policy",
343+
terraform: `resource "aws_iam_policy" "test_policy" {
344+
name = "test-policy"
345+
policy = data.aws_iam_policy_document.policy.json
346+
}
347+
348+
data "aws_iam_policy_document" "policy" {
349+
source_policy_documents = [
350+
jsonencode({
351+
Statement = [
352+
{
353+
Action = [
354+
"ec2:Describe*",
355+
]
356+
Effect = "Allow"
357+
Resource = "*"
358+
},
359+
]
360+
}),
361+
]
362+
}
363+
`,
364+
expected: []iam.Policy{
365+
{
366+
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
367+
Document: func() iam.Document {
368+
builder := iamgo.NewPolicyBuilder().
369+
WithStatement(
370+
iamgo.NewStatementBuilder().
371+
WithActions([]string{"ec2:Describe*"}).
372+
WithResources([]string{"*"}).
373+
WithEffect("Allow").
374+
Build(),
375+
)
376+
377+
return iam.Document{
378+
Parsed: builder.Build(),
379+
Metadata: iacTypes.NewTestMetadata(),
380+
IsOffset: true,
381+
HasRefs: false,
382+
}
383+
}(),
384+
},
385+
},
386+
},
387+
{
388+
name: "invalid `override_policy_documents` attribute",
389+
terraform: `resource "aws_iam_policy" "test_policy" {
390+
name = "test-policy"
391+
policy = data.aws_iam_policy_document.policy.json
392+
}
393+
394+
data "aws_iam_policy_document" "policy" {
395+
source_policy_documents = data.aws_iam_policy_document.policy2.json
396+
}`,
397+
expected: []iam.Policy{
398+
{
399+
Name: iacTypes.String("test-policy", iacTypes.NewTestMetadata()),
400+
Document: iam.Document{
401+
IsOffset: true,
402+
},
403+
},
404+
},
405+
},
172406
}
173407

174408
for _, test := range tests {

0 commit comments

Comments
 (0)