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 }