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

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package llx
     5  
     6  import (
     7  	"errors"
     8  	"strconv"
     9  	"time"
    10  
    11  	"go.mondoo.com/cnquery/types"
    12  )
    13  
    14  type Resource interface {
    15  	// Name of the resource
    16  	MqlName() string
    17  	// ID of this resource
    18  	MqlID() string
    19  }
    20  
    21  type MockResource struct {
    22  	Name string
    23  	ID   string
    24  }
    25  
    26  func (m *MockResource) MqlName() string {
    27  	return m.Name
    28  }
    29  
    30  func (m *MockResource) MqlID() string {
    31  	return m.ID
    32  }
    33  
    34  // resourceFunctions are all the shared handlers for resource calls
    35  var resourceFunctionsV2 map[string]chunkHandlerV2
    36  
    37  func _resourceWhereV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64, invert bool) (*RawData, uint64, error) {
    38  	// where(resource.list, function)
    39  	itemsRef := chunk.Function.Args[0]
    40  	items, rref, err := e.resolveValue(itemsRef, ref)
    41  	if err != nil || rref > 0 {
    42  		return nil, rref, err
    43  	}
    44  	list := items.Value.([]interface{})
    45  	if len(list) == 0 {
    46  		return bind, 0, nil
    47  	}
    48  
    49  	resource := bind.Value.(Resource)
    50  
    51  	arg1 := chunk.Function.Args[1]
    52  	blockRef, ok := arg1.RefV2()
    53  	if !ok {
    54  		return nil, 0, errors.New("Failed to retrieve function reference of 'where' call")
    55  	}
    56  
    57  	dref, err := e.ensureArgsResolved(chunk.Function.Args[2:], ref)
    58  	if dref != 0 || err != nil {
    59  		return nil, dref, err
    60  	}
    61  
    62  	blockId := e.ctx.code.Id + strconv.FormatUint(blockRef>>32, 10)
    63  
    64  	ct := items.Type.Child()
    65  
    66  	argsList := make([][]*RawData, len(list))
    67  	for i := range list {
    68  		argsList[i] = []*RawData{
    69  			{
    70  				Type:  ct,
    71  				Value: list[i],
    72  			},
    73  		}
    74  	}
    75  
    76  	err = e.runFunctionBlocks(argsList, blockRef, func(results []arrayBlockCallResult, errs []error) {
    77  		resList := []*Primitive{}
    78  		for i, res := range results {
    79  			isTruthy := res.isTruthy()
    80  			if isTruthy == !invert {
    81  				prim := (&RawData{Value: list[i], Type: ct}).Result().Data
    82  				resList = append(resList, prim)
    83  			}
    84  		}
    85  
    86  		// get all mandatory args
    87  		resourceInfo := e.ctx.runtime.Schema().Lookup(resource.MqlName())
    88  		copyFields := []string{}
    89  		for k, v := range resourceInfo.Fields {
    90  			if k != "list" && v.IsMandatory {
    91  				copyFields = append(copyFields, k)
    92  			}
    93  		}
    94  		args := map[string]*Primitive{
    95  			"list": ArrayPrimitive(resList, ct),
    96  		}
    97  
    98  		resResource, err := e.ctx.runtime.CloneResource(resource, blockId, copyFields, args)
    99  
   100  		var data *RawData
   101  		if err != nil {
   102  			data = &RawData{
   103  				Error: errors.New("Failed to create filter result resource: " + err.Error()),
   104  			}
   105  			e.cache.Store(ref, &stepCache{
   106  				Result: data,
   107  			})
   108  		} else {
   109  			data = &RawData{
   110  				Type:  bind.Type,
   111  				Value: resResource,
   112  			}
   113  			e.cache.Store(ref, &stepCache{
   114  				Result:   data,
   115  				IsStatic: false,
   116  			})
   117  		}
   118  
   119  		e.triggerChain(ref, data)
   120  	})
   121  
   122  	if err != nil {
   123  		return nil, 0, err
   124  	}
   125  
   126  	return nil, 0, nil
   127  }
   128  
   129  func resourceWhereV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) {
   130  	return _resourceWhereV2(e, bind, chunk, ref, false)
   131  }
   132  
   133  func resourceWhereNotV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) {
   134  	return _resourceWhereV2(e, bind, chunk, ref, true)
   135  }
   136  
   137  func resourceMapV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) {
   138  	// map(resource.list, function)
   139  	itemsRef := chunk.Function.Args[0]
   140  	items, rref, err := e.resolveValue(itemsRef, ref)
   141  	if err != nil || rref > 0 {
   142  		return nil, rref, err
   143  	}
   144  	list := items.Value.([]interface{})
   145  	if len(list) == 0 {
   146  		return bind, 0, nil
   147  	}
   148  
   149  	arg1 := chunk.Function.Args[1]
   150  	fref, ok := arg1.RefV2()
   151  	if !ok {
   152  		return nil, 0, errors.New("Failed to retrieve function reference of 'map' call")
   153  	}
   154  
   155  	dref, err := e.ensureArgsResolved(chunk.Function.Args[2:], ref)
   156  	if dref != 0 || err != nil {
   157  		return nil, dref, err
   158  	}
   159  
   160  	ct := items.Type.Child()
   161  
   162  	argsList := make([][]*RawData, len(list))
   163  	for i := range list {
   164  		argsList[i] = []*RawData{
   165  			{
   166  				Type:  ct,
   167  				Value: list[i],
   168  			},
   169  		}
   170  	}
   171  
   172  	err = e.runFunctionBlocks(argsList, fref, func(results []arrayBlockCallResult, errs []error) {
   173  		mappedType := types.Unset
   174  		resList := []interface{}{}
   175  		f := e.ctx.code.Block(fref)
   176  		epChecksum := e.ctx.code.Checksums[f.Entrypoints[0]]
   177  
   178  		for _, res := range results {
   179  			if epValIface, ok := res.entrypoints[epChecksum]; ok {
   180  				epVal := epValIface.(*RawData)
   181  				mappedType = epVal.Type
   182  				resList = append(resList, epVal.Value)
   183  			}
   184  		}
   185  
   186  		data := &RawData{
   187  			Type:  types.Array(mappedType),
   188  			Value: resList,
   189  		}
   190  
   191  		e.cache.Store(ref, &stepCache{
   192  			Result: data,
   193  		})
   194  
   195  		e.triggerChain(ref, data)
   196  	})
   197  
   198  	if err != nil {
   199  		return nil, 0, err
   200  	}
   201  
   202  	return nil, 0, nil
   203  }
   204  
   205  func resourceLengthV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) {
   206  	// length(resource.list)
   207  	itemsRef := chunk.Function.Args[0]
   208  	items, rref, err := e.resolveValue(itemsRef, ref)
   209  	if err != nil || rref > 0 {
   210  		return nil, rref, err
   211  	}
   212  
   213  	list := items.Value.([]interface{})
   214  	return IntData(int64(len(list))), 0, nil
   215  }
   216  
   217  var timeFormats = map[string]string{
   218  	"ansic":    time.ANSIC,
   219  	"rfc822":   time.RFC822,
   220  	"rfc822z":  time.RFC822Z,
   221  	"rfc850":   time.RFC850,
   222  	"rfc1123":  time.RFC1123,
   223  	"rfc1123z": time.RFC1123Z,
   224  	"rfc3339":  time.RFC3339,
   225  	"kitchen":  time.Kitchen,
   226  	"stamp":    time.Stamp,
   227  	"datetime": time.DateTime,
   228  	"date":     time.DateOnly,
   229  	"time":     time.TimeOnly,
   230  }
   231  
   232  // Note: the list of recognized timeFormats is mutually exclusive.
   233  // This means that for any given timestamp for one format it won't
   234  // parse with any of the other formats. Should this ever change,
   235  // the order in which formats are parsed will play a more important role.
   236  var defaultTimeFormatsOrder = []string{
   237  	time.RFC3339,
   238  	time.DateTime,
   239  	time.DateOnly,
   240  	time.TimeOnly,
   241  	time.RFC1123,
   242  	time.RFC1123Z,
   243  	time.ANSIC,
   244  	time.RFC822,
   245  	time.RFC822Z,
   246  	time.RFC850,
   247  	time.Kitchen,
   248  	time.Stamp,
   249  }
   250  
   251  func resourceDateV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) {
   252  	args, rref, err := primitive2array(e, ref, chunk.Function.Args)
   253  	if err != nil || rref != 0 {
   254  		return nil, rref, err
   255  	}
   256  
   257  	var format string
   258  	if len(args) >= 2 {
   259  		format = args[1].(string)
   260  		if f, ok := timeFormats[format]; ok {
   261  			format = f
   262  		}
   263  	}
   264  
   265  	if format != "" {
   266  		parsed, err := time.Parse(format, args[0].(string))
   267  		if err != nil {
   268  			return nil, 0, errors.New("failed to parse time: " + err.Error())
   269  		}
   270  		return TimeData(parsed), 0, nil
   271  	}
   272  
   273  	// Note: Yes, this approach is much slower than giving us a hint
   274  	// about which time format is used.
   275  	for _, format := range defaultTimeFormatsOrder {
   276  		parsed, err := time.Parse(format, args[0].(string))
   277  		if err != nil {
   278  			continue
   279  		}
   280  		return TimeData(parsed), 0, nil
   281  	}
   282  
   283  	return nil, 0, errors.New("failed to parse time")
   284  }