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 }