Passed
Pull Request — master (#60)
by Stefano
02:49
created

cmd_test.TestScanCommand   B

Complexity

Conditions 6

Size

Total Lines 81
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 53
nop 1
dl 0
loc 81
rs 7.6048
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
package cmd_test
2
3
import (
4
	"net"
5
	"net/http"
6
	"net/http/httptest"
7
	"sync"
8
	"syscall"
9
	"testing"
10
	"time"
11
12
	"github.com/armon/go-socks5"
13
	"github.com/stefanoj3/dirstalk/pkg/common/test"
14
	"github.com/stretchr/testify/assert"
15
)
16
17
const socks5TestServerHost = "127.0.0.1:8899"
18
19
func TestScanCommand(t *testing.T) {
20
	logger, loggerBuffer := test.NewLogger()
21
22
	c, err := createCommand(logger)
23
	assert.NoError(t, err)
24
	assert.NotNil(t, c)
25
26
	testServer, serverAssertion := test.NewServerWithAssertion(
27
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28
			if r.URL.Path == "/test/" {
29
				w.WriteHeader(http.StatusOK)
30
				return
31
			}
32
			if r.URL.Path == "/potato" {
33
				w.WriteHeader(http.StatusOK)
34
				return
35
			}
36
37
			if r.URL.Path == "/test/test/" {
38
				http.Redirect(w, r, "/potato", http.StatusMovedPermanently)
39
				return
40
			}
41
42
			w.WriteHeader(http.StatusNotFound)
43
		}),
44
	)
45
	defer testServer.Close()
46
47
	_, _, err = executeCommand(
48
		c,
49
		"scan",
50
		testServer.URL,
51
		"--dictionary",
52
		"testdata/dict2.txt",
53
		"-v",
54
		"--http-timeout",
55
		"300",
56
	)
57
	assert.NoError(t, err)
58
59
	assert.Equal(t, 17, serverAssertion.Len())
60
61
	requestsMap := map[string]string{}
62
63
	serverAssertion.Range(func(_ int, r http.Request) {
64
		requestsMap[r.URL.Path] = r.Method
65
	})
66
67
	expectedRequests := map[string]string{
68
		"/test/":               http.MethodGet,
69
		"/test/home":           http.MethodGet,
70
		"/test/blabla":         http.MethodGet,
71
		"/test/home/index.php": http.MethodGet,
72
		"/potato":              http.MethodGet,
73
74
		"/potato/test/":          http.MethodGet,
75
		"/potato/home":           http.MethodGet,
76
		"/potato/home/index.php": http.MethodGet,
77
		"/potato/blabla":         http.MethodGet,
78
79
		"/test/test/test/":          http.MethodGet,
80
		"/test/test/home":           http.MethodGet,
81
		"/test/test/home/index.php": http.MethodGet,
82
		"/test/test/blabla":         http.MethodGet,
83
84
		"/test/test/": http.MethodGet,
85
86
		"/home":           http.MethodGet,
87
		"/blabla":         http.MethodGet,
88
		"/home/index.php": http.MethodGet,
89
	}
90
91
	assert.Equal(t, expectedRequests, requestsMap)
92
93
	expectedResultTree := `/
94
├── potato
95
└── test
96
    └── test
97
`
98
99
	assert.Contains(t, loggerBuffer.String(), expectedResultTree)
100
}
101
102
func TestScanWithNoTargetShouldErr(t *testing.T) {
103
	logger, _ := test.NewLogger()
104
105
	c, err := createCommand(logger)
106
	assert.NoError(t, err)
107
	assert.NotNil(t, c)
108
109
	_, _, err = executeCommand(c, "scan", "--dictionary", "testdata/dict2.txt")
110
	assert.Error(t, err)
111
	assert.Contains(t, err.Error(), "no URL provided")
112
}
113
114
func TestScanWithInvalidTargetShouldErr(t *testing.T) {
115
	logger, _ := test.NewLogger()
116
117
	c, err := createCommand(logger)
118
	assert.NoError(t, err)
119
	assert.NotNil(t, c)
120
121
	_, _, err = executeCommand(c, "scan", "--dictionary", "testdata/dict2.txt", "localhost%%2")
122
	assert.Error(t, err)
123
	assert.Contains(t, err.Error(), "invalid URI")
124
}
125
126
func TestScanCommandCanBeInterrupted(t *testing.T) {
127
	logger, loggerBuffer := test.NewLogger()
128
129
	c, err := createCommand(logger)
130
	assert.NoError(t, err)
131
	assert.NotNil(t, c)
132
133
	testServer, serverAssertion := test.NewServerWithAssertion(
134
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
135
			time.Sleep(time.Millisecond * 650)
136
137
			if r.URL.Path == "/test/" {
138
				w.WriteHeader(http.StatusOK)
139
				return
140
			}
141
142
			w.WriteHeader(http.StatusNotFound)
143
		}),
