go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/llx/rawdata_json.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package llx
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"math"
    12  	"sort"
    13  	"strconv"
    14  	"time"
    15  
    16  	"go.mondoo.com/cnquery/types"
    17  	"go.mondoo.com/cnquery/utils/sortx"
    18  )
    19  
    20  func intKeys(m map[int]interface{}) []int {
    21  	keys := make([]int, len(m))
    22  	var i int
    23  	for k := range m {
    24  		keys[i] = k
    25  		i++
    26  	}
    27  	return keys
    28  }
    29  
    30  // Note: We override the default output here to enable JSON5 like export of infinity.
    31  func int2json(i int64) string {
    32  	if i == math.MaxInt64 {
    33  		return "Inf"
    34  	}
    35  	if i == math.MinInt64 {
    36  		return "-Inf"
    37  	}
    38  
    39  	return strconv.FormatInt(i, 10)
    40  }
    41  
    42  // Note: We override the default output here to enable JSON5 like export of infinity.
    43  func float2json(f float64) string {
    44  	if math.IsInf(f, 1) {
    45  		return "Inf"
    46  	}
    47  	if math.IsInf(f, -1) {
    48  		return "-Inf"
    49  	}
    50  
    51  	return strconv.FormatFloat(f, 'g', -1, 64)
    52  }
    53  
    54  // Takes care of escaping the given string
    55  func string2json(s string) string {
    56  	return fmt.Sprintf("%#v", s)
    57  }
    58  
    59  func label(ref string, bundle *CodeBundle, isResource bool) string {
    60  	if bundle == nil {
    61  		return "<unknown>"
    62  	}
    63  
    64  	labels := bundle.Labels
    65  	if labels == nil {
    66  		return ref
    67  	}
    68  
    69  	label := labels.Labels[ref]
    70  	if label == "" {
    71  		return "<unknown>"
    72  	}
    73  
    74  	return label
    75  }
    76  
    77  func removeUnderscoreKeys(keys []string) []string {
    78  	results := make([]string, 0, len(keys))
    79  	for i := 0; i < len(keys); i++ {
    80  		if keys[i] != "" && keys[i][0] != '_' {
    81  			results = append(results, keys[i])
    82  		}
    83  	}
    84  	return results
    85  }
    86  
    87  func refMapJSON(typ types.Type, data map[string]interface{}, codeID string, bundle *CodeBundle, buf *bytes.Buffer) error {
    88  	buf.WriteByte('{')
    89  
    90  	keys := sortx.Keys(data)
    91  
    92  	// What is the best explanation for why we do this?
    93  	keys = removeUnderscoreKeys(keys)
    94  
    95  	last := len(keys) - 1
    96  	for i, k := range keys {
    97  		v := data[k]
    98  		label := label(k, bundle, true)
    99  		buf.WriteString(string2json(label))
   100  		buf.WriteString(":")
   101  
   102  		val := v.(*RawData)
   103  		if val.Error != nil {
   104  			buf.WriteString(PrettyPrintString("Error: " + val.Error.Error()))
   105  		} else {
   106  			rawDataJSON(val.Type, val.Value, k, bundle, buf)
   107  		}
   108  
   109  		if i != last {
   110  			buf.WriteByte(',')
   111  		}
   112  	}
   113  
   114  	buf.WriteByte('}')
   115  	return nil
   116  }
   117  
   118  func rawDictJSON(typ types.Type, raw interface{}, buf *bytes.Buffer) error {
   119  	switch data := raw.(type) {
   120  	case bool:
   121  		if data {
   122  			buf.WriteString("true")
   123  		} else {
   124  			buf.WriteString("false")
   125  		}
   126  		return nil
   127  
   128  	case int64:
   129  		buf.WriteString(int2json(data))
   130  		return nil
   131  
   132  	case float64:
   133  		buf.WriteString(float2json(data))
   134  		return nil
   135  
   136  	case string:
   137  		buf.WriteString(string2json(data))
   138  		return nil
   139  
   140  	case time.Time:
   141  		b, err := data.MarshalJSON()
   142  		buf.Write(b)
   143  		return err
   144  
   145  	case []interface{}:
   146  		buf.WriteByte('[')
   147  
   148  		last := len(data) - 1
   149  		for i := range data {
   150  			err := rawDictJSON(typ, data[i], buf)
   151  			if err != nil {
   152  				return err
   153  			}
   154  			if i != last {
   155  				buf.WriteByte(',')
   156  			}
   157  		}
   158  
   159  		buf.WriteByte(']')
   160  		return nil
   161  
   162  	case map[string]interface{}:
   163  		buf.WriteByte('{')
   164  
   165  		keys := sortx.Keys(data)
   166  
   167  		last := len(keys) - 1
   168  		for i, k := range keys {
   169  			v := data[k]
   170  			buf.WriteString(string2json(k) + ":")
   171  
   172  			if v == nil {
   173  				buf.WriteString("null")
   174  			} else {
   175  				err := rawDictJSON(typ, v, buf)
   176  				if err != nil {
   177  					return err
   178  				}
   179  			}
   180  
   181  			if i != last {
   182  				buf.WriteByte(',')
   183  			}
   184  		}
   185  
   186  		buf.WriteByte('}')
   187  		return nil
   188  
   189  	default:
   190  		b, err := json.Marshal(raw)
   191  		buf.Write(b)
   192  		return err
   193  	}
   194  }
   195  
   196  func rawArrayJSON(typ types.Type, data []interface{}, codeID string, bundle *CodeBundle, buf *bytes.Buffer) error {
   197  	buf.WriteByte('[')
   198  
   199  	last := len(data) - 1
   200  	childType := typ.Child()
   201  	var err error
   202  	for i := range data {
   203  		err = rawDataJSON(childType, data[i], codeID, bundle, buf)
   204  		if err != nil {
   205  			return err
   206  		}
   207  
   208  		if i != last {
   209  			buf.WriteByte(',')
   210  		}
   211  	}
   212  
   213  	buf.WriteByte(']')
   214  
   215  	return nil
   216  }
   217  
   218  func rawStringMapJSON(typ types.Type, data map[string]interface{}, codeID string, bundle *CodeBundle, buf *bytes.Buffer) error {
   219  	buf.WriteByte('{')
   220  
   221  	last := len(data) - 1
   222  	childType := typ.Child()
   223  	keys := sortx.Keys(data)
   224  
   225  	var err error
   226  	for i, key := range keys {
   227  		buf.WriteString(string2json(key) + ":")
   228  
   229  		err = rawDataJSON(childType, data[key], codeID, bundle, buf)
   230  		if err != nil {
   231  			return err
   232  		}
   233  
   234  		if i != last {
   235  			buf.WriteByte(',')
   236  		}
   237  	}
   238  
   239  	buf.WriteByte('}')
   240  
   241  	return nil
   242  }
   243  
   244  func rawIntMapJSON(typ types.Type, data map[int]interface{}, codeID string, bundle *CodeBundle, buf *bytes.Buffer) error {
   245  	buf.WriteByte('{')
   246  
   247  	last := len(data) - 1
   248  	childType := typ.Child()
   249  
   250  	keys := intKeys(data)
   251  	sort.Ints(keys)
   252  
   253  	var err error
   254  	for i, key := range keys {
   255  		buf.WriteString(string2json(strconv.Itoa(key)) + ":")
   256  
   257  		err = rawDataJSON(childType, data[key], codeID, bundle, buf)
   258  		if err != nil {
   259  			return err
   260  		}
   261  
   262  		if i != last {
   263  			buf.WriteByte(',')
   264  		}
   265  	}
   266  
   267  	buf.WriteByte('}')
   268  
   269  	return nil
   270  }
   271  
   272  // The heart of the JSON marshaller. We try to avoid the default marshaller whenever
   273  // possible for now, because our type system provides most of the information we need,
   274  // allowing us to avoid more costly reflection calls.
   275  func rawDataJSON(typ types.Type, data interface{}, codeID string, bundle *CodeBundle, buf *bytes.Buffer) error {
   276  	if typ.NotSet() {
   277  		return errors.New("type information is missing")
   278  	}
   279  
   280  	if data == nil {
   281  		buf.WriteString("null")
   282  		return nil
   283  	}
   284  
   285  	switch typ.Underlying() {
   286  	case types.Any:
   287  		r, err := json.Marshal(data)
   288  		buf.Write(r)
   289  		return err
   290  
   291  	case types.Ref:
   292  		r := "\"ref:" + fmt.Sprintf("%d", data.(int32)) + "\""
   293  		buf.WriteString(r)
   294  		return nil
   295  
   296  	case types.Nil:
   297  		buf.WriteString("null")
   298  		return nil
   299  
   300  	case types.Bool:
   301  		if data.(bool) {
   302  			buf.WriteString("true")
   303  		} else {
   304  			buf.WriteString("false")
   305  		}
   306  		return nil
   307  
   308  	case types.Int:
   309  		buf.WriteString(int2json(data.(int64)))
   310  		return nil
   311  
   312  	case types.Float:
   313  		// Note: We override the default output here to enable JSON5 like export of infinity.
   314  		if math.IsInf(data.(float64), 1) {
   315  			buf.WriteString("Inf")
   316  			return nil
   317  		}
   318  		if math.IsInf(data.(float64), -1) {
   319  			buf.WriteString("-Inf")
   320  			return nil
   321  		}
   322  
   323  		buf.WriteString(strconv.FormatFloat(data.(float64), 'g', -1, 64))
   324  		return nil
   325  
   326  	case types.String:
   327  		buf.WriteString(string2json(data.(string)))
   328  		return nil
   329  
   330  	case types.Regex:
   331  		raw := string2json(data.(string))
   332  		buf.WriteByte(raw[0])
   333  		buf.WriteByte('/')
   334  		buf.WriteString(raw[1 : len(raw)-1])
   335  		buf.WriteByte('/')
   336  		buf.WriteByte(raw[len(raw)-1])
   337  		return nil
   338  
   339  	case types.Time:
   340  		time := data.(*time.Time)
   341  		if time == nil {
   342  			buf.WriteString("null")
   343  			return nil
   344  		}
   345  
   346  		// if *time == NeverPastTime || *time == NeverFutureTime {
   347  		// 	TODO: ... unclear
   348  		// }
   349  
   350  		b, err := time.MarshalJSON()
   351  		buf.Write(b)
   352  		return err
   353  
   354  	case types.Dict:
   355  		return rawDictJSON(typ, data, buf)
   356  
   357  	case types.Score:
   358  		buf.WriteString(ScoreString(data.([]byte)))
   359  		return nil
   360  
   361  	case types.Block:
   362  		return refMapJSON(typ, data.(map[string]interface{}), codeID, bundle, buf)
   363  
   364  	case types.ArrayLike:
   365  		return rawArrayJSON(typ, data.([]interface{}), codeID, bundle, buf)
   366  
   367  	case types.MapLike:
   368  		if typ.Key() == types.String {
   369  			return rawStringMapJSON(typ, data.(map[string]interface{}), codeID, bundle, buf)
   370  		}
   371  		if typ.Key() == types.Int {
   372  			return rawIntMapJSON(typ, data.(map[int]interface{}), codeID, bundle, buf)
   373  		}
   374  		return errors.New("unable to marshal map, its type is not supported: " + typ.Label() + ", raw: " + fmt.Sprintf("%#v", data))
   375  
   376  	case types.ResourceLike:
   377  		r := data.(Resource)
   378  		idline := r.MqlName()
   379  		if id := r.MqlID(); id != "" {
   380  			idline += " id = " + id
   381  		}
   382  
   383  		buf.WriteString(string2json(idline))
   384  		return nil
   385  
   386  	default:
   387  		b, err := json.Marshal(data)
   388  		buf.Write(b)
   389  		return err
   390  	}
   391  }
   392  
   393  func JSONerror(err error) []byte {
   394  	return []byte("{\"error\":" + string2json(err.Error()) + "}")
   395  }
   396  
   397  func (r *RawData) JSON(codeID string, bundle *CodeBundle) []byte {
   398  	if r.Value == nil && r.Error != nil {
   399  		return JSONerror(r.Error)
   400  	}
   401  
   402  	var res bytes.Buffer
   403  	rawDataJSON(r.Type, r.Value, codeID, bundle, &res)
   404  	return res.Bytes()
   405  }
   406  
   407  func (r *RawData) JSONfield(codeID string, bundle *CodeBundle) []byte {
   408  	label := label(codeID, bundle, true)
   409  	value := r.JSON(codeID, bundle)
   410  	return []byte(string2json(label) + ":" + string(value))
   411  }