mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 17:39:09 -10:00
Keys of different types can format to the same string (`1` and `1.0`), and keeping either value would depend on map iteration order. `ToJSONValue` and `ToJSONMap` now return an error on such a collision, which both `MarshalJSON` methods propagate. Co-Authored-By: Claude Opus 5.5 <[email protected]>
228 lines
5.1 KiB
Go
228 lines
5.1 KiB
Go
// Taken from https://github.com/spf13/cast.
|
|
|
|
package types
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
|
)
|
|
|
|
// JSONScalarToString converts an interface coming from json to string
|
|
// Inspired from: https://github.com/cli/cli/blob/09b09810dd812e3ede54b59ad9d6912b946ac6c5/pkg/export/template.go#L72
|
|
func JSONScalarToString(input interface{}) (string, error) {
|
|
switch tt := input.(type) {
|
|
case string:
|
|
return ToString(tt), nil
|
|
case float64:
|
|
return ToString(tt), nil
|
|
case nil:
|
|
return ToString(tt), nil
|
|
case bool:
|
|
return ToString(tt), nil
|
|
default:
|
|
return "", fmt.Errorf("cannot convert type to string: %v", tt)
|
|
}
|
|
}
|
|
|
|
// ToString converts an interface to string in a quick way
|
|
func ToString(data interface{}) string {
|
|
switch s := data.(type) {
|
|
case nil:
|
|
return ""
|
|
case string:
|
|
return s
|
|
case bool:
|
|
return strconv.FormatBool(s)
|
|
case float64:
|
|
return strconv.FormatFloat(s, 'f', -1, 64)
|
|
case float32:
|
|
return strconv.FormatFloat(float64(s), 'f', -1, 32)
|
|
case int:
|
|
return strconv.Itoa(s)
|
|
case int64:
|
|
return strconv.FormatInt(s, 10)
|
|
case int32:
|
|
return strconv.Itoa(int(s))
|
|
case int16:
|
|
return strconv.FormatInt(int64(s), 10)
|
|
case int8:
|
|
return strconv.FormatInt(int64(s), 10)
|
|
case uint:
|
|
return strconv.FormatUint(uint64(s), 10)
|
|
case uint64:
|
|
return strconv.FormatUint(s, 10)
|
|
case uint32:
|
|
return strconv.FormatUint(uint64(s), 10)
|
|
case uint16:
|
|
return strconv.FormatUint(uint64(s), 10)
|
|
case uint8:
|
|
return strconv.FormatUint(uint64(s), 10)
|
|
case []byte:
|
|
return string(s)
|
|
case severity.Holder:
|
|
return s.Severity.String()
|
|
case severity.Severity:
|
|
return s.String()
|
|
case fmt.Stringer:
|
|
return s.String()
|
|
case error:
|
|
return s.Error()
|
|
default:
|
|
return fmt.Sprintf("%v", data)
|
|
}
|
|
}
|
|
|
|
// ToStringNSlice converts an interface to string in a quick way or to a slice with strings
|
|
// if the input is a slice of interfaces.
|
|
func ToStringNSlice(data interface{}) interface{} {
|
|
switch s := data.(type) {
|
|
case []interface{}:
|
|
var a []string
|
|
for _, v := range s {
|
|
a = append(a, ToString(v))
|
|
}
|
|
return a
|
|
default:
|
|
return ToString(data)
|
|
}
|
|
}
|
|
|
|
func ToHexOrString(data interface{}) string {
|
|
switch s := data.(type) {
|
|
case string:
|
|
if isASCII(s) {
|
|
return s
|
|
}
|
|
return hex.Dump([]byte(s))
|
|
case []byte:
|
|
return hex.Dump(s)
|
|
default:
|
|
return fmt.Sprintf("%v", data)
|
|
}
|
|
}
|
|
|
|
func isASCII(s string) bool {
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] > 0x7f {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ToStringSlice casts an interface to a []string type.
|
|
func ToStringSlice(i interface{}) []string {
|
|
var a []string
|
|
|
|
switch v := i.(type) {
|
|
case []interface{}:
|
|
for _, u := range v {
|
|
a = append(a, ToString(u))
|
|
}
|
|
return a
|
|
case []string:
|
|
return v
|
|
case string:
|
|
return strings.Fields(v)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ToByteSlice casts an interface to a []byte type.
|
|
func ToByteSlice(i interface{}) []byte {
|
|
switch v := i.(type) {
|
|
case []byte:
|
|
return v
|
|
case []string:
|
|
return []byte(strings.Join(v, ""))
|
|
case string:
|
|
return []byte(v)
|
|
case []interface{}:
|
|
var buff bytes.Buffer
|
|
for _, u := range v {
|
|
buff.WriteString(ToString(u))
|
|
}
|
|
return buff.Bytes()
|
|
default:
|
|
strValue := ToString(i)
|
|
return []byte(strValue)
|
|
}
|
|
}
|
|
|
|
// ToStringMap casts an interface to a map[string]interface{} type.
|
|
func ToStringMap(i interface{}) map[string]interface{} {
|
|
var m = map[string]interface{}{}
|
|
|
|
switch v := i.(type) {
|
|
case map[interface{}]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = val
|
|
}
|
|
return m
|
|
case map[string]interface{}:
|
|
return v
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ToJSONValue returns a copy of value in which every map[interface{}]interface{}
|
|
// nested in maps and slices becomes a map[string]interface{} keyed by the
|
|
// fmt.Sprint form of its keys. Template YAML decodes nested maps with
|
|
// interface keys, which the JSON encoder rejects. Two keys sharing a
|
|
// fmt.Sprint form, such as 1 and 1.0, return an error.
|
|
func ToJSONValue(value interface{}) (interface{}, error) {
|
|
switch typed := value.(type) {
|
|
case map[interface{}]interface{}:
|
|
converted := make(map[string]interface{}, len(typed))
|
|
for key, item := range typed {
|
|
name := fmt.Sprint(key)
|
|
if _, exists := converted[name]; exists {
|
|
return nil, fmt.Errorf("map key %q collides after string conversion", name)
|
|
}
|
|
encoded, err := ToJSONValue(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
converted[name] = encoded
|
|
}
|
|
return converted, nil
|
|
case map[string]interface{}:
|
|
return ToJSONMap(typed)
|
|
case []interface{}:
|
|
converted := make([]interface{}, len(typed))
|
|
for i, item := range typed {
|
|
encoded, err := ToJSONValue(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
converted[i] = encoded
|
|
}
|
|
return converted, nil
|
|
default:
|
|
return value, nil
|
|
}
|
|
}
|
|
|
|
// ToJSONMap applies ToJSONValue to every value of m, returning nil for a nil m.
|
|
func ToJSONMap(m map[string]interface{}) (map[string]interface{}, error) {
|
|
if m == nil {
|
|
return nil, nil
|
|
}
|
|
converted := make(map[string]interface{}, len(m))
|
|
for key, item := range m {
|
|
encoded, err := ToJSONValue(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
converted[key] = encoded
|
|
}
|
|
return converted, nil
|
|
}
|