go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/llx/builtin_global.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  
    10  	"go.mondoo.com/cnquery/types"
    11  )
    12  
    13  // handleGlobal takes a global function and returns a handler if found.
    14  // this is not exported as it is only used internally. it exposes everything
    15  // below this function
    16  func handleGlobalV2(op string) (handleFunctionV2, bool) {
    17  	f, ok := globalFunctionsV2[op]
    18  	if !ok {
    19  		return nil, false
    20  	}
    21  	return f, true
    22  }
    23  
    24  // DEFINITIONS
    25  
    26  type handleFunctionV2 func(*blockExecutor, *Function, uint64) (*RawData, uint64, error)
    27  
    28  var globalFunctionsV2 map[string]handleFunctionV2
    29  
    30  func init() {
    31  	globalFunctionsV2 = map[string]handleFunctionV2{
    32  		"expect":         expectV2,
    33  		"if":             ifCallV2,
    34  		"switch":         switchCallV2,
    35  		"score":          scoreCallV2,
    36  		"typeof":         typeofCallV2,
    37  		"{}":             blockV2,
    38  		"return":         returnCallV2,
    39  		"createResource": globalCreateResource,
    40  	}
    41  }
    42  
    43  func globalCreateResource(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
    44  	if l := len(f.Args); l%2 != 1 || l == 0 {
    45  		return nil, 0, errors.New("Called `createResource` with invalid number of arguments")
    46  	}
    47  
    48  	binding, ok := f.Args[0].RefV2()
    49  	if !ok {
    50  		return nil, 0, errors.New("Called `createResource` with invalid arguments: expected ref")
    51  	}
    52  
    53  	t := types.Type(f.Type)
    54  	return e.createResource(t.ResourceName(), binding, &Function{
    55  		Type: f.Type,
    56  		Args: f.Args[1:],
    57  	}, ref)
    58  }
    59  
    60  func ifCallV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
    61  	if len(f.Args) < 3 {
    62  		return nil, 0, errors.New("Called if with " + strconv.Itoa(len(f.Args)) + " arguments, expected at least 3")
    63  	}
    64  
    65  	var idx int
    66  	max := len(f.Args)
    67  	for idx+2 < max {
    68  		res, dref, err := e.resolveValue(f.Args[idx], ref)
    69  		if err != nil || dref != 0 || res == nil {
    70  			return res, dref, err
    71  		}
    72  
    73  		if truthy, _ := res.IsTruthy(); truthy {
    74  			depArgs := f.Args[idx+2]
    75  			res, dref, err = e.runBlock(nil, f.Args[idx+1], depArgs.Array, ref)
    76  			return res, dref, err
    77  		}
    78  
    79  		idx += 3
    80  	}
    81  
    82  	if idx < max {
    83  		depArgs := f.Args[idx+1]
    84  		res, dref, err := e.runBlock(nil, f.Args[idx], depArgs.Array, ref)
    85  		return res, dref, err
    86  	}
    87  
    88  	return NilData, 0, nil
    89  }
    90  
    91  func switchCallV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
    92  	// very similar to the if-call above; minor differences:
    93  	// - we have an optional reference value (which is in the function call)
    94  	// - default is translated to `true` in its condition; everything else is a function
    95  
    96  	if len(f.Args) < 2 {
    97  		return nil, 0, errors.New("Called switch with no arguments, expected at least one case statement")
    98  	}
    99  
   100  	var bind *RawData
   101  	if types.Type(f.Args[0].Type) != types.Unset {
   102  		var dref uint64
   103  		var err error
   104  		bind, dref, err = e.resolveValue(f.Args[0], ref)
   105  		if err != nil || dref != 0 || bind == nil {
   106  			return bind, dref, err
   107  		}
   108  	}
   109  
   110  	// ignore the first argument, it's just the reference value
   111  	idx := 1
   112  	max := len(f.Args)
   113  	defaultCaseIdx := -1
   114  	for idx+2 < max {
   115  		if types.Type(f.Args[idx].Type) == types.Bool {
   116  			defaultCaseIdx = idx
   117  			idx += 3
   118  			continue
   119  		}
   120  
   121  		res, dref, err := e.resolveValue(f.Args[idx], ref)
   122  		if err != nil || dref != 0 || res == nil {
   123  			return res, dref, err
   124  		}
   125  
   126  		if truthy, _ := res.IsTruthy(); truthy {
   127  			depArgs := f.Args[idx+2]
   128  			res, dref, err = e.runBlock(bind, f.Args[idx+1], depArgs.Array, ref)
   129  			return res, dref, err
   130  		}
   131  
   132  		idx += 3
   133  	}
   134  
   135  	if defaultCaseIdx != -1 {
   136  		res, dref, err := e.runBlock(nil, f.Args[defaultCaseIdx+1], f.Args[defaultCaseIdx+2].Array, ref)
   137  		return res, dref, err
   138  	}
   139  
   140  	return NilData, 0, nil
   141  }
   142  
   143  func scoreCallV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
   144  	if len(f.Args) != 1 {
   145  		return nil, 0, errors.New("Called `score` with " + strconv.Itoa(len(f.Args)) + " arguments, expected one")
   146  	}
   147  
   148  	res, dref, err := e.resolveValue(f.Args[0], ref)
   149  	if err != nil || dref != 0 || res == nil {
   150  		return res, dref, err
   151  	}
   152  
   153  	var b []byte
   154  	switch res.Type {
   155  	case types.Int:
   156  		b, err = scoreVector(int32(res.Value.(int64)))
   157  
   158  	case types.Float:
   159  		b, err = scoreVector(int32(res.Value.(float64)))
   160  
   161  	case types.String:
   162  		b, err = scoreString(res.Value.(string))
   163  	}
   164  
   165  	if err != nil {
   166  		return nil, 0, err
   167  	}
   168  
   169  	return ScoreData(b), 0, nil
   170  }
   171  
   172  func typeofCallV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
   173  	if len(f.Args) != 1 {
   174  		return nil, 0, errors.New("Called `typeof` with " + strconv.Itoa(len(f.Args)) + " arguments, expected one")
   175  	}
   176  
   177  	res, dref, err := e.resolveValue(f.Args[0], ref)
   178  	if err != nil || dref != 0 || res == nil {
   179  		return res, dref, err
   180  	}
   181  
   182  	return StringData(res.Type.Label()), 0, nil
   183  }
   184  
   185  func expectV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
   186  	if len(f.Args) != 1 {
   187  		return nil, 0, errors.New("Called expect with " + strconv.Itoa(len(f.Args)) + " arguments, expected 1")
   188  	}
   189  	res, dref, err := e.resolveValue(f.Args[0], ref)
   190  	if res != nil && res.Type != types.Bool {
   191  		return nil, 0, errors.New("Called expect body with wrong type, it should be a boolean (type mismatch)")
   192  	}
   193  	return res, dref, err
   194  }
   195  
   196  func blockV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
   197  	if len(f.Args) != 1 {
   198  		return nil, 0, errors.New("Called block with " + strconv.Itoa(len(f.Args)) + " arguments, expected 1")
   199  	}
   200  	panic("NOT YET BLOCK CALL")
   201  	// res, dref, err := c.resolveValue(f.Args[0], ref)
   202  	// if res != nil && res.Type[0] != types.Bool {
   203  	// 	return nil, 0, errors.New("Called expect body with wrong type, it should be a boolean (type mismatch)")
   204  	// }
   205  	// return res, dref, err
   206  }
   207  
   208  func returnCallV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
   209  	arg := f.Args[0]
   210  	return e.resolveValue(arg, ref)
   211  }