github.com/crowdsecurity/crowdsec@v1.6.1/pkg/parser/runtime.go (about) 1 package parser 2 3 /* 4 This file contains 5 - the runtime parsing routines 6 */ 7 8 import ( 9 "errors" 10 "fmt" 11 "reflect" 12 "strconv" 13 "strings" 14 "sync" 15 "time" 16 17 "github.com/mohae/deepcopy" 18 "github.com/prometheus/client_golang/prometheus" 19 log "github.com/sirupsen/logrus" 20 21 "github.com/crowdsecurity/crowdsec/pkg/dumps" 22 "github.com/crowdsecurity/crowdsec/pkg/exprhelpers" 23 "github.com/crowdsecurity/crowdsec/pkg/types" 24 ) 25 26 /* ok, this is kinda experimental, I don't know how bad of an idea it is .. */ 27 func SetTargetByName(target string, value string, evt *types.Event) bool { 28 if evt == nil { 29 return false 30 } 31 32 //it's a hack, we do it for the user 33 target = strings.TrimPrefix(target, "evt.") 34 35 log.Debugf("setting target %s to %s", target, value) 36 defer func() { 37 if r := recover(); r != nil { 38 log.Errorf("Runtime error while trying to set '%s': %+v", target, r) 39 return 40 } 41 }() 42 43 iter := reflect.ValueOf(evt).Elem() 44 if (iter == reflect.Value{}) || iter.IsZero() { 45 log.Tracef("event is nill") 46 //event is nill 47 return false 48 } 49 for _, f := range strings.Split(target, ".") { 50 /* 51 ** According to current Event layout we only have to handle struct and map 52 */ 53 switch iter.Kind() { 54 case reflect.Map: 55 tmp := iter.MapIndex(reflect.ValueOf(f)) 56 /*if we're in a map and the field doesn't exist, the user wants to add it :) */ 57 if (tmp == reflect.Value{}) || tmp.IsZero() { 58 log.Debugf("map entry is zero in '%s'", target) 59 } 60 iter.SetMapIndex(reflect.ValueOf(f), reflect.ValueOf(value)) 61 return true 62 case reflect.Struct: 63 tmp := iter.FieldByName(f) 64 if !tmp.IsValid() { 65 log.Debugf("'%s' is not a valid target because '%s' is not valid", target, f) 66 return false 67 } 68 if tmp.Kind() == reflect.Ptr { 69 tmp = reflect.Indirect(tmp) 70 } 71 iter = tmp 72 case reflect.Ptr: 73 tmp := iter.Elem() 74 iter = reflect.Indirect(tmp.FieldByName(f)) 75 default: 76 log.Errorf("unexpected type %s in '%s'", iter.Kind(), target) 77 return false 78 } 79 } 80 //now we should have the final member :) 81 if !iter.CanSet() { 82 log.Errorf("'%s' can't be set", target) 83 return false 84 } 85 if iter.Kind() != reflect.String { 86 log.Errorf("Expected string, got %v when handling '%s'", iter.Kind(), target) 87 return false 88 } 89 iter.Set(reflect.ValueOf(value)) 90 return true 91 } 92 93 func printStaticTarget(static ExtraField) string { 94 switch { 95 case static.Method != "": 96 return static.Method 97 case static.Parsed != "": 98 return fmt.Sprintf(".Parsed[%s]", static.Parsed) 99 case static.Meta != "": 100 return fmt.Sprintf(".Meta[%s]", static.Meta) 101 case static.Enriched != "": 102 return fmt.Sprintf(".Enriched[%s]", static.Enriched) 103 case static.TargetByName != "": 104 return static.TargetByName 105 default: 106 return "?" 107 } 108 } 109 110 func (n *Node) ProcessStatics(statics []ExtraField, event *types.Event) error { 111 //we have a few cases : 112 //(meta||key) + (static||reference||expr) 113 var value string 114 clog := n.Logger 115 116 for _, static := range statics { 117 value = "" 118 if static.Value != "" { 119 value = static.Value 120 } else if static.RunTimeValue != nil { 121 output, err := exprhelpers.Run(static.RunTimeValue, map[string]interface{}{"evt": event}, clog, n.Debug) 122 if err != nil { 123 clog.Warningf("failed to run RunTimeValue : %v", err) 124 continue 125 } 126 switch out := output.(type) { 127 case string: 128 value = out 129 case int: 130 value = strconv.Itoa(out) 131 case float64, float32: 132 value = fmt.Sprintf("%f", out) 133 case map[string]interface{}: 134 clog.Warnf("Expression '%s' returned a map, please use ToJsonString() to convert it to string if you want to keep it as is, or refine your expression to extract a string", static.ExpValue) 135 case []interface{}: 136 clog.Warnf("Expression '%s' returned an array, please use ToJsonString() to convert it to string if you want to keep it as is, or refine your expression to extract a string", static.ExpValue) 137 case nil: 138 clog.Debugf("Expression '%s' returned nil, skipping", static.ExpValue) 139 default: 140 clog.Errorf("unexpected return type for '%s' : %T", static.ExpValue, output) 141 return errors.New("unexpected return type for RunTimeValue") 142 } 143 } 144 145 if value == "" { 146 //allow ParseDate to have empty input 147 if static.Method != "ParseDate" { 148 clog.Debugf("Empty value for %s, skip.", printStaticTarget(static)) 149 continue 150 } 151 } 152 153 if static.Method != "" { 154 processed := false 155 /*still way too hackish, but : inject all the results in enriched, and */ 156 if enricherPlugin, ok := n.EnrichFunctions.Registered[static.Method]; ok { 157 clog.Tracef("Found method '%s'", static.Method) 158 ret, err := enricherPlugin.EnrichFunc(value, event, enricherPlugin.Ctx, n.Logger.WithField("method", static.Method)) 159 if err != nil { 160 clog.Errorf("method '%s' returned an error : %v", static.Method, err) 161 } 162 processed = true 163 clog.Debugf("+ Method %s('%s') returned %d entries to merge in .Enriched\n", static.Method, value, len(ret)) 164 //Hackish check, but those methods do not return any data by design 165 if len(ret) == 0 && static.Method != "UnmarshalJSON" { 166 clog.Debugf("+ Method '%s' empty response on '%s'", static.Method, value) 167 } 168 for k, v := range ret { 169 clog.Debugf("\t.Enriched[%s] = '%s'\n", k, v) 170 event.Enriched[k] = v 171 } 172 } else { 173 clog.Debugf("method '%s' doesn't exist or plugin not initialized", static.Method) 174 } 175 if !processed { 176 clog.Debugf("method '%s' doesn't exist", static.Method) 177 } 178 } else if static.Parsed != "" { 179 clog.Debugf(".Parsed[%s] = '%s'", static.Parsed, value) 180 event.Parsed[static.Parsed] = value 181 } else if static.Meta != "" { 182 clog.Debugf(".Meta[%s] = '%s'", static.Meta, value) 183 event.Meta[static.Meta] = value 184 } else if static.Enriched != "" { 185 clog.Debugf(".Enriched[%s] = '%s'", static.Enriched, value) 186 event.Enriched[static.Enriched] = value 187 } else if static.TargetByName != "" { 188 if !SetTargetByName(static.TargetByName, value, event) { 189 clog.Errorf("Unable to set value of '%s'", static.TargetByName) 190 } else { 191 clog.Debugf("%s = '%s'", static.TargetByName, value) 192 } 193 } else { 194 clog.Fatal("unable to process static : unknown target") 195 } 196 } 197 return nil 198 } 199 200 var NodesHits = prometheus.NewCounterVec( 201 prometheus.CounterOpts{ 202 Name: "cs_node_hits_total", 203 Help: "Total events entered node.", 204 }, 205 []string{"source", "type", "name"}, 206 ) 207 208 var NodesHitsOk = prometheus.NewCounterVec( 209 prometheus.CounterOpts{ 210 Name: "cs_node_hits_ok_total", 211 Help: "Total events successfully exited node.", 212 }, 213 []string{"source", "type", "name"}, 214 ) 215 216 var NodesHitsKo = prometheus.NewCounterVec( 217 prometheus.CounterOpts{ 218 Name: "cs_node_hits_ko_total", 219 Help: "Total events unsuccessfully exited node.", 220 }, 221 []string{"source", "type", "name"}, 222 ) 223 224 // 225 226 var NodesWlHitsOk = prometheus.NewCounterVec( 227 prometheus.CounterOpts{ 228 Name: "cs_node_wl_hits_ok_total", 229 Help: "Total events successfully whitelisted by node.", 230 }, 231 []string{"source", "type", "name", "reason"}, 232 ) 233 234 var NodesWlHits = prometheus.NewCounterVec( 235 prometheus.CounterOpts{ 236 Name: "cs_node_wl_hits_total", 237 Help: "Total events processed by whitelist node.", 238 }, 239 []string{"source", "type", "name", "reason"}, 240 ) 241 242 func stageidx(stage string, stages []string) int { 243 for i, v := range stages { 244 if stage == v { 245 return i 246 } 247 } 248 return -1 249 } 250 251 var ParseDump bool 252 var DumpFolder string 253 254 var StageParseCache dumps.ParserResults 255 var StageParseMutex sync.Mutex 256 257 func Parse(ctx UnixParserCtx, xp types.Event, nodes []Node) (types.Event, error) { 258 var event = xp 259 260 /* the stage is undefined, probably line is freshly acquired, set to first stage !*/ 261 if event.Stage == "" && len(ctx.Stages) > 0 { 262 event.Stage = ctx.Stages[0] 263 log.Tracef("no stage, set to : %s", event.Stage) 264 } 265 event.Process = false 266 if event.Time.IsZero() { 267 event.Time = time.Now().UTC() 268 } 269 270 if event.Parsed == nil { 271 event.Parsed = make(map[string]string) 272 } 273 if event.Enriched == nil { 274 event.Enriched = make(map[string]string) 275 } 276 if event.Meta == nil { 277 event.Meta = make(map[string]string) 278 } 279 if event.Unmarshaled == nil { 280 event.Unmarshaled = make(map[string]interface{}) 281 } 282 if event.Type == types.LOG { 283 log.Tracef("INPUT '%s'", event.Line.Raw) 284 } 285 286 if ParseDump { 287 if StageParseCache == nil { 288 StageParseMutex.Lock() 289 StageParseCache = make(dumps.ParserResults) 290 StageParseCache["success"] = make(map[string][]dumps.ParserResult) 291 StageParseCache["success"][""] = make([]dumps.ParserResult, 0) 292 StageParseMutex.Unlock() 293 } 294 } 295 296 for _, stage := range ctx.Stages { 297 if ParseDump { 298 StageParseMutex.Lock() 299 if _, ok := StageParseCache[stage]; !ok { 300 StageParseCache[stage] = make(map[string][]dumps.ParserResult) 301 } 302 StageParseMutex.Unlock() 303 } 304 /* if the node is forward in stages, seek to this stage */ 305 /* this is for example used by testing system to inject logs in post-syslog-parsing phase*/ 306 if stageidx(event.Stage, ctx.Stages) > stageidx(stage, ctx.Stages) { 307 log.Tracef("skipping stage, we are already at [%s] expecting [%s]", event.Stage, stage) 308 continue 309 } 310 log.Tracef("node stage : %s, current stage : %s", event.Stage, stage) 311 312 /* if the stage is wrong, it means that the log didn't manage "pass" a stage with a onsuccess: next_stage tag */ 313 if event.Stage != stage { 314 log.Debugf("Event not parsed, expected stage '%s' got '%s', abort", stage, event.Stage) 315 event.Process = false 316 return event, nil 317 } 318 319 isStageOK := false 320 for idx, node := range nodes { 321 //Only process current stage's nodes 322 if event.Stage != node.Stage { 323 continue 324 } 325 clog := log.WithFields(log.Fields{ 326 "node-name": node.rn, 327 "stage": event.Stage, 328 }) 329 clog.Tracef("Processing node %d/%d -> %s", idx, len(nodes), node.rn) 330 if ctx.Profiling { 331 node.Profiling = true 332 } 333 ret, err := node.process(&event, ctx, map[string]interface{}{"evt": &event}) 334 if err != nil { 335 clog.Errorf("Error while processing node : %v", err) 336 return event, err 337 } 338 clog.Tracef("node (%s) ret : %v", node.rn, ret) 339 if ParseDump { 340 var parserIdxInStage int 341 StageParseMutex.Lock() 342 if len(StageParseCache[stage][node.Name]) == 0 { 343 StageParseCache[stage][node.Name] = make([]dumps.ParserResult, 0) 344 parserIdxInStage = len(StageParseCache[stage]) 345 } else { 346 parserIdxInStage = StageParseCache[stage][node.Name][0].Idx 347 } 348 StageParseMutex.Unlock() 349 350 evtcopy := deepcopy.Copy(event) 351 parserInfo := dumps.ParserResult{Evt: evtcopy.(types.Event), Success: ret, Idx: parserIdxInStage} 352 StageParseMutex.Lock() 353 StageParseCache[stage][node.Name] = append(StageParseCache[stage][node.Name], parserInfo) 354 StageParseMutex.Unlock() 355 } 356 if ret { 357 isStageOK = true 358 } 359 if ret && node.OnSuccess == "next_stage" { 360 clog.Debugf("node successful, stop end stage %s", stage) 361 break 362 } 363 //the parsed object moved onto the next phase 364 if event.Stage != stage { 365 clog.Tracef("node moved stage, break and redo") 366 break 367 } 368 } 369 if !isStageOK { 370 log.Debugf("Log didn't finish stage %s", event.Stage) 371 event.Process = false 372 return event, nil 373 } 374 } 375 376 event.Process = true 377 return event, nil 378 }