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 }