Passed
Pull Request — master (#40)
by Stefano
03:01
created

scan_test.TestShouldSendCookiesWhenSpecified   A

Complexity

Conditions 3

Size

Total Lines 46
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 32
nop 1
dl 0
loc 46
rs 9.112
c 0
b 0
f 0
1
package scan_test
2
3
import (
4
	"fmt"
5
	"net"
6
	"net/http"
7
	"net/http/httptest"
8
	"net/url"
9
	"sync"
10
	"testing"
11
	"time"
12
13
	"github.com/armon/go-socks5"
14
	"github.com/chuckpreslar/emission"
15
	"github.com/stefanoj3/dirstalk/pkg/common/test"
16
	"github.com/stefanoj3/dirstalk/pkg/scan"
17
	"github.com/stretchr/testify/assert"
18
)
19
20
const socks5TestServerHost = "127.0.0.1:8899"
21
22
func TestStartScan(t *testing.T) {
23
	logger, _ := test.NewLogger()
24
25
	requestMap := &sync.Map{}
26
	testServer := buildTestServer(requestMap)
27
	defer testServer.Close()
28
29
	u, err := url.Parse(testServer.URL)
30
	assert.NoError(t, err)
31
32
	config := &scan.Config{
33
		Threads:               3,
34
		DictionaryPath:        "testdata/dictionary1.txt",
35
		HTTPMethods:           []string{http.MethodGet},
36
		TimeoutInMilliseconds: 50,
37
		ScanDepth:             3,
38
	}
39
40
	actualResults := make([]scan.Target, 0, 2)
41
	mx := sync.Mutex{}
42
	eventManager := emission.NewEmitter()
43
	eventManager.On(scan.EventResultFound, func(r *scan.Result) {
44
		if r.Response.StatusCode != 200 {
45
			return
46
		}
47
48
		mx.Lock()
49
		defer mx.Unlock()
50
		actualResults = append(actualResults, r.Target)
51
	})
52
53
	err = scan.StartScan(logger, eventManager, config, u)
54
	assert.NoError(t, err)
55
56
	// Asserting which requests are made to the remote service
57
	assertRequest(t, true, http.MethodGet, "/home", requestMap)
58
	assertRequest(t, true, http.MethodGet, "/about", requestMap)
59
	assertRequest(t, true, http.MethodGet, "/home/home", requestMap)
60
	assertRequest(t, true, http.MethodGet, "/home/about", requestMap)
61
	assertRequest(t, true, http.MethodGet, "/home/about/home", requestMap)
62
	assertRequest(t, true, http.MethodGet, "/home/about/about", requestMap)
63
64
	assertRequest(t, false, http.MethodDelete, "/home", requestMap)
65
	assertRequest(t, false, http.MethodDelete, "/about", requestMap)
66
67
	assertRequest(t, false, http.MethodPatch, "/home", requestMap)
68
	assertRequest(t, false, http.MethodPatch, "/about", requestMap)
69
70
	assertRequest(t, false, http.MethodPost, "/home", requestMap)
71
	assertRequest(t, false, http.MethodPost, "/about", requestMap)
72
73
	assertRequest(t, false, http.MethodPut, "/home", requestMap)
74
	assertRequest(t, false, http.MethodPut, "/about", requestMap)
75
76
	assertRequest(t, false, http.MethodGet, "/about/home", requestMap)
77
	assertRequest(t, false, http.MethodGet, "/about/about", requestMap)
78
	// -----------------------------------------------------------
79
80
	// Asserting on the actual results found - considering only 200s for this test
81
	assert.Len(t, actualResults, 2)
82
	assertTargetsContains(t, scan.Target{Depth: 3, Path: "/home", Method: http.MethodGet}, actualResults)
83
	assertTargetsContains(t, scan.Target{Depth: 2, Path: "/home/about", Method: http.MethodGet}, actualResults)
84
}
85
86
func TestStartScanWithSocks5ShouldFindResultsWhenAServerIsAvailable(t *testing.T) {
87
	logger, _ := test.NewLogger()
88
89
	testServer := buildTestServer(&sync.Map{})
90
	defer testServer.Close()
91
92
	u, err := url.Parse(testServer.URL)
93
	assert.NoError(t, err)
94
95
	socks5URL, err := url.Parse("socks5://" + socks5TestServerHost)
96
	assert.NoError(t, err)
97
98
	config := &scan.Config{
99
		Threads:               3,
100
		DictionaryPath:        "testdata/dictionary1.txt",
101
		HTTPMethods:           []string{http.MethodGet},
102
		TimeoutInMilliseconds: 50,
103
		ScanDepth:             3,
104
		Socks5Url:             socks5URL,
105
	}
106
107
	listener := startSocks5TestServer(t)
108
	defer listener.Close()
109
110
	actualResults := make([]scan.Target, 0, 2)
111
	mx := sync.Mutex{}
112
	eventManager := emission.NewEmitter()
113
	eventManager.On(scan.EventResultFound, func(r *scan.Result) {
114
		if r.Response.StatusCode != 200 {
115
			return
116
		}
117
118
		mx.Lock()
119
		defer mx.Unlock()
120
		actualResults = append(actualResults, r.Target)
121
	})
122
123
	err = scan.StartScan(logger, eventManager, config, u)
124
	assert.NoError(t, err)
125
126
	assert.Len(t, actualResults, 2)
127
	assertTargetsContains(t, scan.Target{Depth: 3, Path: "/home", Method: http.MethodGet}, actualResults)
128
	assertTargetsContains(t, scan.Target{Depth: 2, Path: "/home/about", Method: http.MethodGet}, actualResults)
129
}
130
131
func TestShouldUseTheSpecifiedUserAgent(t *testing.T) {
132
	const testUserAgent = "my_test_user_agent"
133
134
	logger, _ := test.NewLogger()
135
136
	testServer, serverAssertion := test.NewServerWithAssertion(
137
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
138
	)
139
	defer testServer.Close()
140
141
	u, err := url.Parse(testServer.URL)
142
	assert.NoError(t, err)
143
144
	config := &scan.Config{
145
		Threads:               3,
146
		DictionaryPath:        "testdata/one_element_dictionary.txt",
147
		HTTPMethods:           []string{http.MethodGet},
148
		TimeoutInMilliseconds: 50,
149
		ScanDepth:             3,
150
		UserAgent:             testUserAgent,
151
	}
152
153
	eventManager := emission.NewEmitter()
154
	err = scan.StartScan(logger, eventManager, config, u)
155
	assert.NoError(t, err)
156
157
	assert.True(t, serverAssertion.Len() > 0)
158
	serverAssertion.Range(func(_ int, r http.Request) {
159
		assert.Equal(t, testUserAgent, r.Header.Get("User-Agent"))
160
	})
161
}
162
163
func TestShouldFailToScanWithAnUnreachableSocks5Server(t *testing.T) {
164
	logger, loggerBuffer := test.NewLogger()
165
166
	testServer, serverAssertion := test.NewServerWithAssertion(
167
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
168
	)
169
	defer testServer.Close()
170
171
	u, err := url.Parse(testServer.URL)
172
	assert.NoError(t, err)
173
174
	socks5URL, err := url.Parse("socks5://127.0.0.1:12345")
175
	assert.NoError(t, err)
176
177
	config := &scan.Config{
178
		Threads:               3,
179
		DictionaryPath:        "testdata/dictionary2.txt",
180
		HTTPMethods:           []string{http.MethodGet},
181
		TimeoutInMilliseconds: 50,
182
		ScanDepth:             3,
183
		Socks5Url:             socks5URL,
184
	}
185
186
	listener := startSocks5TestServer(t)
187
	defer listener.Close()
188
189
	actualResults := make([]scan.Target, 0, 2)
190
	mx := sync.Mutex{}
191
	eventManager := emission.NewEmitter()
192
	eventManager.On(scan.EventResultFound, func(r *scan.Result) {
193
		if r.Response.StatusCode != 200 {
194
			return
195
		}
196
197
		mx.Lock()
198
		defer mx.Unlock()
199
		actualResults = append(actualResults, r.Target)
200
	})
201
202
	err = scan.StartScan(logger, eventManager, config, u)
203
	assert.NoError(t, err)
204
205
	assert.Len(t, actualResults, 0)
206
	assert.Contains(t, loggerBuffer.String(), "connection refused")
207
208
	assert.True(t, serverAssertion.Len() == 0)
209
}
210
211
func TestShouldRetainCookiesSetByTheServerWhenCookieJarIsEnabled(t *testing.T) {
212
	const (
213
		cookieName  = "my_cookies_name"
214
		cookieValue = "my_cookie_value_123"
215
	)
216
	logger, _ := test.NewLogger()
217
218
	once := sync.Once{}
219
	testServer, serverAssertion := test.NewServerWithAssertion(
220
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
221
			once.Do(func() {
222
				http.SetCookie(
223
					w,
224
					&http.Cookie{
225
						Name:    cookieName,
226
						Value:   cookieValue,
227
						Expires: time.Now().AddDate(0, 1, 0),
228
					},
229
				)
230
			})
231
		}),
232
	)
