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