144
	)
145
	defer testServer.Close()
146
147
	go func() {
148
		time.Sleep(time.Millisecond * 200)
149
		_ = syscall.Kill(syscall.Getpid(), syscall.SIGINT)
150
	}()
151
152
	_, _, err = executeCommand(
153
		c,
154
		"scan",
155
		testServer.URL,
156
		"--dictionary",
157
		"testdata/dict2.txt",
158
		"-v",
159
		"--http-timeout",
160
		"900",
161
	)
162
	assert.NoError(t, err)
163
164
	assert.True(t, serverAssertion.Len() > 0)
165
	assert.Contains(t, loggerBuffer.String(), "Received sigint")
166
}
167
168
func TestScanWithRemoteDictionary(t *testing.T) {
169
	logger, _ := test.NewLogger()
170
171
	c, err := createCommand(logger)
172
	assert.NoError(t, err)
173
	assert.NotNil(t, c)
174
175
	dictionaryServer := httptest.NewServer(
176
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
177
			dict := `home
178
home/index.php
179
blabla
180
`
181
			w.WriteHeader(http.StatusOK)
182
			_, _ = w.Write([]byte(dict))
183
		}),
184
	)
185
	defer dictionaryServer.Close()
186
187
	testServer, serverAssertion := test.NewServerWithAssertion(
188
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
189
			w.WriteHeader(http.StatusNotFound)
190
		}),
191
	)
192
	defer testServer.Close()
193
194
	_, _, err = executeCommand(
195
		c,
196
		"scan",
197
		testServer.URL,
198
		"--dictionary",
199
		dictionaryServer.URL,
200
		"--http-timeout",
201
		"300",
202
	)
203
	assert.NoError(t, err)
204
205
	assert.Equal(t, 3, serverAssertion.Len())
206
}
207
208
func TestScanWithUserAgentFlag(t *testing.T) {
209
	const testUserAgent = "my_test_user_agent"
210
211
	logger, loggerBuffer := test.NewLogger()
212
213
	c, err := createCommand(logger)
214
	assert.NoError(t, err)
215
	assert.NotNil(t, c)
216
217
	testServer, serverAssertion := test.NewServerWithAssertion(
218
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
219
			w.WriteHeader(http.StatusNotFound)
220
		}),
221
	)
222
	defer testServer.Close()
223
224
	_, _, err = executeCommand(
225
		c,
226
		"scan",
227
		testServer.URL,
228
		"--user-agent",
229
		testUserAgent,
230
		"--dictionary",
231
		"testdata/dict.txt",
232
		"--http-timeout",
233
		"300",
234
	)
235
	assert.NoError(t, err)
236
237
	assert.Equal(t, 3, serverAssertion.Len())
238
	serverAssertion.Range(func(_ int, r http.Request) {
239
		assert.Equal(t, testUserAgent, r.Header.Get("User-Agent"))
240
	})
241
242
	// to ensure we print the user agent to the cli
243
	assert.Contains(t, loggerBuffer.String(), testUserAgent)
244
}
245
246
func TestScanWithCookies(t *testing.T) {
247
	logger, loggerBuffer := test.NewLogger()
248
249
	c, err := createCommand(logger)
250
	assert.NoError(t, err)
251
	assert.NotNil(t, c)
252
253
	testServer, serverAssertion := test.NewServerWithAssertion(
254
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
255
	)
256
	defer testServer.Close()
257
258
	_, _, err = executeCommand(
259
		c,
260
		"scan",
261
		testServer.URL,
262
		"--cookie",
263
		"name1=val1",
264
		"--cookie",
265
		"name2=val2",
266
		"--dictionary",
267
		"testdata/dict.txt",
268
		"--http-timeout",
269
		"300",
270
	)
271
	assert.NoError(t, err)
272
273
	serverAssertion.Range(func(_ int, r http.Request) {
274
		assert.Equal(t, 2, len(r.Cookies()))
275
276
		assert.Equal(t, r.Cookies()[0].Name, "name1")
277
		assert.Equal(t, r.Cookies()[0].Value, "val1")
278
279
		assert.Equal(t, r.Cookies()[1].Name, "name2")
280
		assert.Equal(t, r.Cookies()[1].Value, "val2")
281
	})
282
283
	// to ensure we print the cookies to the cli
284
	assert.Contains(t, loggerBuffer.String(), "name1=val1")
285
	assert.Contains(t, loggerBuffer.String(), "name2=val2")
286
}
287
288
func TestWhenProvidingCookiesInWrongFormatShouldErr(t *testing.T) {
289
	const malformedCookie = "gibberish"
290
291
	logger, _ := test.NewLogger()
292
293
	c, err := createCommand(logger)
294
	assert.NoError(t, err)
295
	assert.NotNil(t, c)
296
297
	testServer, serverAssertion := test.NewServerWithAssertion(
298
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
299
			w.WriteHeader(http.StatusNotFound)
300
		}),
