Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/testutils/integration.go
2070 views
1
package testutils
2
3
import (
4
"crypto/tls"
5
"errors"
6
"fmt"
7
"net"
8
"net/http"
9
"net/http/httptest"
10
"os"
11
"os/exec"
12
"regexp"
13
"strings"
14
15
"github.com/gobwas/ws"
16
"github.com/julienschmidt/httprouter"
17
"github.com/projectdiscovery/utils/conversion"
18
)
19
20
// ExtraArgs
21
var (
22
ExtraDebugArgs = []string{}
23
ExtraEnvVars = []string{
24
"DISABLE_CLOUD_UPLOAD_WRN=true",
25
"DISABLE_CLOUD_UPLOAD=true",
26
}
27
)
28
29
// RunNucleiTemplateAndGetResults returns a list of results for a template
30
func RunNucleiTemplateAndGetResults(template, url string, debug bool, extra ...string) ([]string, error) {
31
return RunNucleiAndGetResults(true, template, url, debug, extra...)
32
}
33
34
// RunNucleiWorkflowAndGetResults returns a list of results for a workflow
35
func RunNucleiWorkflowAndGetResults(template, url string, debug bool, extra ...string) ([]string, error) {
36
return RunNucleiAndGetResults(false, template, url, debug, extra...)
37
}
38
39
func RunNucleiAndGetResults(isTemplate bool, template, url string, debug bool, extra ...string) ([]string, error) {
40
var templateOrWorkflowFlag string
41
if isTemplate {
42
templateOrWorkflowFlag = "-t"
43
} else {
44
templateOrWorkflowFlag = "-w"
45
}
46
47
return RunNucleiBareArgsAndGetResults(debug, nil, append([]string{
48
templateOrWorkflowFlag,
49
template,
50
"-target",
51
url,
52
}, extra...)...)
53
}
54
55
func RunNucleiBareArgsAndGetResults(debug bool, env []string, extra ...string) ([]string, error) {
56
cmd := exec.Command("./nuclei")
57
extra = append(extra, ExtraDebugArgs...)
58
cmd.Args = append(cmd.Args, extra...)
59
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
60
cmd.Args = append(cmd.Args, "-interactions-poll-duration", "1")
61
cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
62
cmd.Args = append(cmd.Args, "-allow-local-file-access")
63
if env != nil {
64
cmd.Env = append(os.Environ(), env...)
65
}
66
cmd.Env = append(cmd.Env, ExtraEnvVars...)
67
if debug {
68
cmd.Args = append(cmd.Args, "-debug")
69
cmd.Stderr = os.Stderr
70
fmt.Println(cmd.String())
71
} else {
72
cmd.Args = append(cmd.Args, "-silent")
73
}
74
output, err := cmd.Output()
75
var data string
76
if len(output) > 0 {
77
data = strings.TrimSpace(conversion.String(output))
78
}
79
if debug {
80
fmt.Println(data)
81
}
82
var parts []string
83
items := strings.Split(data, "\n")
84
for _, i := range items {
85
if i != "" {
86
parts = append(parts, i)
87
}
88
}
89
90
if (data == "" || len(parts) == 0) && err != nil {
91
return nil, fmt.Errorf("%v: %v", err.Error(), data)
92
}
93
94
return parts, nil
95
}
96
97
// RunNucleiWithArgsAndGetResults returns result,and runtime errors
98
func RunNucleiWithArgsAndGetResults(debug bool, args ...string) ([]string, error) {
99
cmd := exec.Command("./nuclei", args...)
100
cmd.Env = append(cmd.Env, ExtraEnvVars...)
101
if debug {
102
cmd.Args = append(cmd.Args, "-debug")
103
cmd.Stderr = os.Stderr
104
fmt.Println(cmd.String())
105
} else {
106
cmd.Args = append(cmd.Args, "-silent")
107
}
108
output, err := cmd.Output()
109
var data string
110
if len(output) > 0 {
111
data = strings.TrimSpace(conversion.String(output))
112
}
113
if debug {
114
fmt.Println(data)
115
}
116
var parts []string
117
items := strings.Split(data, "\n")
118
for _, i := range items {
119
if i != "" {
120
parts = append(parts, i)
121
}
122
}
123
124
if (data == "" || len(parts) == 0) && err != nil {
125
return nil, fmt.Errorf("%v: %v", err.Error(), data)
126
}
127
return parts, nil
128
}
129
130
// RunNucleiArgsAndGetErrors returns a list of errors in nuclei output (ERR,WRN,FTL)
131
func RunNucleiArgsAndGetErrors(debug bool, env []string, extra ...string) ([]string, error) {
132
cmd := exec.Command("./nuclei")
133
extra = append(extra, ExtraDebugArgs...)
134
cmd.Env = append(os.Environ(), env...)
135
cmd.Args = append(cmd.Args, extra...)
136
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
137
cmd.Args = append(cmd.Args, "-interactions-poll-duration", "1")
138
cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
139
cmd.Args = append(cmd.Args, "-allow-local-file-access")
140
cmd.Args = append(cmd.Args, "-nc") // disable color
141
cmd.Env = append(cmd.Env, ExtraEnvVars...)
142
dataOutput, err := cmd.CombinedOutput()
143
if debug {
144
fmt.Println(string(dataOutput))
145
}
146
var data string
147
if len(dataOutput) > 0 {
148
data = strings.TrimSpace(conversion.String(dataOutput))
149
}
150
results := []string{}
151
for _, v := range strings.Split(data, "\n") {
152
line := strings.TrimSpace(v)
153
switch {
154
case strings.HasPrefix(line, "[ERR]"):
155
results = append(results, line)
156
case strings.HasPrefix(line, "[WRN]"):
157
results = append(results, line)
158
case strings.HasPrefix(line, "[FTL]"):
159
results = append(results, line)
160
}
161
}
162
return results, err
163
}
164
165
// RunNucleiArgsWithEnvAndGetResults returns a list of results in nuclei output (ERR,WRN,FTL)
166
func RunNucleiArgsWithEnvAndGetResults(debug bool, env []string, extra ...string) ([]string, error) {
167
cmd := exec.Command("./nuclei")
168
extra = append(extra, ExtraDebugArgs...)
169
cmd.Env = append(os.Environ(), env...)
170
cmd.Env = append(cmd.Env, ExtraEnvVars...)
171
cmd.Args = append(cmd.Args, extra...)
172
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
173
cmd.Args = append(cmd.Args, "-interactions-poll-duration", "5")
174
cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
175
cmd.Args = append(cmd.Args, "-allow-local-file-access")
176
if debug {
177
cmd.Args = append(cmd.Args, "-debug")
178
cmd.Stderr = os.Stderr
179
fmt.Println(cmd.String())
180
} else {
181
cmd.Args = append(cmd.Args, "-silent")
182
}
183
dataOutput, err := cmd.Output()
184
var data string
185
if len(dataOutput) > 0 {
186
data = strings.TrimSpace(conversion.String(dataOutput))
187
}
188
if debug {
189
fmt.Println(data)
190
}
191
var parts []string
192
items := strings.Split(data, "\n")
193
for _, i := range items {
194
if i != "" {
195
parts = append(parts, i)
196
}
197
}
198
if (data == "" || len(parts) == 0) && err != nil {
199
return nil, fmt.Errorf("%v: %v", err.Error(), data)
200
}
201
return parts, nil
202
}
203
204
var templateLoaded = regexp.MustCompile(`(?:Templates|Workflows) loaded[^:]*: (\d+)`)
205
206
// RunNucleiBinaryAndGetLoadedTemplates returns a list of results for a template
207
func RunNucleiBinaryAndGetLoadedTemplates(nucleiBinary string, debug bool, args []string) (string, error) {
208
cmd := exec.Command(nucleiBinary, args...)
209
cmd.Env = append(cmd.Env, ExtraEnvVars...)
210
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
211
if debug {
212
cmd.Args = append(cmd.Args, "-debug")
213
fmt.Println(cmd.String())
214
}
215
data, err := cmd.CombinedOutput()
216
if debug {
217
fmt.Println(string(data))
218
}
219
if err != nil {
220
return "", err
221
}
222
matches := templateLoaded.FindAllStringSubmatch(string(data), -1)
223
if len(matches) == 0 {
224
return "", errors.New("no matches found")
225
}
226
return matches[0][1], nil
227
}
228
func RunNucleiBinaryAndGetCombinedOutput(debug bool, args []string) (string, error) {
229
args = append(args, "-interactions-cooldown-period", "10", "-interactions-poll-duration", "1")
230
cmd := exec.Command("./nuclei", args...)
231
cmd.Env = append(cmd.Env, ExtraEnvVars...)
232
if debug {
233
cmd.Args = append(cmd.Args, "-debug")
234
fmt.Println(cmd.String())
235
}
236
data, err := cmd.CombinedOutput()
237
if debug {
238
fmt.Println(string(data))
239
}
240
if err != nil {
241
return "", err
242
}
243
return string(data), nil
244
}
245
246
// TestCase is a single integration test case
247
type TestCase interface {
248
// Execute executes a test case and returns any errors if occurred
249
Execute(filePath string) error
250
}
251
252
// TCPServer creates a new tcp server that returns a response
253
type TCPServer struct {
254
URL string
255
listener net.Listener
256
}
257
258
// keys taken from https://pascal.bach.ch/2015/12/17/from-tcp-to-tls-in-go/
259
const serverKey = `-----BEGIN EC PARAMETERS-----
260
BgUrgQQAIg==
261
-----END EC PARAMETERS-----
262
-----BEGIN EC PRIVATE KEY-----
263
MIGkAgEBBDBJazGwuqgOLsCMr7P56w26JBEHQokiuAy2iCQfCnmOWm7S9FveQ/DP
264
qB69zvUPs26gBwYFK4EEACKhZANiAARehvy96ygCAsJ6iQvthzl/Nvq4P3c4MGyx
265
UMLMe0L10OCxeCl5ZY2CuFf8UnBgV1u414U4+yjIrS57w1/3utBKC9TVRGj+Vcls
266
2NZ4+8Jh6/M/Jf/Mpd8QyIy0WesEUM4=
267
-----END EC PRIVATE KEY-----
268
`
269
270
const serverCert = `-----BEGIN CERTIFICATE-----
271
MIICJDCCAakCCQDFa0/D9jJw6DAKBggqhkjOPQQDAjB7MQswCQYDVQQGEwJVUzEP
272
MA0GA1UECAwGcGRsYW5kMQ8wDQYDVQQHDAZwZGNpdHkxCzAJBgNVBAoMAnBkMQsw
273
CQYDVQQLDAJwZDELMAkGA1UEAwwCcGQxIzAhBgkqhkiG9w0BCQEWFGFueXRoaW5n
274
QGFueXRoaW5nLnBkMB4XDTIyMDEyNzIyMDUwNFoXDTMyMDEyNTIyMDUwNFowezEL
275
MAkGA1UEBhMCVVMxDzANBgNVBAgMBnBkbGFuZDEPMA0GA1UEBwwGcGRjaXR5MQsw
276
CQYDVQQKDAJwZDELMAkGA1UECwwCcGQxCzAJBgNVBAMMAnBkMSMwIQYJKoZIhvcN
277
AQkBFhRhbnl0aGluZ0Bhbnl0aGluZy5wZDB2MBAGByqGSM49AgEGBSuBBAAiA2IA
278
BF6G/L3rKAICwnqJC+2HOX82+rg/dzgwbLFQwsx7QvXQ4LF4KXlljYK4V/xScGBX
279
W7jXhTj7KMitLnvDX/e60EoL1NVEaP5VyWzY1nj7wmHr8z8l/8yl3xDIjLRZ6wRQ
280
zjAKBggqhkjOPQQDAgNpADBmAjEAgxGPbjRlhz+1Scmr6RU9VbzVJWN8KCsTTpx7
281
pqfmKpJ29UYReZN+fm/6fc5vkv1rAjEAkTuTf8ARSn1UiKlCTTDQVtCoRcMVLQQp
282
TCxxGzcAlUAAJE6+SJpY7fPRe+n2EvPS
283
-----END CERTIFICATE-----
284
`
285
286
// NewTCPServer creates a new TCP server from a handler
287
func NewTCPServer(tlsConfig *tls.Config, port int, handler func(conn net.Conn)) *TCPServer {
288
server := &TCPServer{}
289
290
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
291
if err != nil {
292
panic(err)
293
}
294
server.URL = l.Addr().String()
295
server.listener = l
296
297
if tlsConfig != nil {
298
cer, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
299
if err != nil {
300
panic(err)
301
}
302
tlsConfig.Certificates = []tls.Certificate{cer}
303
}
304
305
go func() {
306
for {
307
// Listen for an incoming connection.
308
conn, err := l.Accept()
309
if err != nil {
310
continue
311
}
312
// Handle connections in a new goroutine.
313
if tlsConfig != nil {
314
connTls := tls.Server(conn, tlsConfig)
315
go handler(connTls)
316
} else {
317
go handler(conn)
318
}
319
}
320
}()
321
return server
322
}
323
324
// Close closes the TCP server
325
func (s *TCPServer) Close() {
326
_ = s.listener.Close()
327
}
328
329
// NewWebsocketServer creates a new websocket server from a handler
330
func NewWebsocketServer(path string, handler func(conn net.Conn), originValidate func(origin string) bool, port ...int) *httptest.Server {
331
handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
332
if value := r.Header.Get("Origin"); value != "" && !originValidate(value) {
333
w.WriteHeader(http.StatusBadRequest)
334
return
335
}
336
conn, _, _, err := ws.UpgradeHTTP(r, w)
337
if err != nil {
338
return
339
}
340
go func() {
341
defer func() {
342
_ = conn.Close()
343
}()
344
345
handler(conn)
346
}()
347
})
348
349
if path != "" {
350
router := httprouter.New()
351
router.HandlerFunc("*", "/test", handlerFunc)
352
return httptest.NewServer(router)
353
}
354
return httptest.NewServer(handlerFunc)
355
}
356
357