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  }