Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/common/expressions/expressions.go
2072 views
1
package expressions
2
3
import (
4
"strings"
5
6
"github.com/Knetic/govaluate"
7
8
"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"
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 iterations <= maxIterations {
80
// check if we reached the maximum number of iterations
81
82
iterations++
83
// attempt to find open markers
84
indexOpenMarker := strings.Index(data, OpenMarker)
85
// exits if not found
86
if indexOpenMarker < 0 {
87
break
88
}
89
90
indexOpenMarkerOffset := indexOpenMarker + len(OpenMarker)
91
92
shouldSearchCloseMarker := true
93
closeMarkerFound := false
94
innerData := data
95
var potentialMatch string
96
var indexCloseMarker, indexCloseMarkerOffset int
97
skip := indexOpenMarkerOffset
98
for shouldSearchCloseMarker {
99
// attempt to find close marker
100
indexCloseMarker = stringsutil.IndexAt(innerData, CloseMarker, skip)
101
// if no close markers are found exit
102
if indexCloseMarker < 0 {
103
shouldSearchCloseMarker = false
104
continue
105
}
106
indexCloseMarkerOffset = indexCloseMarker + len(CloseMarker)
107
108
potentialMatch = innerData[indexOpenMarkerOffset:indexCloseMarker]
109
if isExpression(potentialMatch, base) {
110
closeMarkerFound = true
111
shouldSearchCloseMarker = false
112
exps = append(exps, potentialMatch)
113
} else {
114
skip = indexCloseMarkerOffset
115
}
116
}
117
118
if closeMarkerFound {
119
// move after the close marker
120
data = data[indexCloseMarkerOffset:]
121
} else {
122
// move after the open marker
123
data = data[indexOpenMarkerOffset:]
124
}
125
}
126
return exps
127
}
128
129
func isExpression(data string, base map[string]interface{}) bool {
130
if _, err := govaluate.NewEvaluableExpression(data); err == nil {
131
if stringsutil.ContainsAny(data, getFunctionsNames(base)...) {
132
return true
133
} else if stringsutil.ContainsAny(data, dsl.FunctionNames...) {
134
return true
135
}
136
return false
137
}
138
_, err := govaluate.NewEvaluableExpressionWithFunctions(data, dsl.HelperFunctions)
139
return err == nil
140
}
141
142
func getFunctionsNames(m map[string]interface{}) []string {
143
keys := make([]string, 0, len(m))
144
for k := range m {
145
keys = append(keys, k)
146
}
147
return keys
148
}
149
150