Passed
Push — master ( e87dc6...829c59 )
by Stefano
02:13
created

cmd_test.TestScanCommand   B

Complexity

Conditions 6

Size

Total Lines 82
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

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