Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/operators/matchers/match_test.go
2866 views
1
package matchers
2
3
import (
4
"testing"
5
6
"github.com/Knetic/govaluate"
7
"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"
8
"github.com/stretchr/testify/require"
9
)
10
11
func TestWordANDCondition(t *testing.T) {
12
m := &Matcher{condition: ANDCondition, Words: []string{"a", "b"}}
13
14
isMatched, matched := m.MatchWords("a b", nil)
15
require.True(t, isMatched, "Could not match words with valid AND condition")
16
require.Equal(t, m.Words, matched)
17
18
isMatched, matched = m.MatchWords("b", nil)
19
require.False(t, isMatched, "Could match words with invalid AND condition")
20
require.Equal(t, []string{}, matched)
21
}
22
23
func TestRegexANDCondition(t *testing.T) {
24
m := &Matcher{Type: MatcherTypeHolder{MatcherType: RegexMatcher}, Condition: "and", Regex: []string{"[a-z]{3}", "\\d{2}"}}
25
err := m.CompileMatchers()
26
require.Nil(t, err)
27
28
isMatched, matched := m.MatchRegex("abc abcd 123")
29
require.True(t, isMatched, "Could not match regex with valid AND condition")
30
require.Equal(t, []string{"abc", "abc", "12"}, matched)
31
32
isMatched, matched = m.MatchRegex("bc 1")
33
require.False(t, isMatched, "Could match regex with invalid AND condition")
34
require.Equal(t, []string{}, matched)
35
}
36
37
func TestORCondition(t *testing.T) {
38
m := &Matcher{condition: ORCondition, Words: []string{"a", "b"}}
39
40
isMatched, matched := m.MatchWords("a b", nil)
41
require.True(t, isMatched, "Could not match valid word OR condition")
42
require.Equal(t, []string{"a"}, matched)
43
44
isMatched, matched = m.MatchWords("b", nil)
45
require.True(t, isMatched, "Could not match valid word OR condition")
46
require.Equal(t, []string{"b"}, matched)
47
48
isMatched, matched = m.MatchWords("c", nil)
49
require.False(t, isMatched, "Could match invalid word OR condition")
50
require.Equal(t, []string{}, matched)
51
}
52
53
func TestRegexOrCondition(t *testing.T) {
54
m := &Matcher{Type: MatcherTypeHolder{MatcherType: RegexMatcher}, Condition: "or", Regex: []string{"[a-z]{3}", "\\d{2}"}}
55
err := m.CompileMatchers()
56
require.Nil(t, err)
57
58
isMatched, matched := m.MatchRegex("ab 123")
59
require.True(t, isMatched, "Could not match valid regex OR condition")
60
require.Equal(t, []string{"12"}, matched)
61
62
isMatched, matched = m.MatchRegex("bc 1")
63
require.False(t, isMatched, "Could match invalid regex OR condition")
64
require.Equal(t, []string{}, matched)
65
}
66
67
func TestHexEncoding(t *testing.T) {
68
m := &Matcher{Encoding: "hex", Type: MatcherTypeHolder{MatcherType: WordsMatcher}, Part: "body", Words: []string{"50494e47"}}
69
err := m.CompileMatchers()
70
require.Nil(t, err, "could not compile matcher")
71
72
isMatched, matched := m.MatchWords("PING", nil)
73
require.True(t, isMatched, "Could not match valid Hex condition")
74
require.Equal(t, m.Words, matched)
75
}
76
77
func TestMatcher_MatchDSL(t *testing.T) {
78
compiled, err := govaluate.NewEvaluableExpressionWithFunctions("contains(body, \"{{VARIABLE}}\")", dsl.HelperFunctions)
79
require.Nil(t, err, "couldn't compile expression")
80
81
m := &Matcher{Type: MatcherTypeHolder{MatcherType: DSLMatcher}, dslCompiled: []*govaluate.EvaluableExpression{compiled}}
82
err = m.CompileMatchers()
83
require.Nil(t, err, "could not compile matcher")
84
85
values := []string{"PING", "pong"}
86
87
for _, value := range values {
88
isMatched := m.MatchDSL(map[string]interface{}{"body": value, "VARIABLE": value})
89
require.True(t, isMatched)
90
}
91
}
92
93
func TestMatcher_MatchXPath_HTML(t *testing.T) {
94
body := `<!doctype html>
95
<html>
96
<head>
97
<title>Example Domain</title>
98
99
<meta charset="utf-8" />
100
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
101
<meta name="viewport" content="width=device-width, initial-scale=1" />
102
</head>
103
104
<body>
105
<div>
106
<h1>Example Domain</h1>
107
<p>This domain is for use in illustrative examples in documents. You may use this
108
domain in literature without prior coordination or asking for permission.</p>
109
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
110
</div>
111
</body>
112
</html>
113
`
114
body2 := `<!doctype html>
115
<html>
116
<head>
117
<title>Example Domain</title>
118
</head>
119
<body>
120
<h1> It's test time! </h1>
121
</body>
122
</html>
123
`
124
125
// single match
126
m := &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, XPath: []string{"/html/body/div/p[2]/a"}}
127
err := m.CompileMatchers()
128
require.Nil(t, err)
129
130
isMatched := m.MatchXPath(body)
131
require.True(t, isMatched, "Could not match valid XPath")
132
133
isMatched = m.MatchXPath("<h1>aaaaaaaaa")
134
require.False(t, isMatched, "Could match invalid XPath")
135
136
// OR match
137
m = &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, Condition: "or", XPath: []string{"/html/head/title[contains(text(), 'PATRICAAA')]", "/html/body/div/p[2]/a"}}
138
err = m.CompileMatchers()
139
require.Nil(t, err)
140
141
isMatched = m.MatchXPath(body)
142
require.True(t, isMatched, "Could not match valid multi-XPath with OR condition")
143
144
isMatched = m.MatchXPath(body2)
145
require.False(t, isMatched, "Could match invalid multi-XPath with OR condition")
146
147
// AND match
148
m = &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, Condition: "and", XPath: []string{"/html/head/title[contains(text(), 'Example Domain')]", "/html/body/div/p[2]/a"}}
149
err = m.CompileMatchers()
150
require.Nil(t, err)
151
152
isMatched = m.MatchXPath(body)
153
require.True(t, isMatched, "Could not match valid multi-XPath with AND condition")
154
155
isMatched = m.MatchXPath(body2)
156
require.False(t, isMatched, "Could match invalid multi-XPath with AND condition")
157
158
// invalid xpath
159
m = &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, XPath: []string{"//a[@a==1]"}}
160
_ = m.CompileMatchers()
161
isMatched = m.MatchXPath(body)
162
require.False(t, isMatched, "Invalid xpath did not return false")
163
}
164
165
func TestMatcher_MatchXPath_XML(t *testing.T) {
166
body := `<?xml version="1.0" encoding="utf-8"?><foo>bar</foo><wibble id="1" /><parent><child>baz</child></parent>`
167
body2 := `<?xml version="1.0" encoding="utf-8"?><test>bar</test><wibble2 id="1" /><roditelj><dijete>alo</dijete></roditelj>`
168
169
// single match
170
m := &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, XPath: []string{"//foo[contains(text(), 'bar')]"}}
171
err := m.CompileMatchers()
172
require.Nil(t, err)
173
174
isMatched := m.MatchXPath(body)
175
require.True(t, isMatched, "Could not match valid XPath")
176
177
isMatched = m.MatchXPath("<h1>aaaaaaaaa</h1>")
178
require.False(t, isMatched, "Could match invalid XPath")
179
180
// OR match
181
m = &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, Condition: "or", XPath: []string{"/foo[contains(text(), 'PATRICAAA')]", "/parent/child"}}
182
err = m.CompileMatchers()
183
require.Nil(t, err)
184
185
isMatched = m.MatchXPath(body)
186
require.True(t, isMatched, "Could not match valid multi-XPath with OR condition")
187
188
isMatched = m.MatchXPath(body2)
189
require.False(t, isMatched, "Could match invalid multi-XPath with OR condition")
190
191
// AND match
192
m = &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, Condition: "and", XPath: []string{"/foo[contains(text(), 'bar')]", "/parent/child"}}
193
err = m.CompileMatchers()
194
require.Nil(t, err)
195
196
isMatched = m.MatchXPath(body)
197
require.True(t, isMatched, "Could not match valid multi-XPath with AND condition")
198
199
isMatched = m.MatchXPath(body2)
200
require.False(t, isMatched, "Could match invalid multi-XPath with AND condition")
201
202
// invalid xpath
203
m = &Matcher{Type: MatcherTypeHolder{MatcherType: XPathMatcher}, XPath: []string{"//a[@a==1]"}}
204
_ = m.CompileMatchers()
205
isMatched = m.MatchXPath(body)
206
require.False(t, isMatched, "Invalid xpath did not return false")
207
208
// invalid xml
209
isMatched = m.MatchXPath("<h1> not right <q id=2/>notvalid")
210
require.False(t, isMatched, "Invalid xpath did not return false")
211
}
212
213
func TestMatchRegex_CaseInsensitivePrefixSkip(t *testing.T) {
214
m := &Matcher{Type: MatcherTypeHolder{MatcherType: RegexMatcher}, Condition: "or", Regex: []string{"(?i)abc"}}
215
err := m.CompileMatchers()
216
require.NoError(t, err)
217
ok, got := m.MatchRegex("zzz AbC yyy")
218
require.True(t, ok)
219
require.NotEmpty(t, got)
220
}
221
222
func TestMatchStatusCodeAndSize(t *testing.T) {
223
mStatus := &Matcher{Status: []int{200, 302}}
224
require.True(t, mStatus.MatchStatusCode(200))
225
require.True(t, mStatus.MatchStatusCode(302))
226
require.False(t, mStatus.MatchStatusCode(404))
227
228
mSize := &Matcher{Size: []int{5, 10}}
229
require.True(t, mSize.MatchSize(5))
230
require.False(t, mSize.MatchSize(7))
231
}
232
233
func TestMatchBinary_AND_OR(t *testing.T) {
234
// AND should fail if any binary not present
235
mAnd := &Matcher{Type: MatcherTypeHolder{MatcherType: BinaryMatcher}, Condition: "and", Binary: []string{"50494e47", "414141"}} // "PING", "AAA"
236
require.NoError(t, mAnd.CompileMatchers())
237
ok, _ := mAnd.MatchBinary("PING")
238
require.False(t, ok)
239
// OR should succeed if any present
240
mOr := &Matcher{Type: MatcherTypeHolder{MatcherType: BinaryMatcher}, Condition: "or", Binary: []string{"414141", "50494e47"}} // "AAA", "PING"
241
require.NoError(t, mOr.CompileMatchers())
242
ok, got := mOr.MatchBinary("xxPINGyy")
243
require.True(t, ok)
244
require.NotEmpty(t, got)
245
}
246
247
func TestMatchRegex_LiteralPrefixShortCircuit(t *testing.T) {
248
// AND: first regex has literal prefix "abc"; corpus lacks it => early false
249
mAnd := &Matcher{Type: MatcherTypeHolder{MatcherType: RegexMatcher}, Condition: "and", Regex: []string{"abc[0-9]*", "[0-9]{2}"}}
250
require.NoError(t, mAnd.CompileMatchers())
251
ok, matches := mAnd.MatchRegex("zzz 12 yyy")
252
require.False(t, ok)
253
require.Empty(t, matches)
254
255
// OR: first regex skipped due to missing prefix, second matches => true
256
mOr := &Matcher{Type: MatcherTypeHolder{MatcherType: RegexMatcher}, Condition: "or", Regex: []string{"abc[0-9]*", "[0-9]{2}"}}
257
require.NoError(t, mOr.CompileMatchers())
258
ok, matches = mOr.MatchRegex("zzz 12 yyy")
259
require.True(t, ok)
260
require.Equal(t, []string{"12"}, matches)
261
}
262
263
func TestMatcher_MatchDSL_ErrorHandling(t *testing.T) {
264
// First expression errors (division by zero), second is true
265
bad, err := govaluate.NewEvaluableExpression("1 / 0")
266
require.NoError(t, err)
267
good, err := govaluate.NewEvaluableExpression("1 == 1")
268
require.NoError(t, err)
269
270
m := &Matcher{Type: MatcherTypeHolder{MatcherType: DSLMatcher}, Condition: "or", dslCompiled: []*govaluate.EvaluableExpression{bad, good}}
271
require.NoError(t, m.CompileMatchers())
272
ok := m.MatchDSL(map[string]interface{}{})
273
require.True(t, ok)
274
}
275
276