github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/internal/adapters/terraform/aws/iam/convert.go (about)

     1  package iam
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/khulnasoft-lab/defsec/pkg/scan"
     7  
     8  	"github.com/khulnasoft-lab/defsec/pkg/terraform"
     9  
    10  	"github.com/khulnasoft-lab/defsec/pkg/providers/aws/iam"
    11  
    12  	"github.com/liamg/iamgo"
    13  )
    14  
    15  type wrappedDocument struct {
    16  	Source   scan.MetadataProvider
    17  	Document iamgo.Document
    18  }
    19  
    20  func ParsePolicyFromAttr(attr *terraform.Attribute, owner *terraform.Block, modules terraform.Modules) (*iam.Document, error) {
    21  
    22  	documents := findAllPolicies(modules, owner, attr)
    23  	if len(documents) > 0 {
    24  		return &iam.Document{
    25  			Parsed:   documents[0].Document,
    26  			Metadata: documents[0].Source.GetMetadata(),
    27  			IsOffset: true,
    28  		}, nil
    29  	}
    30  
    31  	if attr.IsString() {
    32  
    33  		dataBlock, err := modules.GetBlockById(attr.Value().AsString())
    34  		if err != nil {
    35  			parsed, err := iamgo.Parse([]byte(unescapeVars(attr.Value().AsString())))
    36  			if err != nil {
    37  				return nil, err
    38  			}
    39  			return &iam.Document{
    40  				Parsed:   *parsed,
    41  				Metadata: attr.GetMetadata(),
    42  				IsOffset: false,
    43  				HasRefs:  len(attr.AllReferences()) > 0,
    44  			}, nil
    45  		} else if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" {
    46  			if doc, err := ConvertTerraformDocument(modules, dataBlock); err == nil {
    47  				return &iam.Document{
    48  					Metadata: dataBlock.GetMetadata(),
    49  					Parsed:   doc.Document,
    50  					IsOffset: true,
    51  					HasRefs:  false,
    52  				}, nil
    53  			}
    54  		}
    55  	}
    56  
    57  	return &iam.Document{
    58  		Metadata: owner.GetMetadata(),
    59  	}, nil
    60  }
    61  
    62  func unescapeVars(input string) string {
    63  	return strings.ReplaceAll(input, "&{", "${")
    64  }
    65  
    66  // ConvertTerraformDocument converts a terraform data policy into an iamgo policy https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document
    67  func ConvertTerraformDocument(modules terraform.Modules, block *terraform.Block) (*wrappedDocument, error) {
    68  
    69  	builder := iamgo.NewPolicyBuilder()
    70  
    71  	if sourceAttr := block.GetAttribute("source_json"); sourceAttr.IsString() {
    72  		doc, err := iamgo.ParseString(sourceAttr.Value().AsString())
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  		builder = iamgo.PolicyBuilderFromDocument(*doc)
    77  	}
    78  
    79  	if sourceDocumentsAttr := block.GetAttribute("source_policy_documents"); sourceDocumentsAttr.IsIterable() {
    80  		docs := findAllPolicies(modules, block, sourceDocumentsAttr)
    81  		for _, doc := range docs {
    82  			statements, _ := doc.Document.Statements()
    83  			for _, statement := range statements {
    84  				builder.WithStatement(statement)
    85  			}
    86  		}
    87  	}
    88  
    89  	if idAttr := block.GetAttribute("policy_id"); idAttr.IsString() {
    90  		r := idAttr.GetMetadata().Range()
    91  		builder.WithId(idAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine())
    92  	}
    93  
    94  	if versionAttr := block.GetAttribute("version"); versionAttr.IsString() {
    95  		r := versionAttr.GetMetadata().Range()
    96  		builder.WithVersion(versionAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine())
    97  	}
    98  
    99  	for _, statementBlock := range block.GetBlocks("statement") {
   100  		statement := parseStatement(statementBlock)
   101  		builder.WithStatement(statement, statement.Range().StartLine, statement.Range().EndLine)
   102  	}
   103  
   104  	if overrideDocumentsAttr := block.GetAttribute("override_policy_documents"); overrideDocumentsAttr.IsIterable() {
   105  		docs := findAllPolicies(modules, block, overrideDocumentsAttr)
   106  		for _, doc := range docs {
   107  			statements, _ := doc.Document.Statements()
   108  			for _, statement := range statements {
   109  				builder.WithStatement(statement, statement.Range().StartLine, statement.Range().EndLine)
   110  			}
   111  		}
   112  	}
   113  
   114  	return &wrappedDocument{Document: builder.Build(), Source: block}, nil
   115  }
   116  
   117  // nolint
   118  func parseStatement(statementBlock *terraform.Block) iamgo.Statement {
   119  
   120  	metadata := statementBlock.GetMetadata()
   121  
   122  	builder := iamgo.NewStatementBuilder()
   123  	builder.WithRange(metadata.Range().GetStartLine(), metadata.Range().GetEndLine())
   124  
   125  	if sidAttr := statementBlock.GetAttribute("sid"); sidAttr.IsString() {
   126  		r := sidAttr.GetMetadata().Range()
   127  		builder.WithSid(sidAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine())
   128  	}
   129  	if actionsAttr := statementBlock.GetAttribute("actions"); actionsAttr.IsIterable() {
   130  		r := actionsAttr.GetMetadata().Range()
   131  		values := actionsAttr.AsStringValues().AsStrings()
   132  		builder.WithActions(values, r.GetStartLine(), r.GetEndLine())
   133  	}
   134  	if notActionsAttr := statementBlock.GetAttribute("not_actions"); notActionsAttr.IsIterable() {
   135  		r := notActionsAttr.GetMetadata().Range()
   136  		values := notActionsAttr.AsStringValues().AsStrings()
   137  		builder.WithNotActions(values, r.GetStartLine(), r.GetEndLine())
   138  	}
   139  	if resourcesAttr := statementBlock.GetAttribute("resources"); resourcesAttr.IsIterable() {
   140  		r := resourcesAttr.GetMetadata().Range()
   141  		values := resourcesAttr.AsStringValues().AsStrings()
   142  		builder.WithResources(values, r.GetStartLine(), r.GetEndLine())
   143  	}
   144  	if notResourcesAttr := statementBlock.GetAttribute("not_resources"); notResourcesAttr.IsIterable() {
   145  		r := notResourcesAttr.GetMetadata().Range()
   146  		values := notResourcesAttr.AsStringValues().AsStrings()
   147  		builder.WithNotResources(values, r.GetStartLine(), r.GetEndLine())
   148  	}
   149  	if effectAttr := statementBlock.GetAttribute("effect"); effectAttr.IsString() {
   150  		r := effectAttr.GetMetadata().Range()
   151  		builder.WithEffect(effectAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine())
   152  	} else {
   153  		builder.WithEffect(iamgo.EffectAllow)
   154  	}
   155  
   156  	for _, principalBlock := range statementBlock.GetBlocks("principals") {
   157  		typeAttr := principalBlock.GetAttribute("type")
   158  		if !typeAttr.IsString() {
   159  			continue
   160  		}
   161  		identifiersAttr := principalBlock.GetAttribute("identifiers")
   162  		if !identifiersAttr.IsIterable() {
   163  			continue
   164  		}
   165  		r := principalBlock.GetMetadata().Range()
   166  		switch typeAttr.Value().AsString() {
   167  		case "*":
   168  			builder.WithAllPrincipals(true, r.GetStartLine(), r.GetEndLine())
   169  		case "AWS":
   170  			values := identifiersAttr.AsStringValues().AsStrings()
   171  			builder.WithAWSPrincipals(values, r.GetStartLine(), r.GetEndLine())
   172  		case "Federated":
   173  			values := identifiersAttr.AsStringValues().AsStrings()
   174  			builder.WithFederatedPrincipals(values, r.GetStartLine(), r.GetEndLine())
   175  		case "Service":
   176  			values := identifiersAttr.AsStringValues().AsStrings()
   177  			builder.WithServicePrincipals(values, r.GetStartLine(), r.GetEndLine())
   178  		case "CanonicalUser":
   179  			values := identifiersAttr.AsStringValues().AsStrings()
   180  			builder.WithCanonicalUsersPrincipals(values, r.GetStartLine(), r.GetEndLine())
   181  		}
   182  	}
   183  
   184  	for _, conditionBlock := range statementBlock.GetBlocks("condition") {
   185  		testAttr := conditionBlock.GetAttribute("test")
   186  		if !testAttr.IsString() {
   187  			continue
   188  		}
   189  		variableAttr := conditionBlock.GetAttribute("variable")
   190  		if !variableAttr.IsString() {
   191  			continue
   192  		}
   193  		valuesAttr := conditionBlock.GetAttribute("values")
   194  		values := valuesAttr.AsStringValues().AsStrings()
   195  		if valuesAttr.IsNil() || len(values) == 0 {
   196  			continue
   197  		}
   198  
   199  		r := conditionBlock.GetMetadata().Range()
   200  
   201  		builder.WithCondition(
   202  			testAttr.Value().AsString(),
   203  			variableAttr.Value().AsString(),
   204  			values,
   205  			r.GetStartLine(),
   206  			r.GetEndLine(),
   207  		)
   208  
   209  	}
   210  	return builder.Build()
   211  }
   212  
   213  func findAllPolicies(modules terraform.Modules, parentBlock *terraform.Block, attr *terraform.Attribute) []wrappedDocument {
   214  	var documents []wrappedDocument
   215  	for _, ref := range attr.AllReferences() {
   216  		for _, b := range modules.GetBlocks() {
   217  			if b.Type() != "data" || b.TypeLabel() != "aws_iam_policy_document" {
   218  				continue
   219  			}
   220  			if ref.RefersTo(b.Reference()) {
   221  				document, err := ConvertTerraformDocument(modules, b)
   222  				if err != nil {
   223  					continue
   224  				}
   225  				documents = append(documents, *document)
   226  				continue
   227  			}
   228  			kref := *ref
   229  			kref.SetKey(parentBlock.Reference().RawKey())
   230  			if kref.RefersTo(b.Reference()) {
   231  				document, err := ConvertTerraformDocument(modules, b)
   232  				if err != nil {
   233  					continue
   234  				}
   235  				documents = append(documents, *document)
   236  			}
   237  		}
   238  	}
   239  	return documents
   240  }