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

     1  package context
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/zclconf/go-cty/cty"
     8  )
     9  
    10  type Context struct {
    11  	ctx    *hcl.EvalContext
    12  	parent *Context
    13  }
    14  
    15  func NewContext(ctx *hcl.EvalContext, parent *Context) *Context {
    16  	if ctx.Variables == nil {
    17  		ctx.Variables = make(map[string]cty.Value)
    18  	}
    19  	return &Context{
    20  		ctx:    ctx,
    21  		parent: parent,
    22  	}
    23  }
    24  
    25  func (c *Context) NewChild() *Context {
    26  	return NewContext(c.ctx.NewChild(), c)
    27  }
    28  
    29  func (c *Context) Parent() *Context {
    30  	return c.parent
    31  }
    32  
    33  func (c *Context) Inner() *hcl.EvalContext {
    34  	return c.ctx
    35  }
    36  
    37  func (c *Context) Root() *Context {
    38  	root := c
    39  	for root.Parent() != nil {
    40  		root = root.Parent()
    41  	}
    42  	return root
    43  }
    44  
    45  func (c *Context) Get(parts ...string) cty.Value {
    46  	if len(parts) == 0 {
    47  		return cty.NilVal
    48  	}
    49  	src := c.ctx.Variables
    50  	for i, part := range parts {
    51  		if i == len(parts)-1 {
    52  			return src[part]
    53  		}
    54  		nextPart := src[part]
    55  		if nextPart == cty.NilVal {
    56  			return cty.NilVal
    57  		}
    58  		src = nextPart.AsValueMap()
    59  	}
    60  	return cty.NilVal
    61  }
    62  
    63  func (c *Context) GetByDot(path string) cty.Value {
    64  	return c.Get(strings.Split(path, ".")...)
    65  }
    66  
    67  func (c *Context) SetByDot(val cty.Value, path string) {
    68  	c.Set(val, strings.Split(path, ".")...)
    69  }
    70  
    71  func (c *Context) Set(val cty.Value, parts ...string) {
    72  	if len(parts) == 0 {
    73  		return
    74  	}
    75  
    76  	v := mergeVars(c.ctx.Variables[parts[0]], parts[1:], val)
    77  	c.ctx.Variables[parts[0]] = v
    78  }
    79  
    80  func mergeVars(src cty.Value, parts []string, value cty.Value) cty.Value {
    81  
    82  	if len(parts) == 0 {
    83  		if value.IsKnown() && value.Type().IsObjectType() && !value.IsNull() && value.LengthInt() > 0 && src.IsKnown() && src.Type().IsObjectType() && !src.IsNull() && src.LengthInt() > 0 {
    84  			return mergeObjects(src, value)
    85  		}
    86  		return value
    87  	}
    88  
    89  	data := make(map[string]cty.Value)
    90  	if src.Type().IsObjectType() && !src.IsNull() && src.LengthInt() > 0 {
    91  		data = src.AsValueMap()
    92  		tmp, ok := src.AsValueMap()[parts[0]]
    93  		if !ok {
    94  			src = cty.ObjectVal(make(map[string]cty.Value))
    95  		} else {
    96  			src = tmp
    97  		}
    98  	}
    99  
   100  	data[parts[0]] = mergeVars(src, parts[1:], value)
   101  
   102  	return cty.ObjectVal(data)
   103  }
   104  
   105  func mergeObjects(a cty.Value, b cty.Value) cty.Value {
   106  	output := make(map[string]cty.Value)
   107  
   108  	for key, val := range a.AsValueMap() {
   109  		output[key] = val
   110  	}
   111  	for key, val := range b.AsValueMap() {
   112  		old, exists := output[key]
   113  		if exists && val.IsKnown() && val.Type().IsObjectType() && !val.IsNull() && val.LengthInt() > 0 && old.IsKnown() && old.Type().IsObjectType() && !old.IsNull() && old.LengthInt() > 0 {
   114  			output[key] = mergeObjects(val, old)
   115  		} else {
   116  			output[key] = val
   117  		}
   118  	}
   119  	return cty.ObjectVal(output)
   120  }