301
	)
302
	defer testServer.Close()
303
304
	_, _, err = executeCommand(
305
		c,
306
		"scan",
307
		testServer.URL,
308
		"--cookie",
309
		malformedCookie,
310
		"--dictionary",
311
		"testdata/dict.txt",
312
	)
313
	assert.Error(t, err)
314
	assert.Contains(t, err.Error(), "cookie format is invalid")
315
	assert.Contains(t, err.Error(), malformedCookie)
316
317
	assert.Equal(t, 0, serverAssertion.Len())
318
}
319
320
func TestScanWithCookieJar(t *testing.T) {
321
	const (
322
		serverCookieName  = "server_cookie_name"
323
		serverCookieValue = "server_cookie_value"
324
	)
325
326
	logger, _ := test.NewLogger()
327
328
	c, err := createCommand(logger)
329
	assert.NoError(t, err)
330
	assert.NotNil(t, c)
331
332
	once := sync.Once{}
333
	testServer, serverAssertion := test.NewServerWithAssertion(
334
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
335
			once.Do(func() {
336
				http.SetCookie(
337
					w,
338
					&http.Cookie{
339
						Name:    serverCookieName,
340
						Value:   serverCookieValue,
341
						Expires: time.Now().AddDate(0, 1, 0),
342
					},
343
				)
344
			})
345
		}),
346
	)
347
	defer testServer.Close()
348
349
	_, _, err = executeCommand(
350
		c,
351
		"scan",
352
		testServer.URL,
353
		"--use-cookie-jar",
354
		"--dictionary",
355
		"testdata/dict.txt",
356
		"--http-timeout",
357
		"300",
358
		"-t",
359
		"1",
360
	)
361
	assert.NoError(t, err)
362
363
	serverAssertion.Range(func(index int, r http.Request) {
364
		if index == 0 { // first request should have no cookies
365
			assert.Equal(t, 0, len(r.Cookies()))
366
			return
367
		}
368
369
		assert.Equal(t, 1, len(r.Cookies()))
370
		assert.Equal(t, r.Cookies()[0].Name, serverCookieName)
371
		assert.Equal(t, r.Cookies()[0].Value, serverCookieValue)
372
	})
373
}
374
375
func TestScanWithUnknownFlagShouldErr(t *testing.T) {
376
	logger, _ := test.NewLogger()
377
378
	c, err := createCommand(logger)
379
	assert.NoError(t, err)
380
	assert.NotNil(t, c)
381
382
	testServer, serverAssertion := test.NewServerWithAssertion(
383
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
384
	)
385
	defer testServer.Close()
386
387
	_, _, err = executeCommand(
388
		c,
389
		"scan",
390
		testServer.URL,
391
		"--gibberishflag",
392
		"--dictionary",
393
		"testdata/dict.txt",
394
	)
395
	assert.Error(t, err)
396
	assert.Contains(t, err.Error(), "unknown flag")
397
398
	assert.Equal(t, 0, serverAssertion.Len())
399
}
400
401
func TestScanWithHeaders(t *testing.T) {
402
	logger, loggerBuffer := test.NewLogger()
403
404
	c, err := createCommand(logger)
405
	assert.NoError(t, err)
406
	assert.NotNil(t, c)
407
408
	testServer, serverAssertion := test.NewServerWithAssertion(
409
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
410
	)
411
	defer testServer.Close()
412
413
	_, _, err = executeCommand(
414
		c,
415
		"scan",
416
		testServer.URL,
417
		"--header",
418
		"Accept-Language: en-US,en;q=0.5",
419
		"--header",
420
		`"Authorization: Bearer 123"`,
421
		"--dictionary",
422
		"testdata/dict.txt",
423
		"--http-timeout",
424
		"300",
425
	)
426
	assert.NoError(t, err)
427
428
	serverAssertion.Range(func(_ int, r http.Request) {
429
		assert.Equal(t, 2, len(r.Header))
430
431
		assert.Equal(t, "en-US,en;q=0.5", r.Header.Get("Accept-Language"))
432
		assert.Equal(t, "Bearer 123", r.Header.Get("Authorization"))
433
	})
434
435
	// to ensure we print the headers to the cli
436
	assert.Contains(t, loggerBuffer.String(), "Accept-Language")
437
	assert.Contains(t, loggerBuffer.String(), "Authorization")
438
	assert.Contains(t, loggerBuffer.String(), "Bearer 123")
439
}
440
441
func TestScanWithMalformedHeaderShouldErr(t *testing.T) {
442
	const malformedHeader = "gibberish"
443
444
	logger, _ := test.NewLogger()
445
446
	c, err := createCommand(logger)
447
	assert.NoError(t, err)
448
	assert.NotNil(t, c)
449
450
	testServer, serverAssertion := test.NewServerWithAssertion(
451
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
452
	)
453
	defer testServer.Close()
454
455
	_, _, err = executeCommand(
456
		c,
457
		"scan",
458
		testServer.URL,
459
		"--header",
460
		"Accept-Language: en-US,en;q=0.5",
461
		"--header",
462
		malformedHeader,
463
		"--dictionary",
464
		"testdata/dict.txt",
465
	)
466
	assert.Error(t, err)
467
	assert.Contains(t, err.Error(), malformedHeader)
468
	assert.Contains(t, err.Error(), "header is in invalid format")
469
470
	assert.Equal(t, 0, serverAssertion.Len())
471
}
472
473
func TestStartScanWithSocks5ShouldFindResultsWhenAServerIsAvailable(t *testing.T) {
474
	logger, _ := test.NewLogger()
475
476
	c, err := createCommand(logger)
477
	assert.NoError(t, err)
478
	assert.NotNil(t, c)
479
480
	testServer, serverAssertion := test.NewServerWithAssertion(
481
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
482
			w.WriteHeader(http.StatusNotFound)
483
		}),
