Files
Doğan Can Bakır 1ec64a3f95 chore(deps): bump pdsec modules; switch to projectdiscovery/govaluate and aurora v4
Bumps clistats, fastdialer, retryablehttp-go, dsl, networkpolicy,
ratelimit, sarif, utils, wappalyzergo, cdncheck. dsl v0.8.18 moves to
the projectdiscovery/govaluate fork, and utils v0.11.0 pulls aurora v4,
so the corresponding imports and type references are migrated.

Refs #7380.
2026-05-18 17:14:29 +03:00

189 lines
4.5 KiB
Go

package component
import (
"reflect"
"strconv"
"github.com/leslie-qiwa/flat"
"github.com/logrusorgru/aurora/v4"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
)
// Value is a value component containing a single
// parameter for the component
//
// It is a type of container that is used to represent
// all the data values that are used in a request.
type Value struct {
data string
parsed dataformat.KV
dataFormat string
encoder dataformat.DataFormat
}
// NewValue returns a new value component
func NewValue(data string) *Value {
if data == "" {
return &Value{}
}
v := &Value{data: data}
// Do any dataformat decoding on the data if needed
decodedDataformat, err := dataformat.Decode(data)
if err == nil && decodedDataformat != nil {
v.SetParsed(decodedDataformat.Data, decodedDataformat.DataFormat)
}
return v
}
// Clones current state of this value
func (v *Value) Clone() *Value {
return &Value{
data: v.data,
parsed: v.parsed.Clone(),
dataFormat: v.dataFormat,
encoder: v.encoder,
}
}
// String returns the string representation of the value
func (v *Value) String() string {
return v.data
}
// Parsed returns the parsed value
func (v *Value) Parsed() dataformat.KV {
return v.parsed
}
// SetParsed sets the parsed value map
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)
}
}
// SetParsedValue sets the parsed value for a key
// in the parsed map
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
}
// TODO(dwisiswant0): I'm sure that this can be simplified because
// `dataformat.KV.*` is a type of `mapsutil.*` where the value is `any`. So,
// it looks like we won't type conversion here or even have its own methods
// inside `dataformat.KV`.
// If the value is a list, append to it
// otherwise replace it
switch v := origValue.(type) {
case []interface{}:
// update last value
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:
// explicitly check for typed slice
if val, ok := IsTypedSlice(v); ok {
if len(val) > 0 {
val[len(val)-1] = value
}
origValue = val
} else {
// make it default warning instead of error
gologger.DefaultLogger.Print().Msgf("[%v] unknown type %T for value %s", aurora.BrightYellow("WARN"), v, v)
}
}
v.parsed.Set(key, origValue)
return true
}
// Delete removes a key from the parsed value
func (v *Value) Delete(key string) bool {
return v.parsed.Delete(key)
}
// Encode encodes the value into a string
// using the dataformat and encoding
func (v *Value) encode(data dataformat.KV) (string, error) {
if v.encoder != nil {
return v.encoder.Encode(data)
}
return dataformat.Encode(data, v.dataFormat)
}
func (v *Value) Encode() (string, error) {
toEncodeStr := v.data
if v.parsed.OrderedMap != nil {
// flattening orderedmap not supported
if v.dataFormat != "" {
dataformatStr, err := v.encode(v.parsed)
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 := v.encode(dataformat.KVMap(nested))
if err != nil {
return "", err
}
toEncodeStr = dataformatStr
}
return toEncodeStr, nil
}
// In go, []int, []string are not implictily converted to []interface{}
// when using type assertion and they need to be handled separately.
func IsTypedSlice(v interface{}) ([]interface{}, bool) {
if reflect.ValueOf(v).Kind() == reflect.Slice {
// iterate and convert to []interface{}
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
}