bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/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 "strings" 13 "time" 14 15 "bitbucket.org/Aishee/synsec/pkg/exprhelpers" 16 "bitbucket.org/Aishee/synsec/pkg/types" 17 18 "strconv" 19 20 "github.com/davecgh/go-spew/spew" 21 "github.com/mohae/deepcopy" 22 "github.com/prometheus/client_golang/prometheus" 23 log "github.com/sirupsen/logrus" 24 25 "github.com/antonmedv/expr" 26 ) 27 28 /* ok, this is kinda experimental, I don't know how bad of an idea it is .. */ 29 func SetTargetByName(target string, value string, evt *types.Event) bool { 30 31 if evt == nil { 32 return false 33 } 34 35 //it's a hack, we do it for the user 36 target = strings.TrimPrefix(target, "evt.") 37 38 log.Debugf("setting target %s to %s", target, value) 39 defer func() { 40 if r := recover(); r != nil { 41 log.Errorf("Runtime error while trying to set '%s' in %s : %+v", target, spew.Sdump(evt), r) 42 return 43 } 44 }() 45 46 iter := reflect.ValueOf(evt).Elem() 47 if (iter == reflect.Value{}) || iter.IsZero() { 48 log.Tracef("event is nill") 49 //event is nill 50 return false 51 } 52 for _, f := range strings.Split(target, ".") { 53 /* 54 ** According to current Event layout we only have to handle struct and map 55 */ 56 switch iter.Kind() { 57 case reflect.Map: 58 tmp := iter.MapIndex(reflect.ValueOf(f)) 59 /*if we're in a map and the field doesn't exist, the user wants to add it :) */ 60 if (tmp == reflect.Value{}) || tmp.IsZero() { 61 log.Debugf("map entry is zero in '%s'", target) 62 } 63 iter.SetMapIndex(reflect.ValueOf(f), reflect.ValueOf(value)) 64 return true 65 case reflect.Struct: 66 tmp := iter.FieldByName(f) 67 if !tmp.IsValid() { 68 log.Debugf("%s IsValid false", f) 69 return false 70 } 71 iter = tmp 72 break 73 default: 74 log.Errorf("unexpected type %s in '%s'", iter.Kind(), target) 75 return false 76 } 77 } 78 //now we should have the final member :) 79 if !iter.CanSet() { 80 log.Errorf("'%s' can't be set", target) 81 return false 82 } 83 if iter.Kind() != reflect.String { 84 log.Errorf("Expected string, got %v when handling '%s'", iter.Kind(), target) 85 return false 86 } 87 iter.Set(reflect.ValueOf(value)) 88 return true 89 } 90 91 func printStaticTarget(static types.ExtraField) string { 92 93 if static.Method != "" { 94 return static.Method 95 } else if static.Parsed != "" { 96 return fmt.Sprintf(".Parsed[%s]", static.Parsed) 97 } else if static.Meta != "" { 98 return fmt.Sprintf(".Meta[%s]", static.Meta) 99 } else if static.Enriched != "" { 100 return fmt.Sprintf(".Enriched[%s]", static.Enriched) 101 } else if static.TargetByName != "" { 102 return static.TargetByName 103 } else { 104 return "?" 105 } 106 } 107 108 func (n *Node) ProcessStatics(statics []types.ExtraField, event *types.Event) error { 109 //we have a few cases : 110 //(meta||key) + (static||reference||expr) 111 var value string 112 clog := n.Logger 113 114 for _, static := range statics { 115 value = "" 116 if static.Value != "" { 117 value = static.Value 118 } else if static.RunTimeValue != nil { 119 output, err := expr.Run(static.RunTimeValue, exprhelpers.GetExprEnv(map[string]interface{}{"evt": event})) 120 if err != nil { 121 clog.Warningf("failed to run RunTimeValue : %v", err) 122 continue 123 } 124 switch out := output.(type) { 125 case string: 126 value = out 127 case int: 128 value = strconv.Itoa(out) 129 default: 130 clog.Fatalf("unexpected return type for RunTimeValue : %T", output) 131 return errors.New("unexpected return type for RunTimeValue") 132 } 133 } 134 135 if value == "" { 136 clog.Debugf("Empty value for %s, skip.", printStaticTarget(static)) 137 continue 138 } 139 140 if static.Method != "" { 141 processed := false 142 /*still way too hackish, but : inject all the results in enriched, and */ 143 for _, x := range n.EnrichFunctions { 144 if fptr, ok := x.Funcs[static.Method]; ok && x.initiated { 145 clog.Tracef("Found method '%s'", static.Method) 146 ret, err := fptr(value, event, x.RuntimeCtx) 147 if err != nil { 148 clog.Fatalf("plugin function error : %v", err) 149 } 150 processed = true 151 clog.Debugf("+ Method %s('%s') returned %d entries to merge in .Enriched\n", static.Method, value, len(ret)) 152 if len(ret) == 0 { 153 clog.Debugf("+ Method '%s' empty response on '%s'", static.Method, value) 154 } 155 for k, v := range ret { 156 clog.Debugf("\t.Enriched[%s] = '%s'\n", k, v) 157 event.Enriched[k] = v 158 } 159 break 160 } else { 161 clog.Warningf("method '%s' doesn't exist or plugin not initialized", static.Method) 162 } 163 } 164 if !processed { 165 clog.Warningf("method '%s' doesn't exist", static.Method) 166 } 167 } else if static.Parsed != "" { 168 clog.Debugf(".Parsed[%s] = '%s'", static.Parsed, value) 169 event.Parsed[static.Parsed] = value 170 } else if static.Meta != "" { 171 clog.Debugf(".Meta[%s] = '%s'", static.Meta, value) 172 event.Meta[static.Meta] = value 173 } else if static.Enriched != "" { 174 clog.Debugf(".Enriched[%s] = '%s'", static.Enriched, value) 175 event.Enriched[static.Enriched] = value 176 } else if static.TargetByName != "" { 177 if !SetTargetByName(static.TargetByName, value, event) { 178 clog.Errorf("Unable to set value of '%s'", static.TargetByName) 179 } else { 180 clog.Debugf("%s = '%s'", static.TargetByName, value) 181 } 182 } else { 183 clog.Fatalf("unable to process static : unknown tartget") 184 } 185 186 } 187 return nil 188 } 189 190 var NodesHits = prometheus.NewCounterVec( 191 prometheus.CounterOpts{ 192 Name: "cs_node_hits_total", 193 Help: "Total events entered node.", 194 }, 195 []string{"source", "name"}, 196 ) 197 198 var NodesHitsOk = prometheus.NewCounterVec( 199 prometheus.CounterOpts{ 200 Name: "cs_node_hits_ok_total", 201 Help: "Total events successfuly exited node.", 202 }, 203 []string{"source", "name"}, 204 ) 205 206 var NodesHitsKo = prometheus.NewCounterVec( 207 prometheus.CounterOpts{ 208 Name: "cs_node_hits_ko_total", 209 Help: "Total events unsuccessfuly exited node.", 210 }, 211 []string{"source", "name"}, 212 ) 213 214 func stageidx(stage string, stages []string) int { 215 for i, v := range stages { 216 if stage == v { 217 return i 218 } 219 } 220 return -1 221 } 222 223 var ParseDump bool 224 var StageParseCache map[string]map[string]types.Event 225 226 func Parse(ctx UnixParserCtx, xp types.Event, nodes []Node) (types.Event, error) { 227 var event types.Event = xp 228 229 /* the stage is undefined, probably line is freshly acquired, set to first stage !*/ 230 if event.Stage == "" && len(ctx.Stages) > 0 { 231 event.Stage = ctx.Stages[0] 232 log.Tracef("no stage, set to : %s", event.Stage) 233 } 234 event.Process = false 235 if event.Time.IsZero() { 236 event.Time = time.Now() 237 } 238 239 if event.Parsed == nil { 240 event.Parsed = make(map[string]string) 241 } 242 if event.Enriched == nil { 243 event.Enriched = make(map[string]string) 244 } 245 if event.Meta == nil { 246 event.Meta = make(map[string]string) 247 } 248 if event.Type == types.LOG { 249 log.Tracef("INPUT '%s'", event.Line.Raw) 250 } 251 252 if ParseDump { 253 StageParseCache = make(map[string]map[string]types.Event) 254 } 255 256 for _, stage := range ctx.Stages { 257 if ParseDump { 258 StageParseCache[stage] = make(map[string]types.Event) 259 } 260 /* if the node is forward in stages, seek to its stage */ 261 /* this is for example used by testing system to inject logs in post-syslog-parsing phase*/ 262 if stageidx(event.Stage, ctx.Stages) > stageidx(stage, ctx.Stages) { 263 log.Tracef("skipping stage, we are already at [%s] expecting [%s]", event.Stage, stage) 264 continue 265 } 266 log.Tracef("node stage : %s, current stage : %s", event.Stage, stage) 267 268 /* if the stage is wrong, it means that the log didn't manage "pass" a stage with a onsuccess: next_stage tag */ 269 if event.Stage != stage { 270 log.Debugf("Event not parsed, expected stage '%s' got '%s', abort", stage, event.Stage) 271 return types.Event{Process: false}, nil 272 } 273 274 isStageOK := false 275 for idx, node := range nodes { 276 //Only process current stage's nodes 277 if event.Stage != node.Stage { 278 continue 279 } 280 clog := log.WithFields(log.Fields{ 281 "node-name": node.rn, 282 "stage": event.Stage, 283 }) 284 clog.Tracef("Processing node %d/%d -> %s", idx, len(nodes), node.rn) 285 if ctx.Profiling { 286 node.Profiling = true 287 } 288 ret, err := node.process(&event, ctx) 289 if err != nil { 290 clog.Fatalf("Error while processing node : %v", err) 291 } 292 clog.Tracef("node (%s) ret : %v", node.rn, ret) 293 if ret { 294 isStageOK = true 295 if ParseDump { 296 evtcopy := deepcopy.Copy(event) 297 StageParseCache[stage][node.Name] = evtcopy.(types.Event) 298 } 299 } 300 if ret && node.OnSuccess == "next_stage" { 301 clog.Debugf("node successful, stop end stage %s", stage) 302 break 303 } 304 //the parsed object moved onto the next phase 305 if event.Stage != stage { 306 clog.Tracef("node moved stage, break and redo") 307 break 308 } 309 } 310 if !isStageOK { 311 log.Debugf("Log didn't finish stage %s", event.Stage) 312 event.Process = false 313 return event, nil 314 } 315 316 } 317 318 event.Process = true 319 return event, nil 320 321 }