484
	)
485
	defer testServer.Close()
486
487
	socks5Server := startSocks5TestServer(t)
488
	defer socks5Server.Close()
489
490
	_, _, err = executeCommand(
491
		c,
492
		"scan",
493
		testServer.URL,
494
		"--dictionary",
495
		"testdata/dict.txt",
496
		"-v",
497
		"--http-timeout",
498
		"300",
499
		"--socks5",
500
		socks5TestServerHost,
501
	)
502
	assert.NoError(t, err)
503
504
	assert.Equal(t, 3, serverAssertion.Len())
505
}
506
507
func TestShouldFailToScanWithAnUnreachableSocks5Server(t *testing.T) {
508
	logger, loggerBuffer := test.NewLogger()
509
510
	c, err := createCommand(logger)
511
	assert.NoError(t, err)
512
	assert.NotNil(t, c)
513
514
	testServer, serverAssertion := test.NewServerWithAssertion(
515
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
516
			w.WriteHeader(http.StatusNotFound)
517
		}),
518
	)
519
	defer testServer.Close()
520
521
	socks5Server := startSocks5TestServer(t)
522
	defer socks5Server.Close()
523
524
	_, _, err = executeCommand(
525
		c,
526
		"scan",
527
		testServer.URL,
528
		"--dictionary",
529
		"testdata/dict.txt",
530
		"-v",
531
		"--http-timeout",
532
		"300",
533
		"--socks5",
534
		"127.0.0.1:9555", // invalid
535
	)
536
	assert.NoError(t, err)
537
538
	assert.Equal(t, 0, serverAssertion.Len())
539
	assert.Contains(t, loggerBuffer.String(), "failed to perform request")
540
	assert.Contains(t, loggerBuffer.String(), "socks connect tcp")
541
	assert.Contains(t, loggerBuffer.String(), "connect: connection refused")
542
}
543
544
func TestShouldFailToStartWithAnInvalidSocks5Address(t *testing.T) {
545
	logger, _ := test.NewLogger()
546
547
	c, err := createCommand(logger)
548
	assert.NoError(t, err)
549
	assert.NotNil(t, c)
550
551
	testServer, serverAssertion := test.NewServerWithAssertion(
552
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
553
			w.WriteHeader(http.StatusNotFound)
554
		}),
555
	)
556
	defer testServer.Close()
557
558
	_, _, err = executeCommand(
559
		c,
560
		"scan",
561
		testServer.URL,
562
		"--dictionary",
563
		"testdata/dict.txt",
564
		"-v",
565
		"--http-timeout",
566
		"300",
567
		"--socks5",
568
		"localhost%%2", // invalid
569
	)
570
	assert.Error(t, err)
571
	assert.Contains(t, err.Error(), "invalid URL escape")
572
573
	assert.Equal(t, 0, serverAssertion.Len())
574
}
575
576
func startSocks5TestServer(t *testing.T) net.Listener {
577
	conf := &socks5.Config{}
578
	server, err := socks5.New(conf)
579
	if err != nil {
580
		t.Fatalf("failed to create socks5: %s", err.Error())
581
	}
582
583
	listener, err := net.Listen("tcp", socks5TestServerHost)
584
	if err != nil {
585
		t.Fatalf("failed to create listener: %s", err.Error())
586
	}
587
588
	go func() {
589
		// Create SOCKS5 proxy on localhost port 8000
590
		if err := server.Serve(listener); err != nil {
591
			t.Logf("socks5 stopped serving: %s", err.Error())
592
		}
593
	}()
594
595
	return listener
596
}
597