2021-02-24 12:07:16 +05:30
|
|
|
package expressions
|
|
|
|
|
|
|
|
|
|
import (
|
2022-01-09 12:52:04 +01:00
|
|
|
"strings"
|
2021-02-24 12:07:16 +05:30
|
|
|
|
|
|
|
|
"github.com/Knetic/govaluate"
|
2021-11-25 15:39:10 +02:00
|
|
|
|
2021-02-24 12:07:16 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/operators/common/dsl"
|
2022-01-09 12:52:04 +01:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/marker"
|
2021-02-24 12:07:16 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer"
|
2022-11-06 21:24:23 +01:00
|
|
|
stringsutil "github.com/projectdiscovery/utils/strings"
|
2021-02-24 12:07:16 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Evaluate checks if the match contains a dynamic variable, for each
|
|
|
|
|
// found one we will check if it's an expression and can
|
|
|
|
|
// be compiled, it will be evaluated and the results will be returned.
|
|
|
|
|
//
|
|
|
|
|
// The provided keys from finalValues will be used as variable names
|
|
|
|
|
// for substitution inside the expression.
|
|
|
|
|
func Evaluate(data string, base map[string]interface{}) (string, error) {
|
2021-10-09 19:46:23 +05:30
|
|
|
return evaluate(data, base)
|
2021-02-24 12:07:16 +05:30
|
|
|
}
|
2021-07-06 18:27:30 +05:30
|
|
|
|
|
|
|
|
// EvaluateByte checks if the match contains a dynamic variable, for each
|
|
|
|
|
// found one we will check if it's an expression and can
|
|
|
|
|
// be compiled, it will be evaluated and the results will be returned.
|
|
|
|
|
//
|
|
|
|
|
// The provided keys from finalValues will be used as variable names
|
|
|
|
|
// for substitution inside the expression.
|
|
|
|
|
func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) {
|
2021-10-09 19:46:23 +05:30
|
|
|
finalData, err := evaluate(string(data), base)
|
|
|
|
|
return []byte(finalData), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func evaluate(data string, base map[string]interface{}) (string, error) {
|
2022-01-19 14:57:25 +01:00
|
|
|
// replace simple placeholders (key => value) MarkerOpen + key + MarkerClose and General + key + General to value
|
|
|
|
|
data = replacer.Replace(data, base)
|
|
|
|
|
|
2022-01-19 14:10:11 +01:00
|
|
|
// expressions can be:
|
|
|
|
|
// - simple: containing base values keys (variables)
|
|
|
|
|
// - complex: containing helper functions [ + variables]
|
|
|
|
|
// literals like {{2+2}} are not considered expressions
|
2022-08-23 13:16:41 +05:30
|
|
|
expressions := findExpressions(data, marker.ParenthesisOpen, marker.ParenthesisClose, base)
|
2022-01-19 14:10:11 +01:00
|
|
|
for _, expression := range expressions {
|
|
|
|
|
// replace variable placeholders with base values
|
|
|
|
|
expression = replacer.Replace(expression, base)
|
|
|
|
|
// turns expressions (either helper functions+base values or base values)
|
2022-08-23 13:16:41 +05:30
|
|
|
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions)
|
2021-07-06 18:27:30 +05:30
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
result, err := compiled.Evaluate(base)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-02-01 14:12:16 +01:00
|
|
|
// replace incrementally
|
|
|
|
|
data = replacer.ReplaceOne(data, expression, result)
|
2021-07-06 18:27:30 +05:30
|
|
|
}
|
2022-02-01 14:12:16 +01:00
|
|
|
return data, nil
|
2021-07-06 18:27:30 +05:30
|
|
|
}
|
2022-01-09 12:52:04 +01:00
|
|
|
|
2022-01-19 14:10:11 +01:00
|
|
|
// maxIterations to avoid infinite loop
|
|
|
|
|
const maxIterations = 250
|
|
|
|
|
|
2022-08-23 13:16:41 +05:30
|
|
|
func findExpressions(data, OpenMarker, CloseMarker string, base map[string]interface{}) []string {
|
2022-01-19 14:10:11 +01:00
|
|
|
var (
|
|
|
|
|
iterations int
|
|
|
|
|
exps []string
|
|
|
|
|
)
|
|
|
|
|
for {
|
|
|
|
|
// check if we reached the maximum number of iterations
|
|
|
|
|
if iterations > maxIterations {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
iterations++
|
|
|
|
|
// attempt to find open markers
|
2022-01-19 14:57:25 +01:00
|
|
|
indexOpenMarker := strings.Index(data, OpenMarker)
|
2022-01-19 14:10:11 +01:00
|
|
|
// exits if not found
|
|
|
|
|
if indexOpenMarker < 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 14:57:25 +01:00
|
|
|
indexOpenMarkerOffset := indexOpenMarker + len(OpenMarker)
|
2022-01-19 14:10:11 +01:00
|
|
|
|
|
|
|
|
shouldSearchCloseMarker := true
|
|
|
|
|
closeMarkerFound := false
|
|
|
|
|
innerData := data
|
|
|
|
|
var potentialMatch string
|
|
|
|
|
var indexCloseMarker, indexCloseMarkerOffset int
|
|
|
|
|
skip := indexOpenMarkerOffset
|
|
|
|
|
for shouldSearchCloseMarker {
|
|
|
|
|
// attempt to find close marker
|
2022-01-19 14:57:25 +01:00
|
|
|
indexCloseMarker = stringsutil.IndexAt(innerData, CloseMarker, skip)
|
2022-01-19 14:10:11 +01:00
|
|
|
// if no close markers are found exit
|
|
|
|
|
if indexCloseMarker < 0 {
|
|
|
|
|
shouldSearchCloseMarker = false
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-01-19 14:57:25 +01:00
|
|
|
indexCloseMarkerOffset = indexCloseMarker + len(CloseMarker)
|
2022-01-19 14:10:11 +01:00
|
|
|
|
|
|
|
|
potentialMatch = innerData[indexOpenMarkerOffset:indexCloseMarker]
|
2022-08-23 13:16:41 +05:30
|
|
|
if isExpression(potentialMatch, base) {
|
2022-01-19 14:10:11 +01:00
|
|
|
closeMarkerFound = true
|
|
|
|
|
shouldSearchCloseMarker = false
|
|
|
|
|
exps = append(exps, potentialMatch)
|
|
|
|
|
} else {
|
|
|
|
|
skip = indexCloseMarkerOffset
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if closeMarkerFound {
|
|
|
|
|
// move after the close marker
|
|
|
|
|
data = data[indexCloseMarkerOffset:]
|
|
|
|
|
} else {
|
|
|
|
|
// move after the open marker
|
|
|
|
|
data = data[indexOpenMarkerOffset:]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return exps
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-23 13:16:41 +05:30
|
|
|
func isExpression(data string, base map[string]interface{}) bool {
|
2022-01-19 14:10:11 +01:00
|
|
|
if _, err := govaluate.NewEvaluableExpression(data); err == nil {
|
2022-08-23 13:16:41 +05:30
|
|
|
if stringsutil.ContainsAny(data, getFunctionsNames(base)...) {
|
|
|
|
|
return true
|
|
|
|
|
} else if stringsutil.ContainsAny(data, dsl.FunctionNames...) {
|
|
|
|
|
return true
|
2022-01-09 12:52:04 +01:00
|
|
|
}
|
2022-08-23 13:16:41 +05:30
|
|
|
return false
|
2022-01-09 12:52:04 +01:00
|
|
|
}
|
2022-08-23 13:16:41 +05:30
|
|
|
_, err := govaluate.NewEvaluableExpressionWithFunctions(data, dsl.HelperFunctions)
|
|
|
|
|
return err == nil
|
2022-01-19 14:10:11 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-23 13:16:41 +05:30
|
|
|
func getFunctionsNames(m map[string]interface{}) []string {
|
|
|
|
|
keys := make([]string, 0, len(m))
|
2022-01-19 14:10:11 +01:00
|
|
|
for k := range m {
|
|
|
|
|
keys = append(keys, k)
|
|
|
|
|
}
|
|
|
|
|
return keys
|
2022-01-09 12:52:04 +01:00
|
|
|
}
|