233
234
	u, err := url.Parse(testServer.URL)
235
	assert.NoError(t, err)
236
237
	config := &scan.Config{
238
		Threads:               1,
239
		DictionaryPath:        "testdata/dictionary1.txt",
240
		HTTPMethods:           []string{http.MethodGet},
241
		TimeoutInMilliseconds: 400,
242
		ScanDepth:             2,
243
		UseCookieJar:          true,
244
	}
245
	eventManager := emission.NewEmitter()
246
247
	err = scan.StartScan(logger, eventManager, config, u)
248
	assert.NoError(t, err)
249
250
	assert.Equal(t, 14, serverAssertion.Len())
251
	serverAssertion.Range(func(index int, r http.Request) {
252
		if index == 0 { // the first request should have no cookies
253
			assert.Equal(t, 0, len(r.Cookies()))
254
			return
255
		}
256
257
		assert.Equal(t, 1, len(r.Cookies()), "Only one cookie expected, got: %v", r.Cookies())
258
		assert.Equal(t, cookieName, r.Cookies()[0].Name)
259
		assert.Equal(t, cookieValue, r.Cookies()[0].Value)
260
	})
261
}
262
263
func TestShouldNotSendAnyCookieIfServerSetNoneWhenUsingCookieJar(t *testing.T) {
264
	logger, _ := test.NewLogger()
265
266
	testServer, serverAssertion := test.NewServerWithAssertion(
267
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
268
	)
269
270
	u, err := url.Parse(testServer.URL)
271
	assert.NoError(t, err)
272
273
	config := &scan.Config{
274
		Threads:               1,
275
		DictionaryPath:        "testdata/dictionary1.txt",
276
		HTTPMethods:           []string{http.MethodGet},
277
		TimeoutInMilliseconds: 400,
278
		ScanDepth:             2,
279
		UseCookieJar:          true,
280
	}
281
	eventManager := emission.NewEmitter()
282
283
	err = scan.StartScan(logger, eventManager, config, u)
284
	assert.NoError(t, err)
285
286
	assert.Equal(t, 14, serverAssertion.Len())
287
	serverAssertion.Range(func(index int, r http.Request) {
288
		assert.Equal(t, 0, len(r.Cookies()), "No cookies expected, got: %v", r.Cookies())
289
	})
290
}
291
292
func TestShouldSendCookiesWhenSpecified(t *testing.T) {
293
	logger, _ := test.NewLogger()
294
295
	testServer, serverAssertion := test.NewServerWithAssertion(
296
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
297
	)
298
299
	u, err := url.Parse(testServer.URL)
300
	assert.NoError(t, err)
301
302
	cookies := []*http.Cookie{
303
		{
304
			Name:    "name1",
305
			Value:   "value1",
306
			Expires: time.Now().AddDate(0, 0, 2),
307
		},
308
		{
309
			Name:    "name2",
310
			Value:   "value2",
311
			Expires: time.Now().AddDate(0, 0, 2),
312
		},
313
	}
314
315
	config := &scan.Config{
316
		Threads:               3,
317
		DictionaryPath:        "testdata/dictionary1.txt",
318
		HTTPMethods:           []string{http.MethodGet},
319
		TimeoutInMilliseconds: 400,
320
		ScanDepth:             2,
321
		UseCookieJar:          false,
322
		Cookies:               cookies,
323
	}
324
	eventManager := emission.NewEmitter()
325
326
	err = scan.StartScan(logger, eventManager, config, u)
327
	assert.NoError(t, err)
328
329
	assert.True(t, serverAssertion.Len() > 0)
330
	serverAssertion.Range(func(index int, r http.Request) {
331
		assert.Equal(t, 2, len(r.Cookies()), "Expecting two cookies, got: %v", r.Cookies())
332
333
		assert.Equal(t, r.Cookies()[0].Name, cookies[0].Name)
334
		assert.Equal(t, r.Cookies()[0].Value, cookies[0].Value)
335
336
		assert.Equal(t, r.Cookies()[1].Name, cookies[1].Name)
337
		assert.Equal(t, r.Cookies()[1].Value, cookies[1].Value)
338
	})
339
}
340
341
func assertTargetsContains(t *testing.T, target scan.Target, results []scan.Target) {
342
	for _, actualResult := range results {
343
		if target == actualResult {
344
			return
345
		}
346
	}
347
348
	t.Errorf("Target %v not found in %v", target, results)
349
}
350
351
func assertRequest(t *testing.T, expected bool, method, path string, requestMap *sync.Map) {
352
	if expected {
353
		assert.True(
354
			t,
355
			hasRequest(requestMap, method, path),
356
			"expected request for `%s %s`, none received",
357
			method,
358
			path,
359
		)
360
	} else {
361
		assert.False(
362
			t,
363
			hasRequest(requestMap, method, path),
364
			"no request was expected for `%s %s`",
365
			method,
366
			path,
367
		)
368
	}
369
}
370
371
func buildTestServer(requestMap *sync.Map) *httptest.Server {
372
	return httptest.NewServer(
373
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
374
			storeRequest(requestMap, r.Method, r.URL.Path)
375
376
			if r.Method == http.MethodGet && r.URL.Path == "/home" {
377
				w.WriteHeader(200)
378
				return
379
			}
380
381
			if r.Method == http.MethodGet && r.URL.Path == "/home/about" {
382
				w.WriteHeader(200)
383
				return
384
			}
385
386
			w.WriteHeader(http.StatusNotFound)
387
		}),
388
	)
389
}
390
391
func startSocks5TestServer(t *testing.T) net.Listener {
392
	conf := &socks5.Config{}
393
	server, err := socks5.New(conf)
394
	if err != nil {
395
		t.Fatalf("failed to create socks5: %s", err.Error())
396
	}
397
398
	listener, err := net.Listen("tcp", socks5TestServerHost)
399
	if err != nil {
400
		t.Fatalf("failed to create listener: %s", err.Error())
401
	}
402
403
	go func() {
404
		// Create SOCKS5 proxy on localhost port 8000
405
		if err := server.Serve(listener); err != nil {
406
			t.Logf("socks5 stopped serving: %s", err.Error())
407
		}
408
	}()
409
410
	return listener
411
}
412
413
func storeRequest(requestMap *sync.Map, method, path string) {
414
	requestMap.Store(methodAndPathToString(method, path), true)
415
}
416
417
func hasRequest(requestMap *sync.Map, method, path string) bool {
418
	_, ok := requestMap.Load(methodAndPathToString(method, path))
419
	return ok
420
}
421
422
func methodAndPathToString(method, path string) string {
423
	return fmt.Sprintf("%s_%s", method, path)
424
}
425