Path: blob/dev/pkg/protocols/common/expressions/expressions.go
2072 views
package expressions12import (3"strings"45"github.com/Knetic/govaluate"67"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"8"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/marker"9"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"10stringsutil "github.com/projectdiscovery/utils/strings"11)1213// Eval compiles the given expression and evaluate it with the given values preserving the return type14func Eval(expression string, values map[string]interface{}) (interface{}, error) {15compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions)16if err != nil {17return nil, err18}19return compiled.Evaluate(values)20}2122// Evaluate checks if the match contains a dynamic variable, for each23// found one we will check if it's an expression and can24// be compiled, it will be evaluated and the results will be returned.25//26// The provided keys from finalValues will be used as variable names27// for substitution inside the expression.28func Evaluate(data string, base map[string]interface{}) (string, error) {29return evaluate(data, base)30}3132// EvaluateByte checks if the match contains a dynamic variable, for each33// found one we will check if it's an expression and can34// be compiled, it will be evaluated and the results will be returned.35//36// The provided keys from finalValues will be used as variable names37// for substitution inside the expression.38func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) {39finalData, err := evaluate(string(data), base)40return []byte(finalData), err41}4243func evaluate(data string, base map[string]interface{}) (string, error) {44// replace simple placeholders (key => value) MarkerOpen + key + MarkerClose and General + key + General to value45data = replacer.Replace(data, base)4647// expressions can be:48// - simple: containing base values keys (variables)49// - complex: containing helper functions [ + variables]50// literals like {{2+2}} are not considered expressions51expressions := FindExpressions(data, marker.ParenthesisOpen, marker.ParenthesisClose, base)52for _, expression := range expressions {53// replace variable placeholders with base values54expression = replacer.Replace(expression, base)55// turns expressions (either helper functions+base values or base values)56compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions)57if err != nil {58continue59}60result, err := compiled.Evaluate(base)61if err != nil {62continue63}64// replace incrementally65data = replacer.ReplaceOne(data, expression, result)66}67return data, nil68}6970// maxIterations to avoid infinite loop71const maxIterations = 2507273func FindExpressions(data, OpenMarker, CloseMarker string, base map[string]interface{}) []string {74var (75iterations int76exps []string77)78for iterations <= maxIterations {79// check if we reached the maximum number of iterations8081iterations++82// attempt to find open markers83indexOpenMarker := strings.Index(data, OpenMarker)84// exits if not found85if indexOpenMarker < 0 {86break87}8889indexOpenMarkerOffset := indexOpenMarker + len(OpenMarker)9091shouldSearchCloseMarker := true92closeMarkerFound := false93innerData := data94var potentialMatch string95var indexCloseMarker, indexCloseMarkerOffset int96skip := indexOpenMarkerOffset97for shouldSearchCloseMarker {98// attempt to find close marker99indexCloseMarker = stringsutil.IndexAt(innerData, CloseMarker, skip)100// if no close markers are found exit101if indexCloseMarker < 0 {102shouldSearchCloseMarker = false103continue104}105indexCloseMarkerOffset = indexCloseMarker + len(CloseMarker)106107potentialMatch = innerData[indexOpenMarkerOffset:indexCloseMarker]108if isExpression(potentialMatch, base) {109closeMarkerFound = true110shouldSearchCloseMarker = false111exps = append(exps, potentialMatch)112} else {113skip = indexCloseMarkerOffset114}115}116117if closeMarkerFound {118// move after the close marker119data = data[indexCloseMarkerOffset:]120} else {121// move after the open marker122data = data[indexOpenMarkerOffset:]123}124}125return exps126}127128func isExpression(data string, base map[string]interface{}) bool {129if _, err := govaluate.NewEvaluableExpression(data); err == nil {130if stringsutil.ContainsAny(data, getFunctionsNames(base)...) {131return true132} else if stringsutil.ContainsAny(data, dsl.FunctionNames...) {133return true134}135return false136}137_, err := govaluate.NewEvaluableExpressionWithFunctions(data, dsl.HelperFunctions)138return err == nil139}140141func getFunctionsNames(m map[string]interface{}) []string {142keys := make([]string, 0, len(m))143for k := range m {144keys = append(keys, k)145}146return keys147}148149150