go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/llx/data_conversions.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package llx 5 6 import ( 7 "encoding/hex" 8 "errors" 9 "fmt" 10 "reflect" 11 "strconv" 12 "time" 13 14 "go.mondoo.com/cnquery/types" 15 "google.golang.org/protobuf/proto" 16 ) 17 18 type ( 19 dataConverter func(interface{}, types.Type) (*Primitive, error) 20 primitiveConverter func(*Primitive) *RawData 21 ) 22 23 var ( 24 dataConverters map[types.Type]dataConverter 25 primitiveConverters map[types.Type]primitiveConverter 26 ) 27 28 func init() { 29 dataConverters = map[types.Type]dataConverter{ 30 types.Unset: unset2result, 31 types.Nil: nil2result, 32 types.Bool: bool2result, 33 types.Int: int2result, 34 types.Float: float2result, 35 types.String: string2result, 36 types.Regex: regex2result, 37 types.Time: time2result, 38 types.Dict: dict2result, 39 types.Score: score2result, 40 types.Empty: empty2result, 41 types.Block: block2result, 42 types.ArrayLike: array2result, 43 types.MapLike: map2result, 44 types.ResourceLike: resource2result, 45 types.FunctionLike: function2result, 46 } 47 48 primitiveConverters = map[types.Type]primitiveConverter{ 49 types.Unset: punset2raw, 50 types.Nil: pnil2raw, 51 types.Bool: pbool2raw, 52 types.Int: pint2raw, 53 types.Float: pfloat2raw, 54 types.String: pstring2raw, 55 types.Regex: pregex2raw, 56 types.Time: ptime2raw, 57 types.Dict: pdict2raw, 58 types.Score: pscore2raw, 59 types.Empty: pempty2raw, 60 types.Block: pblock2rawV2, 61 types.ArrayLike: parray2raw, 62 types.MapLike: pmap2raw, 63 types.ResourceLike: presource2raw, 64 types.FunctionLike: pfunction2raw, 65 types.Ref: pref2raw, 66 } 67 } 68 69 func dict2primitive(value interface{}) (*Primitive, error) { 70 if value == nil { 71 return NilPrimitive, nil 72 } 73 74 switch x := value.(type) { 75 case bool: 76 return BoolPrimitive(x), nil 77 case int64: 78 return IntPrimitive(x), nil 79 case float64: 80 return FloatPrimitive(x), nil 81 case string: 82 return StringPrimitive(x), nil 83 case []interface{}: 84 res := make([]*Primitive, len(x)) 85 var err error 86 for i := range x { 87 res[i], err = dict2primitive(x[i]) 88 if err != nil { 89 return nil, err 90 } 91 } 92 return &Primitive{Type: string(types.Array(types.Dict)), Array: res}, nil 93 94 case map[string]interface{}: 95 res := make(map[string]*Primitive, len(x)) 96 var err error 97 for k, v := range x { 98 res[k], err = dict2primitive(v) 99 if err != nil { 100 return nil, err 101 } 102 } 103 return &Primitive{Type: string(types.Map(types.String, types.Dict)), Map: res}, nil 104 105 default: 106 return nil, errors.New("failed to convert dict to primitive, unsupported child type: " + reflect.TypeOf(x).String()) 107 } 108 } 109 110 func primitive2dictV2(p *Primitive) (interface{}, error) { 111 switch types.Type(p.Type).Underlying() { 112 case types.Nil: 113 return nil, nil 114 case types.Bool: 115 return bytes2bool(p.Value), nil 116 case types.Int: 117 return bytes2int(p.Value), nil 118 case types.Float: 119 return bytes2float(p.Value), nil 120 case types.String: 121 return string(p.Value), nil 122 case types.ArrayLike: 123 d, _, err := primitive2array(nil, 0, p.Array) 124 return d, err 125 case types.MapLike: 126 m, err := primitive2mapV2(p.Map) 127 return m, err 128 default: 129 hexType := make([]byte, hex.EncodedLen(len(p.Type))) 130 hex.Encode(hexType, []byte(p.Type)) 131 return nil, errors.New("unknown type to convert dict primitive back to raw data (" + string(hexType) + ")") 132 } 133 } 134 135 func unset2result(value interface{}, typ types.Type) (*Primitive, error) { 136 return UnsetPrimitive, nil 137 } 138 139 func nil2result(value interface{}, typ types.Type) (*Primitive, error) { 140 return NilPrimitive, nil 141 } 142 143 func errInvalidConversion(value interface{}, expectedType types.Type) error { 144 return fmt.Errorf("could not convert %T to %s", value, expectedType.Label()) 145 } 146 147 func bool2result(value interface{}, typ types.Type) (*Primitive, error) { 148 v, ok := value.(bool) 149 if !ok { 150 return nil, errInvalidConversion(value, typ) 151 } 152 return BoolPrimitive(v), nil 153 } 154 155 func ref2resultV2(value interface{}, typ types.Type) (*Primitive, error) { 156 v, ok := value.(uint64) 157 if !ok { 158 return nil, errInvalidConversion(value, typ) 159 } 160 return RefPrimitiveV2(v), nil 161 } 162 163 func int2result(value interface{}, typ types.Type) (*Primitive, error) { 164 if v, ok := value.(int64); ok { 165 return IntPrimitive(v), nil 166 } 167 // try to convert float64, which happens when we load this from JSON 168 if v, ok := value.(float64); ok { 169 return IntPrimitive(int64(v)), nil 170 } 171 return nil, errInvalidConversion(value, typ) 172 } 173 174 func float2result(value interface{}, typ types.Type) (*Primitive, error) { 175 v, ok := value.(float64) 176 if !ok { 177 return nil, errInvalidConversion(value, typ) 178 } 179 return FloatPrimitive(v), nil 180 } 181 182 func string2result(value interface{}, typ types.Type) (*Primitive, error) { 183 v, ok := value.(string) 184 if !ok { 185 return nil, errInvalidConversion(value, typ) 186 } 187 return StringPrimitive(v), nil 188 } 189 190 func regex2result(value interface{}, typ types.Type) (*Primitive, error) { 191 v, ok := value.(string) 192 if !ok { 193 return nil, errInvalidConversion(value, typ) 194 } 195 return RegexPrimitive(v), nil 196 } 197 198 func time2result(value interface{}, typ types.Type) (*Primitive, error) { 199 v, ok := value.(*time.Time) 200 if !ok { 201 return nil, errInvalidConversion(value, typ) 202 } 203 return TimePrimitive(v), nil 204 } 205 206 func dict2result(value interface{}, typ types.Type) (*Primitive, error) { 207 prim, err := dict2primitive(value) 208 if err != nil { 209 return nil, err 210 } 211 212 raw, err := proto.MarshalOptions{Deterministic: true}.Marshal(prim) 213 if err != nil { 214 return nil, err 215 } 216 217 return &Primitive{Type: string(types.Dict), Value: raw}, nil 218 } 219 220 func score2result(value interface{}, typ types.Type) (*Primitive, error) { 221 v, ok := value.([]byte) 222 if !ok { 223 return nil, errInvalidConversion(value, typ) 224 } 225 return &Primitive{ 226 Type: string(types.Score), 227 Value: v, 228 }, nil 229 } 230 231 func empty2result(value interface{}, typ types.Type) (*Primitive, error) { 232 return EmptyPrimitive, nil 233 } 234 235 func block2result(value interface{}, typ types.Type) (*Primitive, error) { 236 m, ok := value.(map[string]interface{}) 237 if !ok { 238 return nil, errInvalidConversion(value, typ) 239 } 240 res := make(map[string]*Primitive) 241 242 for k, v := range m { 243 raw, ok := v.(*RawData) 244 if !ok { 245 return nil, errInvalidConversion(value, typ) 246 } 247 res[k] = raw.Result().Data 248 } 249 return &Primitive{Type: string(typ), Map: res}, nil 250 } 251 252 func array2result(value interface{}, typ types.Type) (*Primitive, error) { 253 arr, ok := value.([]interface{}) 254 if !ok { 255 return nil, errInvalidConversion(value, typ) 256 } 257 res := make([]*Primitive, len(arr)) 258 ct := typ.Child() 259 var err error 260 for i := range arr { 261 res[i], err = raw2primitive(arr[i], ct) 262 if err != nil { 263 return nil, err 264 } 265 } 266 return &Primitive{Type: string(typ), Array: res}, nil 267 } 268 269 func stringmap2result(value interface{}, typ types.Type) (*Primitive, error) { 270 m, ok := value.(map[string]interface{}) 271 if !ok { 272 return nil, errInvalidConversion(value, typ) 273 } 274 res := make(map[string]*Primitive) 275 ct := typ.Child() 276 var err error 277 for k, v := range m { 278 res[k], err = raw2primitive(v, ct) 279 if err != nil { 280 return nil, err 281 } 282 } 283 return &Primitive{Type: string(typ), Map: res}, nil 284 } 285 286 func intmap2result(value interface{}, typ types.Type) (*Primitive, error) { 287 m, ok := value.(map[int32]interface{}) 288 if !ok { 289 return nil, errInvalidConversion(value, typ) 290 } 291 res := make(map[string]*Primitive) 292 ct := typ.Child() 293 var err error 294 for k, v := range m { 295 res[strconv.FormatInt(int64(k), 10)], err = raw2primitive(v, ct) 296 if err != nil { 297 return nil, err 298 } 299 } 300 return &Primitive{Type: string(typ), Map: res}, nil 301 } 302 303 func map2result(value interface{}, typ types.Type) (*Primitive, error) { 304 switch typ.Key() { 305 case types.String: 306 return stringmap2result(value, typ) 307 case types.Int: 308 return intmap2result(value, typ) 309 default: 310 return nil, errors.New("only supports turning string or int maps into primitives, not " + typ.Label()) 311 } 312 } 313 314 func resource2result(value interface{}, typ types.Type) (*Primitive, error) { 315 m, ok := value.(Resource) 316 if !ok { 317 return nil, errInvalidConversion(value, typ) 318 } 319 return &Primitive{Type: string(typ), Value: []byte(m.MqlID())}, nil 320 } 321 322 func function2result(value interface{}, typ types.Type) (*Primitive, error) { 323 v, ok := value.(uint64) 324 if ok { 325 return FunctionPrimitive(v), nil 326 } 327 return nil, errInvalidConversion(value, typ) 328 } 329 330 func raw2primitive(value interface{}, typ types.Type) (*Primitive, error) { 331 if value == nil { 332 // there are only few types whose value is allowed to be nil 333 switch typ { 334 case types.Unset: 335 return UnsetPrimitive, nil 336 default: 337 return NilPrimitive, nil 338 } 339 } 340 341 utyp := typ.Underlying() 342 c, ok := dataConverters[utyp] 343 if !ok { 344 rdata, ok := value.(*RawData) 345 if ok { 346 return raw2primitive(rdata.Value, rdata.Type) 347 } 348 return nil, errors.New("cannot serialize data type " + typ.Label()) 349 } 350 return c(value, typ) 351 } 352 353 // Result converts the raw data into a proto-compliant data structure that 354 // can be sent over the wire. It converts the interface{} value of RawData 355 // into a []byte structure that is easily serializable 356 func (r *RawData) Result() *Result { 357 errorMsg := "" 358 359 // In case we encounter an error we need to still construct the result object 360 // with the type information so it can be processed by the server 361 if r.Error != nil { 362 errorMsg = r.Error.Error() 363 364 // if the value is nil, we don't want to loose the type information, 365 // so we return it early before raw2primitive has a chance to change the 366 // type to nil 367 if r.Value == nil { 368 return &Result{ 369 Data: &Primitive{Type: string(r.Type)}, 370 Error: errorMsg, 371 } 372 } 373 } 374 375 data, err := raw2primitive(r.Value, r.Type) 376 if err != nil { 377 // If we already have an error on record, we just return that instead. 378 // This typically only happens when the above check for Value==nil cannot 379 // be determined, because it is hidden behind an interface{}. See: 380 // https://stackoverflow.com/questions/43059653/golang-interfacenil-is-nil-or-not 381 if errorMsg == "" { 382 errorMsg = err.Error() 383 } 384 return &Result{ 385 Data: &Primitive{Type: string(r.Type)}, 386 Error: errorMsg, 387 } 388 } 389 return &Result{ 390 Data: data, 391 Error: errorMsg, 392 } 393 } 394 395 func (r *RawData) CastResult(t types.Type) (*Result, error) { 396 errorMsg := "" 397 398 // In case we encounter an error we need to still construct the result object 399 // with the type information so it can be processed by the server 400 if r.Error != nil { 401 errorMsg = r.Error.Error() 402 } 403 404 // Allow any type to take on nil values 405 if r.Value == nil { 406 return &Result{ 407 Data: &Primitive{Type: string(t)}, 408 Error: errorMsg, 409 }, nil 410 } 411 412 if t == types.Bool { 413 truthy, castable := r.IsTruthy() 414 if !castable { 415 return nil, fmt.Errorf("cannot cast from %s to %s", r.Type.Label(), t.Label()) 416 } 417 return &Result{ 418 Data: BoolPrimitive(truthy), 419 Error: errorMsg, 420 }, nil 421 } 422 423 data, err := raw2primitive(r.Value, t) 424 if err != nil { 425 return nil, err 426 } 427 return &Result{ 428 Data: data, 429 Error: errorMsg, 430 }, nil 431 } 432 433 func (r *RawResult) CastResult(t types.Type) *Result { 434 res, err := r.Data.CastResult(t) 435 if err != nil { 436 return &Result{ 437 CodeId: r.CodeID, 438 Data: &Primitive{Type: string(t)}, 439 Error: err.Error(), 440 } 441 } 442 res.CodeId = r.CodeID 443 return res 444 } 445 446 // Result converts the raw result into a proto-compliant data structure that 447 // can be sent over the wire. See RawData.Result() 448 func (r *RawResult) Result() *Result { 449 res := r.Data.Result() 450 res.CodeId = r.CodeID 451 return res 452 } 453 454 func (r *Result) RawResultV2() *RawResult { 455 if r == nil { 456 return nil 457 } 458 459 res := &RawResult{ 460 Data: r.RawData(), 461 } 462 res.CodeID = r.CodeId 463 return res 464 } 465 466 func (r *Result) RawData() *RawData { 467 if r == nil { 468 return nil 469 } 470 471 data := &RawData{} 472 if r.Data != nil { 473 // The type can be empty, when we do not have data 474 if r.Data.IsNil() || types.Type(r.Data.Type).NotSet() { 475 data.Type = types.Nil 476 } else { 477 data = r.Data.RawData() 478 } 479 } 480 if len(r.Error) > 0 { 481 data.Error = errors.New(r.Error) 482 } 483 return data 484 } 485 486 func punset2raw(p *Primitive) *RawData { 487 return UnsetData 488 } 489 490 func pnil2raw(p *Primitive) *RawData { 491 return NilData 492 } 493 494 func pbool2raw(p *Primitive) *RawData { 495 if len(p.Value) == 0 { 496 return &RawData{ 497 Type: types.Type(p.Type), 498 Value: false, 499 } 500 } 501 return BoolData(bytes2bool(p.Value)) 502 } 503 504 func pint2raw(p *Primitive) *RawData { 505 if len(p.Value) == 0 { 506 return &RawData{ 507 Type: types.Type(p.Type), 508 Value: int64(0), 509 } 510 } 511 return IntData(bytes2int(p.Value)) 512 } 513 514 func pfloat2raw(p *Primitive) *RawData { 515 if len(p.Value) == 0 { 516 return &RawData{ 517 Type: types.Type(p.Type), 518 Value: float64(0), 519 } 520 } 521 return FloatData(bytes2float(p.Value)) 522 } 523 524 func pstring2raw(p *Primitive) *RawData { 525 return StringData(string(p.Value)) 526 } 527 528 func pregex2raw(p *Primitive) *RawData { 529 return RegexData(string(p.Value)) 530 } 531 532 func ptime2raw(p *Primitive) *RawData { 533 if len(p.Value) == 0 { 534 t := time.Unix(0, 0) 535 return &RawData{ 536 Type: types.Type(p.Type), 537 Value: &t, 538 } 539 } 540 return TimeData(bytes2time(p.Value)) 541 } 542 543 func pdict2raw(p *Primitive) *RawData { 544 if p.Value == nil { 545 return &RawData{ 546 Type: types.Dict, 547 Value: nil, 548 } 549 } 550 551 res := Primitive{} // unmarshal placeholder 552 err := proto.Unmarshal(p.Value, &res) 553 if err != nil { 554 return &RawData{Error: err, Type: types.Dict} 555 } 556 557 raw, err := primitive2dictV2(&res) 558 return &RawData{Error: err, Type: types.Dict, Value: raw} 559 } 560 561 func pscore2raw(p *Primitive) *RawData { 562 if len(p.Value) == 0 { 563 return &RawData{ 564 Value: int64(0), 565 Type: types.Score, 566 } 567 } 568 return &RawData{Value: p.Value, Type: types.Score} 569 } 570 571 func pempty2raw(p *Primitive) *RawData { 572 return &RawData{Type: types.Type(p.Type)} 573 } 574 575 func pblock2rawV2(p *Primitive) *RawData { 576 d, err := primitive2rawdataMapV2(p.Map) 577 return &RawData{Value: d, Error: err, Type: types.Type(p.Type)} 578 } 579 580 func parray2raw(p *Primitive) *RawData { 581 // Note: We don't hand over the compiler here. Reason is that if you have 582 // primitives that have refs in them, you should properly resolve them 583 // during the execution of the code. This function is really only applicable 584 // much later when you try to just get to the values of the returned data. 585 d, _, err := primitive2array(nil, 0, p.Array) 586 if d == nil { 587 d = []interface{}{} 588 } 589 return &RawData{Value: d, Error: err, Type: types.Type(p.Type)} 590 } 591 592 func pmap2raw(p *Primitive) *RawData { 593 d, err := primitive2mapV2(p.Map) 594 return &RawData{Value: d, Error: err, Type: types.Type(p.Type)} 595 } 596 597 func presource2raw(p *Primitive) *RawData { 598 id := string(p.Value) 599 typ := types.Type(p.Type) 600 return &RawData{Value: &MockResource{ 601 Name: typ.ResourceName(), 602 ID: id, 603 }, Type: typ} 604 } 605 606 func pfunction2raw(p *Primitive) *RawData { 607 // note: function pointers can never have a value that is nil 608 rv := bytes2int(p.Value) 609 if rv>>32 != 0 { 610 return &RawData{Value: uint64(bytes2int(p.Value)), Type: types.Type(p.Type)} 611 } else { 612 return &RawData{Value: int32(bytes2int(p.Value)), Type: types.Type(p.Type)} 613 } 614 } 615 616 func pref2raw(p *Primitive) *RawData { 617 // note: refs can never have a value that is nil 618 rv := bytes2int(p.Value) 619 if rv>>32 != 0 { 620 return &RawData{Value: uint64(bytes2int(p.Value)), Type: types.Type(p.Type)} 621 } else { 622 return &RawData{Value: int32(bytes2int(p.Value)), Type: types.Type(p.Type)} 623 } 624 } 625 626 // Tries to resolve primitives; returns refs if they don't exist yet. 627 // Returns nil and a ref != 0 if a value needs resolving. 628 func primitive2array(b *blockExecutor, ref uint64, args []*Primitive) ([]interface{}, uint64, error) { 629 if args == nil { 630 return []interface{}{}, 0, nil 631 } 632 633 res := make([]interface{}, len(args)) 634 for i := range args { 635 var cur *RawData 636 637 if b != nil && types.Type(args[i].Type) == types.Ref { 638 var rref uint64 639 var err error 640 cur, rref, err = b.resolveValue(args[i], ref) 641 if rref > 0 || err != nil { 642 return nil, rref, err 643 } 644 } else { 645 cur = args[i].RawData() 646 } 647 648 if cur != nil { 649 if cur.Error != nil { 650 return nil, 0, cur.Error 651 } 652 res[i] = cur.Value 653 } 654 } 655 return res, 0, nil 656 } 657 658 // Converts a map of primitives into a map of go data (no type info). 659 // Return map is never nil. 660 func primitive2mapV2(m map[string]*Primitive) (map[string]interface{}, error) { 661 if m == nil { 662 return map[string]interface{}{}, nil 663 } 664 665 res := make(map[string]interface{}) 666 for k, v := range m { 667 if v == nil { 668 res[k] = nil 669 continue 670 } 671 cur := v.RawData() 672 if cur.Error != nil { 673 return nil, cur.Error 674 } 675 res[k] = cur.Value 676 } 677 return res, nil 678 } 679 680 // Converts a map of primitives into a map of RawData (to preserve type-info). 681 // Return map is never nil. 682 func primitive2rawdataMapV2(m map[string]*Primitive) (map[string]interface{}, error) { 683 if m == nil { 684 return map[string]interface{}{}, nil 685 } 686 687 res := make(map[string]interface{}) 688 for k, v := range m { 689 if v == nil { 690 res[k] = nil 691 continue 692 } 693 cur := v.RawData() 694 if cur.Error != nil { 695 return nil, cur.Error 696 } 697 res[k] = cur 698 } 699 return res, nil 700 } 701 702 // RawData converts the primitive into the internal go-representation of the 703 // data that can be used for computations 704 func (p *Primitive) RawData() *RawData { 705 // FIXME: This is a stopgap. It points to an underlying problem that exists and needs fixing. 706 if p.GetType() == "" { 707 return &RawData{Error: errors.New("cannot convert primitive with NO type information")} 708 } 709 710 typ := types.Type(p.Type) 711 c, ok := primitiveConverters[typ.Underlying()] 712 if !ok { 713 return &RawData{Error: errors.New("cannot convert primitive to value for primitive type " + typ.Label())} 714 } 715 return c(p) 716 } 717 718 func (b *blockExecutor) lookupValue(ref uint64) (*RawData, uint64, error) { 719 if b == nil { 720 panic("value not computed") 721 } 722 723 res, ok := b.cache.Load(ref) 724 if !ok { 725 return b.parent.lookupValue(ref) 726 } 727 return res.Result, 0, res.Result.Error 728 } 729 730 func (b *blockExecutor) resolveRef(srcRef uint64, ref uint64) (*RawData, uint64, error) { 731 if !b.isInMyBlock(srcRef) { 732 // the value is provided by a parent 733 return b.parent.lookupValue(srcRef) 734 } else { 735 // check if the reference exists; if not connect it 736 res, ok := b.cache.Load(srcRef) 737 if !ok { 738 return b.connectRef(srcRef, ref) 739 } 740 return res.Result, 0, res.Result.Error 741 } 742 } 743 744 // returns the resolved argument if it's a ref; otherwise just the argument 745 // returns the reference if something else needs executing before it can be computed 746 // returns an error otherwise 747 func (b *blockExecutor) resolveValue(arg *Primitive, ref uint64) (*RawData, uint64, error) { 748 typ := types.Type(arg.Type) 749 switch typ.Underlying() { 750 case types.Ref: 751 srcRef := uint64(bytes2int(arg.Value)) 752 return b.resolveRef(srcRef, ref) 753 case types.ArrayLike: 754 res := make([]interface{}, len(arg.Array)) 755 for i := range arg.Array { 756 c, ref, err := b.resolveValue(arg.Array[i], ref) 757 if ref != 0 || err != nil { 758 return nil, ref, err 759 } 760 res[i] = c.Value 761 } 762 763 // type is in arg.Value 764 return &RawData{ 765 Type: typ, 766 Value: res, 767 }, 0, nil 768 default: 769 v := arg.RawData() 770 return v, 0, v.Error 771 } 772 } 773 774 func TArr2Raw[T any](arr []T) []interface{} { 775 res := make([]interface{}, len(arr)) 776 for i := range arr { 777 res[i] = arr[i] 778 } 779 return res 780 } 781 782 func TMap2Raw[T any](m map[string]T) map[string]interface{} { 783 res := make(map[string]interface{}, len(m)) 784 for k, v := range m { 785 res[k] = v 786 } 787 return res 788 } 789 790 func TRaw2T[T any](v interface{}) T { 791 if res, ok := v.(T); ok { 792 return res 793 } 794 var res T 795 return res 796 } 797 798 func TRaw2TArr[T any](v interface{}) []T { 799 arr, ok := v.([]interface{}) 800 if !ok { 801 return nil 802 } 803 804 res := make([]T, len(arr)) 805 for i := range arr { 806 res[i], _ = arr[i].(T) 807 } 808 return res 809 } 810 811 func TRaw2TMap[T any](v interface{}) map[string]T { 812 m, ok := v.(map[string]interface{}) 813 if !ok { 814 return nil 815 } 816 817 res := make(map[string]T, len(m)) 818 for k, v := range m { 819 res[k], _ = v.(T) 820 } 821 return res 822 }