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

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package llx
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"go.mondoo.com/cnquery/types"
    14  )
    15  
    16  // RawData is an internal track of raw data that can be cast to the appropriate types
    17  // It cannot be sent over the wire unless serialized (expensive) or
    18  // converted to a proto data structure
    19  type RawData struct {
    20  	Type  types.Type  `json:"type"`
    21  	Value interface{} `json:"value"`
    22  	Error error       `json:"-"`
    23  }
    24  
    25  // a helper structure exclusively used for json unmarshalling of errors
    26  // TODO: find a better way of doing this, this workaround is annoying
    27  type errData struct {
    28  	Error string `json:"error"`
    29  }
    30  
    31  func (r *RawData) MarshalJSON() ([]byte, error) {
    32  	if r.Error != nil {
    33  		return json.Marshal(errData{Error: r.Error.Error()})
    34  	}
    35  
    36  	if r.Type == types.Time {
    37  		tv := r.Value.(*time.Time)
    38  		ut := tv.Unix()
    39  		return json.Marshal(RawData{Type: r.Type, Value: ut})
    40  	}
    41  
    42  	type rd2 RawData
    43  	return json.Marshal((*rd2)(r))
    44  }
    45  
    46  func (r *RawData) UnmarshalJSON(data []byte) error {
    47  	type tmp RawData
    48  	if err := json.Unmarshal(data, (*tmp)(r)); err == nil && r.Type != "" {
    49  		switch r.Type {
    50  		case types.Int:
    51  			r.Value = int64(r.Value.(float64))
    52  		case types.Time:
    53  			tv := r.Value.(float64)
    54  			// JSON serialization of numbers is limited to 1**53 precision, see:
    55  			// https://stackoverflow.com/questions/13502398/json-integers-limit-on-size#comment80159722_13502497
    56  			if tv > (1 << 53) {
    57  				r.Value = &NeverFutureTime
    58  			} else if tv < (-(1 << 53)) {
    59  				r.Value = &NeverPastTime
    60  			} else {
    61  				v := time.Unix(int64(tv), 0)
    62  				r.Value = &v
    63  			}
    64  		}
    65  		return nil
    66  	}
    67  
    68  	var e errData
    69  	if err := json.Unmarshal(data, &e); err != nil {
    70  		return err
    71  	}
    72  
    73  	r.Error = errors.New(e.Error)
    74  	return nil
    75  }
    76  
    77  func dictRawDataString(value interface{}) string {
    78  	switch x := value.(type) {
    79  	case bool:
    80  		if x {
    81  			return "true"
    82  		} else {
    83  			return "false"
    84  		}
    85  	case int64:
    86  		return strconv.FormatInt(x, 10)
    87  	case float64:
    88  		return strconv.FormatFloat(x, 'f', -1, 64)
    89  	case string:
    90  		return "\"" + x + "\""
    91  	case []interface{}:
    92  		var res strings.Builder
    93  		res.WriteString("[")
    94  		for i := range x {
    95  			res.WriteString(dictRawDataString(x[i]))
    96  			if i != len(x)-1 {
    97  				res.WriteString(",")
    98  			}
    99  		}
   100  		res.WriteString("]")
   101  		return res.String()
   102  	case map[string]interface{}:
   103  		var res strings.Builder
   104  		var i int
   105  		res.WriteString("{")
   106  		for k, v := range x {
   107  			res.WriteString("\"" + k + "\":")
   108  			res.WriteString(dictRawDataString(v))
   109  			if i != len(x)-1 {
   110  				res.WriteString(",")
   111  			}
   112  			i++
   113  		}
   114  		res.WriteString("}")
   115  		return res.String()
   116  	default:
   117  		return "?value? (type:dict)"
   118  	}
   119  }
   120  
   121  func rawDataString(typ types.Type, value interface{}) string {
   122  	if value == nil {
   123  		return "<null>"
   124  	}
   125  
   126  	switch typ.Underlying() {
   127  	case types.Bool:
   128  		b := value.(bool)
   129  		if b {
   130  			return "true"
   131  		} else {
   132  			return "false"
   133  		}
   134  	case types.Int:
   135  		return strconv.FormatInt(value.(int64), 10)
   136  	case types.Float:
   137  		return strconv.FormatFloat(value.(float64), 'f', -1, 64)
   138  	case types.String:
   139  		return "\"" + value.(string) + "\""
   140  	case types.Regex:
   141  		return "/" + value.(string) + "/"
   142  	case types.Time:
   143  		return value.(*time.Time).String()
   144  	case types.Dict:
   145  		return dictRawDataString(value)
   146  	case types.Score:
   147  		return ScoreString(value.([]byte))
   148  	case types.ArrayLike:
   149  		var res strings.Builder
   150  		arr := value.([]interface{})
   151  		res.WriteString("[")
   152  		for i := range arr {
   153  			res.WriteString(rawDataString(typ.Child(), arr[i]))
   154  			if i != len(arr)-1 {
   155  				res.WriteString(",")
   156  			}
   157  		}
   158  		res.WriteString("]")
   159  		return res.String()
   160  	case types.MapLike:
   161  		switch typ.Key() {
   162  		case types.String:
   163  			var res strings.Builder
   164  			m := value.(map[string]interface{})
   165  			var i int
   166  			res.WriteString("{")
   167  			for k, v := range m {
   168  				res.WriteString("\"" + k + "\":")
   169  				res.WriteString(rawDataString(typ.Child(), v))
   170  				if i != len(m)-1 {
   171  					res.WriteString(",")
   172  				}
   173  				i++
   174  			}
   175  			res.WriteString("}")
   176  			return res.String()
   177  		default:
   178  			return "map[?]?"
   179  		}
   180  	default:
   181  		return "?value? (typ:" + typ.Label() + ")"
   182  	}
   183  }
   184  
   185  func (r *RawData) String() string {
   186  	return rawDataString(r.Type, r.Value)
   187  }
   188  
   189  // IsTruthy indicates how the query is scored.
   190  // the first return value gives true/false based on if the data indicates success/failure
   191  // the second value indicates if we were able to come to a decision based on the data
   192  // examples:
   193  //
   194  //	truthy: true, 123, [true], "string"
   195  //	falsey: false
   196  //
   197  // if the data includes an error, it is falsey
   198  func (r *RawData) IsTruthy() (bool, bool) {
   199  	if r.Error != nil {
   200  		return false, false
   201  	}
   202  	return isTruthy(r.Value, r.Type)
   203  }
   204  
   205  // Score returns the score value if the value is of score type
   206  func (r *RawData) Score() (int, bool) {
   207  	if r.Error != nil {
   208  		return 0, false
   209  	}
   210  
   211  	if r.Type != types.Score {
   212  		return 0, false
   213  	}
   214  
   215  	v, err := scoreValue(r.Value.([]byte))
   216  	if err != nil {
   217  		return v, false
   218  	}
   219  	return v, true
   220  }
   221  
   222  func isTruthy(data interface{}, typ types.Type) (bool, bool) {
   223  	if data == nil &&
   224  		(typ.NotSet() || !typ.IsResource()) {
   225  		return false, true
   226  	}
   227  
   228  	switch typ.Underlying() {
   229  	case types.Any:
   230  		if b, ok := data.(bool); ok {
   231  			return b, true
   232  		}
   233  		if d, ok := data.(*RawData); ok {
   234  			return isTruthy(d.Value, d.Type)
   235  		}
   236  		return false, false
   237  
   238  	case types.Nil:
   239  		return false, true
   240  
   241  	case types.Bool:
   242  		return data.(bool), true
   243  
   244  	case types.Int:
   245  		return data.(int64) != 0, true
   246  
   247  	case types.Float:
   248  		return data.(float64) != 0, true
   249  
   250  	case types.String:
   251  		return data.(string) != "", true
   252  
   253  	case types.Regex:
   254  		return data.(string) != "", true
   255  
   256  	case types.Time:
   257  		dt := data.(*time.Time)
   258  
   259  		// needs separate testing due to: https://golang.org/doc/faq#nil_error
   260  		if dt == nil {
   261  			return false, true
   262  		}
   263  
   264  		return !dt.IsZero(), true
   265  
   266  	case types.Block:
   267  		res := true
   268  
   269  		m := data.(map[string]interface{})
   270  		if m != nil {
   271  			if bif, ok := m["__t"]; ok {
   272  				if rd, ok := bif.(*RawData); ok {
   273  					return rd.IsTruthy()
   274  				}
   275  			}
   276  			for _, v := range m {
   277  				t1, f1 := isTruthy(v, types.Any)
   278  				if f1 {
   279  					res = res && t1
   280  				}
   281  			}
   282  		}
   283  
   284  		return res, true
   285  
   286  	case types.ArrayLike:
   287  		arr := data.([]interface{})
   288  
   289  		// Empty arrays count as false here, this is because users
   290  		// frequently write statements like:
   291  		//     list.where(a == 1) && list.where(b == 2)
   292  		// which technically should be:
   293  		//     list.contains(a == 1) && list.contains(b == 2)
   294  		// However, it's so frequent with our users and we can't see
   295  		// a reasonable upside to keeping empty array as truthy, since
   296  		// null checks are far less likely.
   297  		if len(arr) == 0 {
   298  			return false, true
   299  		}
   300  
   301  		res := true
   302  		for i := range arr {
   303  			t1, f1 := isTruthy(arr[i], typ.Child())
   304  			if f1 {
   305  				res = res && t1
   306  			}
   307  		}
   308  
   309  		return res, true
   310  
   311  	case types.MapLike:
   312  		res := true
   313  
   314  		switch typ.Key() {
   315  		case types.String:
   316  			m := data.(map[string]interface{})
   317  			for _, v := range m {
   318  				t1, f1 := isTruthy(v, typ.Child())
   319  				if f1 {
   320  					res = res && t1
   321  				}
   322  			}
   323  
   324  		case types.Int:
   325  			m := data.(map[int]interface{})
   326  			for _, v := range m {
   327  				t1, f1 := isTruthy(v, typ.Child())
   328  				if f1 {
   329  					res = res && t1
   330  				}
   331  			}
   332  
   333  		default:
   334  			return false, false
   335  		}
   336  
   337  		return res, true
   338  
   339  	case types.ResourceLike:
   340  		return true, true
   341  
   342  	default:
   343  		return false, false
   344  	}
   345  }
   346  
   347  func (r *RawData) IsSuccess() (bool, bool) {
   348  	if r.Error != nil {
   349  		return false, false
   350  	}
   351  	return isSuccess(r.Value, r.Type)
   352  }
   353  
   354  func isSuccess(data interface{}, typ types.Type) (bool, bool) {
   355  	if data == nil &&
   356  		(typ.NotSet() || !typ.IsResource()) {
   357  		return false, false
   358  	}
   359  
   360  	switch typ.Underlying() {
   361  	case types.Any:
   362  		if b, ok := data.(bool); ok {
   363  			return b, true
   364  		}
   365  		if d, ok := data.(*RawData); ok {
   366  			return isSuccess(d.Value, d.Type)
   367  		}
   368  		return false, false
   369  	case types.Bool:
   370  		return data.(bool), true
   371  	case types.Block:
   372  		m := data.(map[string]interface{})
   373  		if m != nil {
   374  			if bif, ok := m["__s"]; ok {
   375  				if rd, ok := bif.(*RawData); ok {
   376  					return rd.IsSuccess()
   377  				}
   378  			}
   379  			return false, false
   380  		}
   381  
   382  		return false, false
   383  
   384  	case types.ArrayLike:
   385  		arr := data.([]interface{})
   386  		res := true
   387  		valid := false
   388  
   389  		for i := range arr {
   390  			t1, f1 := isSuccess(arr[i], typ.Child())
   391  			if f1 {
   392  				res = res && t1
   393  				valid = true
   394  			}
   395  		}
   396  
   397  		return res && valid, valid
   398  
   399  	default:
   400  		return false, false
   401  	}
   402  }
   403  
   404  // UnsetData for the unset value
   405  var UnsetData = &RawData{Type: types.Unset}
   406  
   407  // AnyData returns any value embedded in a RawData
   408  func AnyData(v interface{}) *RawData {
   409  	return &RawData{
   410  		Type:  types.Any,
   411  		Value: v,
   412  	}
   413  }
   414  
   415  // NilData for the nil value
   416  var NilData = &RawData{Type: types.Nil}
   417  
   418  // BoolData creates a rawdata struct from a go boolean
   419  func BoolData(v bool) *RawData {
   420  	return &RawData{
   421  		Type:  types.Bool,
   422  		Value: v,
   423  	}
   424  }
   425  
   426  // BoolDataPtr creates a rawdata struct from a go boolean
   427  func BoolDataPtr(v *bool) *RawData {
   428  	if v == nil {
   429  		return NilData
   430  	}
   431  	return BoolData(*v)
   432  }
   433  
   434  // BoolFalse is a RawData boolean set to false
   435  var BoolFalse = BoolData(false)
   436  
   437  // BoolTrue is a RawData boolean set to true
   438  var BoolTrue = BoolData(true)
   439  
   440  // IntData creates a rawdata struct from a go int
   441  func IntData(v int64) *RawData {
   442  	return &RawData{
   443  		Type:  types.Int,
   444  		Value: v,
   445  	}
   446  }
   447  
   448  // IntDataPtr creates a rawdata struct from a go int pointer
   449  func IntDataPtr(v *int64) *RawData {
   450  	if v == nil {
   451  		return NilData
   452  	}
   453  	return IntData(*v)
   454  }
   455  
   456  // FloatData creates a rawdata struct from a go float
   457  func FloatData(v float64) *RawData {
   458  	return &RawData{
   459  		Type:  types.Float,
   460  		Value: v,
   461  	}
   462  }
   463  
   464  // StringData creates a rawdata struct from a go string
   465  func StringData(s string) *RawData {
   466  	return &RawData{
   467  		Type:  types.String,
   468  		Value: s,
   469  	}
   470  }
   471  
   472  func StringDataPtr(s *string) *RawData {
   473  	if s == nil {
   474  		return NilData
   475  	}
   476  	return StringData(*s)
   477  }
   478  
   479  // RegexData creates a rawdata struct from a go string
   480  func RegexData(r string) *RawData {
   481  	return &RawData{
   482  		Type:  types.Regex,
   483  		Value: r,
   484  	}
   485  }
   486  
   487  // TimeData creates a rawdata struct from a go time
   488  func TimeData(t time.Time) *RawData {
   489  	return TimeDataPtr(&t)
   490  }
   491  
   492  // TimeData creates a rawdata struct from a go time pointer
   493  func TimeDataPtr(t *time.Time) *RawData {
   494  	if t == nil {
   495  		return NilData
   496  	}
   497  	return &RawData{
   498  		Type:  types.Time,
   499  		Value: t,
   500  	}
   501  }
   502  
   503  // DictData creates a rawdata struct from raw dict data
   504  func DictData(r interface{}) *RawData {
   505  	return &RawData{
   506  		Type:  types.Dict,
   507  		Value: r,
   508  	}
   509  }
   510  
   511  // ScoreData creates a rawdata struct from raw score data
   512  func ScoreData(r interface{}) *RawData {
   513  	return &RawData{
   514  		Type:  types.Score,
   515  		Value: r,
   516  	}
   517  }
   518  
   519  // RefData creates a rawdata struct from a go ref
   520  func RefDataV1(v int32) *RawData {
   521  	return &RawData{
   522  		Type:  types.Ref,
   523  		Value: v,
   524  	}
   525  }
   526  
   527  // RefData creates a rawdata struct from a go ref
   528  func RefDataV2(v uint64) *RawData {
   529  	return &RawData{
   530  		Type:  types.Ref,
   531  		Value: v,
   532  	}
   533  }
   534  
   535  // ArrayData creates a rawdata struct from a go array + child data types
   536  func ArrayData(v []interface{}, typ types.Type) *RawData {
   537  	return &RawData{
   538  		Type:  types.Array(typ),
   539  		Value: v,
   540  	}
   541  }
   542  
   543  // MapData creates a rawdata struct from a go map + child data types
   544  func MapData(v map[string]interface{}, typ types.Type) *RawData {
   545  	return &RawData{
   546  		Type:  types.Map(types.String, typ),
   547  		Value: v,
   548  	}
   549  }
   550  
   551  // MapIntData creates a rawdata struct from a go int map + child data type
   552  func MapIntData(v map[int32]interface{}, typ types.Type) *RawData {
   553  	return &RawData{
   554  		Type:  types.Map(types.Int, typ),
   555  		Value: v,
   556  	}
   557  }
   558  
   559  // ResourceData creates a rawdata struct from a resource
   560  func ResourceData(v Resource, name string) *RawData {
   561  	return &RawData{
   562  		Type:  types.Resource(name),
   563  		Value: v,
   564  	}
   565  }
   566  
   567  // FunctionData creates a rawdata struct from a function reference
   568  func FunctionData(v int32, sig string) *RawData {
   569  	return &RawData{
   570  		Type:  types.Function(0, nil),
   571  		Value: v,
   572  	}
   573  }
   574  
   575  // RawResultByRef is used to sort an array of raw results
   576  type RawResultByRef []*RawResult
   577  
   578  func (a RawResultByRef) Len() int           { return len(a) }
   579  func (a RawResultByRef) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   580  func (a RawResultByRef) Less(i, j int) bool { return a[i].CodeID < a[j].CodeID }