github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/terraform/resource_block.go (about)

     1  package terraform
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"text/template"
     8  )
     9  
    10  type PlanReference struct {
    11  	Value interface{}
    12  }
    13  
    14  type PlanBlock struct {
    15  	Type       string
    16  	Name       string
    17  	BlockType  string
    18  	Blocks     map[string]map[string]interface{}
    19  	Attributes map[string]interface{}
    20  }
    21  
    22  func NewPlanBlock(blockType, resourceType, resourceName string) *PlanBlock {
    23  	if blockType == "managed" {
    24  		blockType = "resource"
    25  	}
    26  
    27  	return &PlanBlock{
    28  		Type:       resourceType,
    29  		Name:       resourceName,
    30  		BlockType:  blockType,
    31  		Blocks:     make(map[string]map[string]interface{}),
    32  		Attributes: make(map[string]interface{}),
    33  	}
    34  }
    35  
    36  func (rb *PlanBlock) HasAttribute(attribute string) bool {
    37  	for k := range rb.Attributes {
    38  		if k == attribute {
    39  			return true
    40  		}
    41  	}
    42  	return false
    43  }
    44  
    45  func (rb *PlanBlock) ToHCL() string {
    46  
    47  	resourceTmpl, err := template.New("resource").Funcs(template.FuncMap{
    48  		"RenderValue":     renderTemplateValue,
    49  		"RenderPrimitive": renderPrimitive,
    50  	}).Parse(resourceTemplate)
    51  	if err != nil {
    52  		panic(err)
    53  	}
    54  
    55  	var res bytes.Buffer
    56  	if err := resourceTmpl.Execute(&res, map[string]interface{}{
    57  		"BlockType":  rb.BlockType,
    58  		"Type":       rb.Type,
    59  		"Name":       rb.Name,
    60  		"Attributes": rb.Attributes,
    61  		"Blocks":     rb.Blocks,
    62  	}); err != nil {
    63  		return ""
    64  	}
    65  	return res.String()
    66  }
    67  
    68  var resourceTemplate = `{{ .BlockType }} "{{ .Type }}" "{{ .Name }}" {
    69  	{{ range $name, $value := .Attributes }}{{ if $value }}{{ $name }} {{ RenderValue $value }}
    70  	{{end}}{{ end }}{{  range $name, $block := .Blocks }}{{ $name }} {
    71  	{{ range $name, $value := $block }}{{ if $value }}{{ $name }} {{ RenderValue $value }}
    72  	{{end}}{{ end }}}
    73  {{end}}}`
    74  
    75  func renderTemplateValue(val interface{}) string {
    76  	switch t := val.(type) {
    77  	case map[string]interface{}:
    78  		return fmt.Sprintf("= %s", renderMap(t))
    79  	case []interface{}:
    80  		return renderSlice(t)
    81  	default:
    82  		return fmt.Sprintf("= %s", renderPrimitive(val))
    83  	}
    84  }
    85  
    86  func renderPrimitive(val interface{}) string {
    87  	switch t := val.(type) {
    88  	case PlanReference:
    89  		return fmt.Sprintf("%v", t.Value)
    90  	case string:
    91  		if strings.Contains(t, "\n") {
    92  			return fmt.Sprintf(`<<EOF
    93  %s
    94  EOF
    95  `, t)
    96  		}
    97  		return fmt.Sprintf("%q", t)
    98  	case []interface{}:
    99  		return renderSlice(t)
   100  	default:
   101  		return fmt.Sprintf("%#v", t)
   102  	}
   103  
   104  }
   105  
   106  func renderSlice(vals []interface{}) string {
   107  	if len(vals) == 0 {
   108  		return "[]"
   109  	}
   110  
   111  	val := vals[0]
   112  
   113  	switch t := val.(type) {
   114  	// if vals[0] is a map[string]interface this is a block, so render it as a map
   115  	case map[string]interface{}:
   116  		return renderMap(t)
   117  	// otherwise its going to be just a list of primitives
   118  	default:
   119  		result := " = [\n"
   120  		for _, v := range vals {
   121  			result = fmt.Sprintf("%s\t%v,\n", result, renderPrimitive(v))
   122  		}
   123  		result = fmt.Sprintf("%s]", result)
   124  		return result
   125  	}
   126  }
   127  
   128  func renderMap(val map[string]interface{}) string {
   129  	if len(val) == 0 {
   130  		return "{}"
   131  	}
   132  
   133  	result := "{\n"
   134  	for k, v := range val {
   135  		if v == nil {
   136  			continue
   137  		}
   138  		result = fmt.Sprintf("%s\t%s = %s\n", result, k, renderPrimitive(v))
   139  	}
   140  	result = fmt.Sprintf("%s}", result)
   141  	return result
   142  }