Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/offlinehttp/operators_test.go
2070 views
1
package offlinehttp
2
3
import (
4
"net/http"
5
"testing"
6
"time"
7
8
"github.com/stretchr/testify/require"
9
10
"github.com/projectdiscovery/nuclei/v3/pkg/model"
11
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
12
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
13
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
14
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
15
"github.com/projectdiscovery/nuclei/v3/pkg/output"
16
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
17
)
18
19
func TestResponseToDSLMap(t *testing.T) {
20
options := testutils.DefaultOptions
21
22
testutils.Init(options)
23
templateID := "testing-http"
24
request := &Request{}
25
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
26
ID: templateID,
27
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
28
})
29
executerOpts.Operators = []*operators.Operators{{}}
30
err := request.Compile(executerOpts)
31
require.Nil(t, err, "could not compile file request")
32
33
resp := &http.Response{}
34
resp.Header = make(http.Header)
35
resp.Header.Set("Test", "Test-Response")
36
host := "http://example.com/test/"
37
matched := "http://example.com/test/?test=1"
38
39
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
40
require.Len(t, event, 14, "could not get correct number of items in dsl map")
41
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
42
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
43
}
44
45
func TestHTTPOperatorMatch(t *testing.T) {
46
options := testutils.DefaultOptions
47
48
testutils.Init(options)
49
templateID := "testing-http"
50
request := &Request{}
51
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
52
ID: templateID,
53
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
54
})
55
executerOpts.Operators = []*operators.Operators{{}}
56
err := request.Compile(executerOpts)
57
require.Nil(t, err, "could not compile file request")
58
59
resp := &http.Response{}
60
resp.Header = make(http.Header)
61
resp.Header.Set("Test", "Test-Response")
62
host := "http://example.com/test/"
63
matched := "http://example.com/test/?test=1"
64
65
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
66
require.Len(t, event, 14, "could not get correct number of items in dsl map")
67
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
68
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
69
70
t.Run("valid", func(t *testing.T) {
71
matcher := &matchers.Matcher{
72
Part: "body",
73
Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
74
Words: []string{"1.1.1.1"},
75
}
76
err = matcher.CompileMatchers()
77
require.Nil(t, err, "could not compile matcher")
78
79
isMatched, matched := request.Match(event, matcher)
80
require.True(t, isMatched, "could not match valid response")
81
require.Equal(t, matcher.Words, matched)
82
})
83
84
t.Run("negative", func(t *testing.T) {
85
matcher := &matchers.Matcher{
86
Part: "body",
87
Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
88
Negative: true,
89
Words: []string{"random"},
90
}
91
err := matcher.CompileMatchers()
92
require.Nil(t, err, "could not compile negative matcher")
93
94
isMatched, matched := request.Match(event, matcher)
95
require.True(t, isMatched, "could not match valid negative response matcher")
96
require.Equal(t, []string{}, matched)
97
})
98
99
t.Run("invalid", func(t *testing.T) {
100
matcher := &matchers.Matcher{
101
Part: "body",
102
Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
103
Words: []string{"random"},
104
}
105
err := matcher.CompileMatchers()
106
require.Nil(t, err, "could not compile matcher")
107
108
isMatched, matched := request.Match(event, matcher)
109
require.False(t, isMatched, "could match invalid response matcher")
110
require.Equal(t, []string{}, matched)
111
})
112
}
113
114
func TestHTTPOperatorExtract(t *testing.T) {
115
options := testutils.DefaultOptions
116
117
testutils.Init(options)
118
templateID := "testing-http"
119
request := &Request{}
120
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
121
ID: templateID,
122
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
123
})
124
executerOpts.Operators = []*operators.Operators{{}}
125
err := request.Compile(executerOpts)
126
require.Nil(t, err, "could not compile file request")
127
128
resp := &http.Response{}
129
resp.Header = make(http.Header)
130
resp.Header.Set("Test-Header", "Test-Response")
131
host := "http://example.com/test/"
132
matched := "http://example.com/test/?test=1"
133
134
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
135
require.Len(t, event, 14, "could not get correct number of items in dsl map")
136
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
137
require.Equal(t, "Test-Response", event["test_header"], "could not get correct resp for header")
138
139
t.Run("extract", func(t *testing.T) {
140
extractor := &extractors.Extractor{
141
Part: "body",
142
Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},
143
Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"},
144
}
145
err = extractor.CompileExtractors()
146
require.Nil(t, err, "could not compile extractor")
147
148
data := request.Extract(event, extractor)
149
require.Greater(t, len(data), 0, "could not extractor valid response")
150
require.Equal(t, map[string]struct{}{"1.1.1.1": {}}, data, "could not extract correct data")
151
})
152
153
t.Run("kval", func(t *testing.T) {
154
extractor := &extractors.Extractor{
155
Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.KValExtractor},
156
KVal: []string{"test_header"},
157
Part: "header",
158
}
159
err = extractor.CompileExtractors()
160
require.Nil(t, err, "could not compile kval extractor")
161
162
data := request.Extract(event, extractor)
163
require.Greater(t, len(data), 0, "could not extractor kval valid response")
164
require.Equal(t, map[string]struct{}{"Test-Response": {}}, data, "could not extract correct kval data")
165
})
166
}
167
168
func TestHTTPMakeResult(t *testing.T) {
169
options := testutils.DefaultOptions
170
171
testutils.Init(options)
172
templateID := "testing-http"
173
request := &Request{}
174
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
175
ID: templateID,
176
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
177
})
178
executerOpts.Operators = []*operators.Operators{{
179
Matchers: []*matchers.Matcher{{
180
Name: "test",
181
Part: "body",
182
Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
183
Words: []string{"1.1.1.1"},
184
}},
185
Extractors: []*extractors.Extractor{{
186
Part: "body",
187
Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},
188
Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"},
189
}},
190
}}
191
err := request.Compile(executerOpts)
192
require.Nil(t, err, "could not compile file request")
193
194
resp := &http.Response{}
195
resp.Header = make(http.Header)
196
resp.Header.Set("Test", "Test-Response")
197
host := "http://example.com/test/"
198
matched := "http://example.com/test/?test=1"
199
200
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
201
require.Len(t, event, 14, "could not get correct number of items in dsl map")
202
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
203
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
204
205
event["ip"] = "192.169.1.1"
206
finalEvent := &output.InternalWrappedEvent{InternalEvent: event}
207
for _, operator := range request.compiledOperators {
208
result, ok := operator.Execute(event, request.Match, request.Extract, false)
209
if ok && result != nil {
210
finalEvent.OperatorsResult = result
211
finalEvent.Results = request.MakeResultEvent(finalEvent)
212
}
213
}
214
require.Equal(t, 1, len(finalEvent.Results), "could not get correct number of results")
215
require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results")
216
require.Equal(t, "1.1.1.1", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results")
217
}
218
219
const exampleRawRequest = `GET / HTTP/1.1
220
Host: example.com
221
Upgrade-Insecure-Requests: 1
222
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36
223
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
224
Accept-Encoding: gzip, deflate
225
Accept-Language: en-US,en;q=0.9,hi;q=0.8
226
If-None-Match: "3147526947+gzip"
227
If-Modified-Since: Thu, 17 Oct 2019 07:18:26 GMT
228
Connection: close
229
230
`
231
232
const exampleRawResponse = exampleResponseHeader + exampleResponseBody
233
const exampleResponseHeader = `
234
HTTP/1.1 200 OK
235
Accept-Ranges: bytes
236
Age: 493322
237
Cache-Control: max-age=604800
238
Content-Type: text/html; charset=UTF-8
239
Date: Thu, 04 Feb 2021 12:15:51 GMT
240
Etag: "3147526947+ident"
241
Expires: Thu, 11 Feb 2021 12:15:51 GMT
242
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
243
Server: ECS (nyb/1D1C)
244
Vary: Accept-Encoding
245
X-Cache: HIT
246
Content-Length: 1256
247
Connection: close
248
`
249
250
const exampleResponseBody = `
251
<!doctype html>
252
<html>
253
<head>
254
<title>Example Domain</title>
255
256
<meta charset="utf-8" />
257
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
258
<meta name="viewport" content="width=device-width, initial-scale=1" />
259
<style type="text/css">
260
body {
261
background-color: #f0f0f2;
262
margin: 0;
263
padding: 0;
264
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
265
266
}
267
div {
268
width: 600px;
269
margin: 5em auto;
270
padding: 2em;
271
background-color: #fdfdff;
272
border-radius: 0.5em;
273
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
274
}
275
a:link, a:visited {
276
color: #38488f;
277
text-decoration: none;
278
}
279
@media (max-width: 700px) {
280
div {
281
margin: 0 auto;
282
width: auto;
283
}
284
}
285
</style>
286
</head>
287
<a>1.1.1.1</a>
288
<body>
289
<div>
290
<h1>Example Domain</h1>
291
<p>This domain is for use in illustrative examples in documents. You may use this
292
domain in literature without prior coordination or asking for permission.</p>
293
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
294
</div>
295
</body>
296
</html>
297
`
298
299