package component
import (
"reflect"
"strconv"
"github.com/leslie-qiwa/flat"
"github.com/logrusorgru/aurora"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
)
type Value struct {
data string
parsed dataformat.KV
dataFormat string
}
func NewValue(data string) *Value {
if data == "" {
return &Value{}
}
v := &Value{data: data}
decodedDataformat, err := dataformat.Decode(data)
if err == nil && decodedDataformat != nil {
v.SetParsed(decodedDataformat.Data, decodedDataformat.DataFormat)
}
return v
}
func (v *Value) Clone() *Value {
return &Value{
data: v.data,
parsed: v.parsed.Clone(),
dataFormat: v.dataFormat,
}
}
func (v *Value) String() string {
return v.data
}
func (v *Value) Parsed() dataformat.KV {
return v.parsed
}
func (v *Value) SetParsed(data dataformat.KV, dataFormat string) {
v.dataFormat = dataFormat
if data.OrderedMap != nil {
v.parsed = data
return
}
parsed := data.Map
flattened, err := flat.Flatten(parsed, flatOpts)
if err == nil {
v.parsed = dataformat.KVMap(flattened)
} else {
v.parsed = dataformat.KVMap(parsed)
}
}
func (v *Value) SetParsedValue(key, value string) bool {
if key == "" {
return false
}
origValue := v.parsed.Get(key)
if origValue == nil {
v.parsed.Set(key, value)
return true
}
switch v := origValue.(type) {
case []interface{}:
if len(v) > 0 {
v[len(v)-1] = value
}
origValue = v
case string:
origValue = value
case int, int32, int64, float32, float64:
parsed, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return false
}
origValue = parsed
case bool:
parsed, err := strconv.ParseBool(value)
if err != nil {
return false
}
origValue = parsed
default:
if val, ok := IsTypedSlice(v); ok {
if len(val) > 0 {
val[len(val)-1] = value
}
origValue = val
} else {
gologger.DefaultLogger.Print().Msgf("[%v] unknown type %T for value %s", aurora.BrightYellow("WARN"), v, v)
}
}
v.parsed.Set(key, origValue)
return true
}
func (v *Value) Delete(key string) bool {
return v.parsed.Delete(key)
}
func (v *Value) Encode() (string, error) {
toEncodeStr := v.data
if v.parsed.OrderedMap != nil {
if v.dataFormat != "" {
dataformatStr, err := dataformat.Encode(v.parsed, v.dataFormat)
if err != nil {
return "", err
}
toEncodeStr = dataformatStr
}
return toEncodeStr, nil
}
nested, err := flat.Unflatten(v.parsed.Map, flatOpts)
if err != nil {
return "", err
}
if v.dataFormat != "" {
dataformatStr, err := dataformat.Encode(dataformat.KVMap(nested), v.dataFormat)
if err != nil {
return "", err
}
toEncodeStr = dataformatStr
}
return toEncodeStr, nil
}
func IsTypedSlice(v interface{}) ([]interface{}, bool) {
if reflect.ValueOf(v).Kind() == reflect.Slice {
slice := reflect.ValueOf(v)
interfaceSlice := make([]interface{}, slice.Len())
for i := 0; i < slice.Len(); i++ {
interfaceSlice[i] = slice.Index(i).Interface()
}
return interfaceSlice, true
}
return nil, false
}