mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 15:50:15 -08:00
147 lines
2.7 KiB
Go
147 lines
2.7 KiB
Go
package context
|
|
|
|
import (
|
|
"maps"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
type Context struct {
|
|
ctx *hcl.EvalContext
|
|
parent *Context
|
|
}
|
|
|
|
func NewContext(ctx *hcl.EvalContext, parent *Context) *Context {
|
|
if ctx.Variables == nil {
|
|
ctx.Variables = make(map[string]cty.Value)
|
|
}
|
|
return &Context{
|
|
ctx: ctx,
|
|
parent: parent,
|
|
}
|
|
}
|
|
|
|
func (c *Context) NewChild() *Context {
|
|
return NewContext(c.ctx.NewChild(), c)
|
|
}
|
|
|
|
func (c *Context) Parent() *Context {
|
|
return c.parent
|
|
}
|
|
|
|
func (c *Context) Inner() *hcl.EvalContext {
|
|
return c.ctx
|
|
}
|
|
|
|
func (c *Context) Root() *Context {
|
|
root := c
|
|
for root.Parent() != nil {
|
|
root = root.Parent()
|
|
}
|
|
return root
|
|
}
|
|
|
|
func (c *Context) Get(parts ...string) cty.Value {
|
|
if len(parts) == 0 {
|
|
return cty.NilVal
|
|
}
|
|
|
|
curr := c.ctx.Variables[parts[0]]
|
|
if len(parts) == 1 {
|
|
return curr
|
|
}
|
|
|
|
for i, part := range parts[1:] {
|
|
if !curr.Type().HasAttribute(part) {
|
|
return cty.NilVal
|
|
}
|
|
|
|
attr := curr.GetAttr(part)
|
|
|
|
if i == len(parts)-2 { // iteration from the first element
|
|
return attr
|
|
}
|
|
|
|
if !attr.IsKnown() || !attr.Type().IsObjectType() {
|
|
return cty.NilVal
|
|
}
|
|
curr = attr
|
|
}
|
|
|
|
return cty.NilVal
|
|
}
|
|
|
|
func (c *Context) GetByDot(path string) cty.Value {
|
|
return c.Get(strings.Split(path, ".")...)
|
|
}
|
|
|
|
func (c *Context) SetByDot(val cty.Value, path string) {
|
|
c.Set(val, strings.Split(path, ".")...)
|
|
}
|
|
|
|
func (c *Context) Set(val cty.Value, parts ...string) {
|
|
if len(parts) == 0 {
|
|
return
|
|
}
|
|
|
|
v := mergeVars(c.ctx.Variables[parts[0]], parts[1:], val)
|
|
c.ctx.Variables[parts[0]] = v
|
|
}
|
|
|
|
func (c *Context) Replace(val cty.Value, path string) {
|
|
parts := strings.Split(path, ".")
|
|
if len(parts) == 0 {
|
|
return
|
|
}
|
|
|
|
delete(c.ctx.Variables, parts[0])
|
|
c.Set(val, parts...)
|
|
}
|
|
|
|
func mergeVars(src cty.Value, parts []string, value cty.Value) cty.Value {
|
|
|
|
if len(parts) == 0 {
|
|
if isNotEmptyObject(src) && isNotEmptyObject(value) {
|
|
return mergeObjects(src, value)
|
|
}
|
|
return value
|
|
}
|
|
|
|
data := make(map[string]cty.Value)
|
|
if isNotEmptyObject(src) {
|
|
data = src.AsValueMap()
|
|
if attr, ok := data[parts[0]]; ok {
|
|
src = attr
|
|
} else {
|
|
src = cty.EmptyObjectVal
|
|
}
|
|
}
|
|
|
|
data[parts[0]] = mergeVars(src, parts[1:], value)
|
|
|
|
return cty.ObjectVal(data)
|
|
}
|
|
|
|
func mergeObjects(a, b cty.Value) cty.Value {
|
|
output := make(map[string]cty.Value)
|
|
|
|
maps.Copy(output, a.AsValueMap())
|
|
b.ForEachElement(func(key, val cty.Value) (stop bool) {
|
|
k := key.AsString()
|
|
old := output[k]
|
|
if old.IsKnown() && isNotEmptyObject(old) && isNotEmptyObject(val) {
|
|
output[k] = mergeObjects(old, val)
|
|
} else {
|
|
output[k] = val
|
|
}
|
|
return false
|
|
})
|
|
return cty.ObjectVal(output)
|
|
}
|
|
|
|
func isNotEmptyObject(val cty.Value) bool {
|
|
return !val.IsNull() && val.IsKnown() && val.Type().IsObjectType() && val.LengthInt() > 0
|
|
}
|