bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/exprhelpers/visitor.go (about)

     1  package exprhelpers
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/antonmedv/expr/parser"
     9  	"github.com/sirupsen/logrus"
    10  	log "github.com/sirupsen/logrus"
    11  
    12  	"github.com/antonmedv/expr"
    13  	"github.com/antonmedv/expr/ast"
    14  	"github.com/antonmedv/expr/vm"
    15  )
    16  
    17  /*
    18  Visitor is used to reconstruct variables with its property called in an expr filter
    19  Thus, we can debug expr filter by displaying all variables contents present in the filter
    20  */
    21  type visitor struct {
    22  	newVar     bool
    23  	currentID  string
    24  	properties []string
    25  	vars       []string
    26  }
    27  
    28  /*
    29  Enter should be present for the interface but is never used
    30  */
    31  func (v *visitor) Enter(node *ast.Node) {}
    32  
    33  /*
    34  Exit is called when running ast.Walk(node, visitor), each time a node exit.
    35  So we have the node information and we can get the identifier (first level of the struct)
    36  and its properties to reconstruct the complete variable.
    37  */
    38  func (v *visitor) Exit(node *ast.Node) {
    39  	if n, ok := (*node).(*ast.IdentifierNode); ok {
    40  		if !v.newVar {
    41  			v.newVar = true
    42  			v.currentID = n.Value
    43  		} else {
    44  			fullVar := fmt.Sprintf("%s.%s", v.currentID, strings.Join(v.properties, "."))
    45  			v.vars = append(v.vars, fullVar)
    46  			v.properties = []string{}
    47  			v.currentID = n.Value
    48  		}
    49  	} else if n, ok := (*node).(*ast.PropertyNode); ok {
    50  		v.properties = append(v.properties, n.Property)
    51  	}
    52  }
    53  
    54  /*
    55  Build reconstruct all the variables used in a filter (to display their content later).
    56  */
    57  func (v *visitor) Build(filter string, exprEnv expr.Option) (*ExprDebugger, error) {
    58  	var expressions []*expression
    59  	ret := &ExprDebugger{
    60  		filter: filter,
    61  	}
    62  	if filter == "" {
    63  		log.Debugf("unable to create expr debugger with empty filter")
    64  		return &ExprDebugger{}, nil
    65  	}
    66  	v.newVar = false
    67  	tree, err := parser.Parse(filter)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	ast.Walk(&tree.Node, v)
    72  	if v.currentID != "" && len(v.properties) > 0 { // if its a variable with property (eg. evt.Line.Labels)
    73  		fullVar := fmt.Sprintf("%s.%s", v.currentID, strings.Join(v.properties, "."))
    74  		v.vars = append(v.vars, fullVar)
    75  	} else if v.currentID != "" && len(v.properties) == 0 { // if it's a variable without property
    76  		fullVar := v.currentID
    77  		v.vars = append(v.vars, fullVar)
    78  	} else {
    79  		log.Debugf("no variable in filter : '%s'", filter)
    80  	}
    81  	v.properties = []string{}
    82  	v.currentID = ""
    83  	for _, variable := range v.vars {
    84  		debugFilter, err := expr.Compile(variable, exprEnv)
    85  		if err != nil {
    86  			return ret, fmt.Errorf("compilation of variable '%s' failed: %v", variable, err)
    87  		}
    88  		tmpExpression := &expression{
    89  			variable,
    90  			debugFilter,
    91  		}
    92  		expressions = append(expressions, tmpExpression)
    93  
    94  	}
    95  	ret.expression = expressions
    96  	return ret, nil
    97  }
    98  
    99  // ExprDebugger contains the list of expression to be run when debugging an expression filter
   100  type ExprDebugger struct {
   101  	filter     string
   102  	expression []*expression
   103  }
   104  
   105  // expression is the structure that represents the variable in string and compiled format
   106  type expression struct {
   107  	Str      string
   108  	Compiled *vm.Program
   109  }
   110  
   111  /*
   112  Run display the content of each variable of a filter by evaluating them with expr,
   113  again the expr environment given in parameter
   114  */
   115  func (e *ExprDebugger) Run(logger *logrus.Entry, filterResult bool, exprEnv map[string]interface{}) {
   116  	if len(e.expression) == 0 {
   117  		logger.Tracef("no variable to eval for filter '%s'", e.filter)
   118  		return
   119  	}
   120  	logger.Debugf("eval(%s) = %s", e.filter, strings.ToUpper(strconv.FormatBool(filterResult)))
   121  	logger.Debugf("eval variables:")
   122  	for _, expression := range e.expression {
   123  		debug, err := expr.Run(expression.Compiled, exprEnv)
   124  		if err != nil {
   125  			logger.Errorf("unable to print debug expression for '%s': %s", expression.Str, err)
   126  		}
   127  		logger.Debugf("       %s = '%s'", expression.Str, debug)
   128  	}
   129  }
   130  
   131  // NewDebugger is the exported function that build the debuggers expressions
   132  func NewDebugger(filter string, exprEnv expr.Option) (*ExprDebugger, error) {
   133  	visitor := &visitor{}
   134  	exprDebugger, err := visitor.Build(filter, exprEnv)
   135  	return exprDebugger, err
   136  }