github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/common/expressions/expressions.go (about) 1 package expressions 2 3 import ( 4 "strings" 5 6 "github.com/Knetic/govaluate" 7 8 "github.com/projectdiscovery/nuclei/v2/pkg/operators/common/dsl" 9 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/marker" 10 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer" 11 stringsutil "github.com/projectdiscovery/utils/strings" 12 ) 13 14 // Eval compiles the given expression and evaluate it with the given values preserving the return type 15 func Eval(expression string, values map[string]interface{}) (interface{}, error) { 16 compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions) 17 if err != nil { 18 return nil, err 19 } 20 return compiled.Evaluate(values) 21 } 22 23 // Evaluate checks if the match contains a dynamic variable, for each 24 // found one we will check if it's an expression and can 25 // be compiled, it will be evaluated and the results will be returned. 26 // 27 // The provided keys from finalValues will be used as variable names 28 // for substitution inside the expression. 29 func Evaluate(data string, base map[string]interface{}) (string, error) { 30 return evaluate(data, base) 31 } 32 33 // EvaluateByte checks if the match contains a dynamic variable, for each 34 // found one we will check if it's an expression and can 35 // be compiled, it will be evaluated and the results will be returned. 36 // 37 // The provided keys from finalValues will be used as variable names 38 // for substitution inside the expression. 39 func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) { 40 finalData, err := evaluate(string(data), base) 41 return []byte(finalData), err 42 } 43 44 func evaluate(data string, base map[string]interface{}) (string, error) { 45 // replace simple placeholders (key => value) MarkerOpen + key + MarkerClose and General + key + General to value 46 data = replacer.Replace(data, base) 47 48 // expressions can be: 49 // - simple: containing base values keys (variables) 50 // - complex: containing helper functions [ + variables] 51 // literals like {{2+2}} are not considered expressions 52 expressions := FindExpressions(data, marker.ParenthesisOpen, marker.ParenthesisClose, base) 53 for _, expression := range expressions { 54 // replace variable placeholders with base values 55 expression = replacer.Replace(expression, base) 56 // turns expressions (either helper functions+base values or base values) 57 compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions) 58 if err != nil { 59 continue 60 } 61 result, err := compiled.Evaluate(base) 62 if err != nil { 63 continue 64 } 65 // replace incrementally 66 data = replacer.ReplaceOne(data, expression, result) 67 } 68 return data, nil 69 } 70 71 // maxIterations to avoid infinite loop 72 const maxIterations = 250 73 74 func FindExpressions(data, OpenMarker, CloseMarker string, base map[string]interface{}) []string { 75 var ( 76 iterations int 77 exps []string 78 ) 79 for { 80 // check if we reached the maximum number of iterations 81 if iterations > maxIterations { 82 break 83 } 84 iterations++ 85 // attempt to find open markers 86 indexOpenMarker := strings.Index(data, OpenMarker) 87 // exits if not found 88 if indexOpenMarker < 0 { 89 break 90 } 91 92 indexOpenMarkerOffset := indexOpenMarker + len(OpenMarker) 93 94 shouldSearchCloseMarker := true 95 closeMarkerFound := false 96 innerData := data 97 var potentialMatch string 98 var indexCloseMarker, indexCloseMarkerOffset int 99 skip := indexOpenMarkerOffset 100 for shouldSearchCloseMarker { 101 // attempt to find close marker 102 indexCloseMarker = stringsutil.IndexAt(innerData, CloseMarker, skip) 103 // if no close markers are found exit 104 if indexCloseMarker < 0 { 105 shouldSearchCloseMarker = false 106 continue 107 } 108 indexCloseMarkerOffset = indexCloseMarker + len(CloseMarker) 109 110 potentialMatch = innerData[indexOpenMarkerOffset:indexCloseMarker] 111 if isExpression(potentialMatch, base) { 112 closeMarkerFound = true 113 shouldSearchCloseMarker = false 114 exps = append(exps, potentialMatch) 115 } else { 116 skip = indexCloseMarkerOffset 117 } 118 } 119 120 if closeMarkerFound { 121 // move after the close marker 122 data = data[indexCloseMarkerOffset:] 123 } else { 124 // move after the open marker 125 data = data[indexOpenMarkerOffset:] 126 } 127 } 128 return exps 129 } 130 131 func isExpression(data string, base map[string]interface{}) bool { 132 if _, err := govaluate.NewEvaluableExpression(data); err == nil { 133 if stringsutil.ContainsAny(data, getFunctionsNames(base)...) { 134 return true 135 } else if stringsutil.ContainsAny(data, dsl.FunctionNames...) { 136 return true 137 } 138 return false 139 } 140 _, err := govaluate.NewEvaluableExpressionWithFunctions(data, dsl.HelperFunctions) 141 return err == nil 142 } 143 144 func getFunctionsNames(m map[string]interface{}) []string { 145 keys := make([]string, 0, len(m)) 146 for k := range m { 147 keys = append(keys, k) 148 } 149 return keys 150 }