Path: blob/dev/pkg/protocols/common/expressions/expressions.go
2851 views
package expressions12import (3"strings"45"github.com/Knetic/govaluate"6"github.com/projectdiscovery/gologger"78"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"9"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/marker"10"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"11stringsutil "github.com/projectdiscovery/utils/strings"12)1314// Eval compiles the given expression and evaluate it with the given values preserving the return type15func Eval(expression string, values map[string]interface{}) (interface{}, error) {16compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions)17if err != nil {18return nil, err19}20return compiled.Evaluate(values)21}2223// Evaluate checks if the match contains a dynamic variable, for each24// found one we will check if it's an expression and can25// be compiled, it will be evaluated and the results will be returned.26//27// The provided keys from finalValues will be used as variable names28// for substitution inside the expression.29func Evaluate(data string, base map[string]interface{}) (string, error) {30return evaluate(data, base)31}3233// EvaluateByte checks if the match contains a dynamic variable, for each34// found one we will check if it's an expression and can35// be compiled, it will be evaluated and the results will be returned.36//37// The provided keys from finalValues will be used as variable names38// for substitution inside the expression.39func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) {40finalData, err := evaluate(string(data), base)41return []byte(finalData), err42}4344func evaluate(data string, base map[string]interface{}) (string, error) {45// replace simple placeholders (key => value) MarkerOpen + key + MarkerClose and General + key + General to value46data = replacer.Replace(data, base)4748// expressions can be:49// - simple: containing base values keys (variables)50// - complex: containing helper functions [ + variables]51// literals like {{2+2}} are not considered expressions52expressions := FindExpressions(data, marker.ParenthesisOpen, marker.ParenthesisClose, base)53for _, expression := range expressions {54// replace variable placeholders with base values55expression = replacer.Replace(expression, base)56// turns expressions (either helper functions+base values or base values)57compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions)58if err != nil {59gologger.Warning().Msgf("Failed to compile expression '%s': %v", expression, err)60continue61}62result, err := compiled.Evaluate(base)63if err != nil {64gologger.Warning().Msgf("Failed to evaluate expression '%s': %v", expression, err)65continue66}67// replace incrementally68data = replacer.ReplaceOne(data, expression, result)69}70return data, nil71}7273// maxIterations to avoid infinite loop74const maxIterations = 2507576func FindExpressions(data, OpenMarker, CloseMarker string, base map[string]interface{}) []string {77var (78iterations int79exps []string80)81for iterations <= maxIterations {82// check if we reached the maximum number of iterations8384iterations++85// attempt to find open markers86indexOpenMarker := strings.Index(data, OpenMarker)87// exits if not found88if indexOpenMarker < 0 {89break90}9192indexOpenMarkerOffset := indexOpenMarker + len(OpenMarker)9394shouldSearchCloseMarker := true95closeMarkerFound := false96innerData := data97var potentialMatch string98var indexCloseMarker, indexCloseMarkerOffset int99skip := indexOpenMarkerOffset100for shouldSearchCloseMarker {101// attempt to find close marker102indexCloseMarker = stringsutil.IndexAt(innerData, CloseMarker, skip)103// if no close markers are found exit104if indexCloseMarker < 0 {105shouldSearchCloseMarker = false106continue107}108indexCloseMarkerOffset = indexCloseMarker + len(CloseMarker)109110potentialMatch = innerData[indexOpenMarkerOffset:indexCloseMarker]111if isExpression(potentialMatch, base) {112closeMarkerFound = true113shouldSearchCloseMarker = false114exps = append(exps, potentialMatch)115} else {116skip = indexCloseMarkerOffset117}118}119120if closeMarkerFound {121// move after the close marker122data = data[indexCloseMarkerOffset:]123} else {124// move after the open marker125data = data[indexOpenMarkerOffset:]126}127}128return exps129}130131func isExpression(data string, base map[string]interface{}) bool {132if _, err := govaluate.NewEvaluableExpression(data); err == nil {133if stringsutil.ContainsAny(data, getFunctionsNames(base)...) {134return true135} else if stringsutil.ContainsAny(data, dsl.FunctionNames...) {136return true137}138return false139}140_, err := govaluate.NewEvaluableExpressionWithFunctions(data, dsl.HelperFunctions)141return err == nil142}143144func getFunctionsNames(m map[string]interface{}) []string {145keys := make([]string, 0, len(m))146for k := range m {147keys = append(keys, k)148}149return keys150}151152153