Files
trivy/pkg/iac/scanners/azure/value.go
2025-09-15 08:46:44 +00:00

341 lines
6.5 KiB
Go

package azure
import (
"encoding/json/jsontext"
"encoding/json/v2"
"fmt"
"maps"
"slices"
"strings"
"time"
"github.com/samber/lo"
"github.com/aquasecurity/trivy/pkg/iac/types"
)
type EvalContext struct{}
type Kind string
const (
KindUnresolvable Kind = "unresolvable"
KindNull Kind = "null"
KindBoolean Kind = "boolean"
KindString Kind = "string"
KindNumber Kind = "number"
KindObject Kind = "object"
KindArray Kind = "array"
KindExpression Kind = "expression"
)
type Value struct {
metadata *types.Metadata
rLit any
rMap map[string]Value
rArr []Value
Kind Kind
Comments []string
}
var NullValue = Value{
Kind: KindNull,
}
func NewValue(value any, metadata types.Metadata) Value {
v := Value{
metadata: &metadata,
}
switch ty := value.(type) {
case []any:
v.Kind = KindArray
for _, child := range ty {
if internal, ok := child.(Value); ok {
v.rArr = append(v.rArr, internal)
} else {
v.rArr = append(v.rArr, NewValue(child, metadata))
}
}
case []Value:
v.Kind = KindArray
v.rArr = append(v.rArr, ty...)
case map[string]any:
v.Kind = KindObject
v.rMap = make(map[string]Value)
for key, val := range ty {
if internal, ok := val.(Value); ok {
v.rMap[key] = internal
} else {
v.rMap[key] = NewValue(val, metadata)
}
}
case map[string]Value:
v.Kind = KindObject
v.rMap = make(map[string]Value)
maps.Copy(v.rMap, ty)
case string:
v.Kind = KindString
v.rLit = ty
case int, int64, int32, float32, float64, int8, int16, uint8, uint16, uint32, uint64:
v.Kind = KindNumber
v.rLit = ty
case bool:
v.Kind = KindBoolean
v.rLit = ty
case nil:
v.Kind = KindNull
v.rLit = ty
default:
v.Kind = KindUnresolvable
v.rLit = ty
}
return v
}
func NewExprValue(value string, metadata types.Metadata) Value {
return Value{
metadata: &metadata,
Kind: KindExpression,
rLit: value,
}
}
func (v *Value) GetMetadata() types.Metadata {
return lo.FromPtr(v.metadata)
}
func (v *Value) SetMetadata(m *types.Metadata) {
v.metadata = m
}
func (v *Value) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
switch k := dec.PeekKind(); k {
case 't', 'f':
v.Kind = KindBoolean
if err := json.UnmarshalDecode(dec, &v.rLit); err != nil {
return err
}
case '"':
var s string
if err := json.UnmarshalDecode(dec, &s); err != nil {
return err
}
if strings.HasPrefix(s, "[") && !strings.HasPrefix(s, "[[") && strings.HasSuffix(s, "]") {
v.Kind = KindExpression
v.rLit = s[1 : len(s)-1]
} else {
v.Kind = KindString
v.rLit = s
}
case '0':
v.Kind = KindNumber
if err := json.UnmarshalDecode(dec, &v.rLit); err != nil {
return err
}
if f, ok := v.rLit.(float64); ok {
if i := int64(f); float64(i) == f {
v.rLit = i
}
}
case '[':
v.Kind = KindArray
if err := json.UnmarshalDecode(dec, &v.rArr); err != nil {
return err
}
case '{':
v.Kind = KindObject
if err := json.UnmarshalDecode(dec, &v.rMap); err != nil {
return err
}
case 'n':
// TODO: UnmarshalJSONFrom is called only for the root null
return dec.SkipValue()
case 0:
return dec.SkipValue()
default:
return fmt.Errorf("unexpected token kind %q at %d", k.String(), dec.InputOffset())
}
return nil
}
func (v Value) AsString() string {
v.Resolve()
if v.Kind != KindString {
return ""
}
return v.rLit.(string)
}
func (v Value) AsBool() bool {
v.Resolve()
if v.Kind != KindBoolean {
return false
}
return v.rLit.(bool)
}
func (v Value) AsInt() int {
v.Resolve()
if v.Kind != KindNumber {
return 0
}
return int(v.rLit.(int64))
}
func (v Value) AsFloat() float64 {
v.Resolve()
if v.Kind != KindNumber {
return 0
}
return v.rLit.(float64)
}
func (v Value) AsIntValue(defaultValue int, metadata types.Metadata) types.IntValue {
v.Resolve()
if v.Kind != KindNumber {
return types.Int(defaultValue, metadata)
}
return types.Int(v.AsInt(), metadata)
}
func (v Value) AsBoolValue(defaultValue bool, metadata types.Metadata) types.BoolValue {
v.Resolve()
if v.Kind == KindString {
possibleValue := strings.ToLower(v.rLit.(string))
if slices.Contains([]string{
"true",
"1",
"yes",
"on",
"enabled",
}, possibleValue) {
return types.Bool(true, metadata)
}
}
if v.Kind != KindBoolean {
return types.Bool(defaultValue, metadata)
}
return types.Bool(v.rLit.(bool), v.GetMetadata())
}
func (v Value) EqualTo(value any) bool {
switch ty := value.(type) {
case string:
return v.AsString() == ty
default:
panic("not supported")
}
}
func (v Value) AsStringValue(defaultValue string, metadata types.Metadata) types.StringValue {
v.Resolve()
if v.Kind != KindString {
return types.StringDefault(defaultValue, metadata)
}
return types.String(v.rLit.(string), v.GetMetadata())
}
func (v Value) GetMapValue(key string) Value {
v.Resolve()
if v.Kind != KindObject {
return NullValue
}
v, exists := v.rMap[key]
if !exists {
return NullValue
}
return v
}
func (v Value) AsMap() map[string]Value {
v.Resolve()
if v.Kind != KindObject {
return nil
}
return v.rMap
}
func (v Value) AsList() []Value {
v.Resolve()
if v.Kind != KindArray {
return nil
}
return v.rArr
}
func (v Value) Raw() any {
switch v.Kind {
case KindArray:
// TODO: recursively build raw array
return nil
case KindObject:
// TODO: recursively build raw object
return nil
default:
return v.rLit
}
}
func (v *Value) Resolve() {
if v.Kind != KindExpression {
return
}
// if resolver, ok := v.Metadata.Internal().(Resolver); ok {
// *v = resolver.ResolveExpression(*v)
// }
}
func (v Value) HasKey(key string) bool {
v.Resolve()
_, ok := v.rMap[key]
return ok
}
func (v Value) AsTimeValue(parentMeta types.Metadata) types.TimeValue {
v.Resolve()
switch v.Kind {
case KindString:
t, err := time.Parse(time.RFC3339, v.rLit.(string))
if err != nil {
return types.Time(time.Time{}, parentMeta)
}
return types.Time(t, v.GetMetadata())
case KindNumber:
var tv int64
switch vv := v.rLit.(type) {
case float64:
tv = int64(vv)
case int64:
tv = vv
}
return types.Time(time.Unix(tv, 0), v.GetMetadata())
default:
return types.Time(time.Time{}, parentMeta)
}
}
func (v Value) AsStringValuesList(defaultValue string) (stringValues []types.StringValue) {
v.Resolve()
if v.Kind != KindArray {
return
}
for _, item := range v.rArr {
stringValues = append(stringValues, item.AsStringValue(defaultValue, item.GetMetadata()))
}
return stringValues
}
func (v Value) IsNull() bool {
return v.Kind == KindNull
}