Passed
Push — master ( 2ef00d...5ef32b )
by Stefano
02:10
created

pkg/scan/bootstrap_integration_test.go   A

Size/Duplication

Total Lines 374
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 40
eloc 247
dl 0
loc 374
rs 9.2
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A scan_test.TestStartScanWithSocks5ShouldFindResultsWhenAServerIsAvailable 0 43 3
B scan_test.TestStartScan 0 62 3
A scan_test.startSocks5TestServer 0 20 5
A scan_test.TestShouldFailToScanWithAnUnreachableSocks5Server 0 46 4
A scan_test.methodAndPathToString 0 2 1
A scan_test.TestShouldNotSendAnyCookieIfServerSetNoneWhenUsingCookieJar 0 26 3
B scan_test.buildTestServer 0 16 6
A scan_test.hasRequest 0 3 1
A scan_test.TestShouldUseTheSpecifiedUserAgent 0 29 3
A scan_test.assertTargetsContains 0 8 3
A scan_test.assertRequest 0 16 2
A scan_test.storeRequest 0 2 1
B scan_test.TestShouldRetainCookiesSetByTheServerWhenCookieJarIsEnabled 0 49 5
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 assertTargetsContains(t *testing.T, target scan.Target, results []scan.Target) {
293
	for _, actualResult := range results {
294
		if target == actualResult {
295
			return
296
		}
297
	}
298
299
	t.Errorf("Target %v not found in %v", target, results)
300
}
301
302
func assertRequest(t *testing.T, expected bool, method, path string, requestMap *sync.Map) {
303
	if expected {
304
		assert.True(
305
			t,
306
			hasRequest(requestMap, method, path),
307
			"expected request for `%s %s`, none received",
308
			method,
309
			path,
310
		)
311
	} else {
312
		assert.False(
313
			t,
314
			hasRequest(requestMap, method, path),
315
			"no request was expected for `%s %s`",
316
			method,
317
			path,
318
		)
319
	}
320
}
321
322
func buildTestServer(requestMap *sync.Map) *httptest.Server {
323
	return httptest.NewServer(
324
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
325
			storeRequest(requestMap, r.Method, r.URL.Path)
326
327
			if r.Method == http.MethodGet && r.URL.Path == "/home" {
328
				w.WriteHeader(200)
329
				return
330
			}
331
332
			if r.Method == http.MethodGet && r.URL.Path == "/home/about" {
333
				w.WriteHeader(200)
334
				return
335
			}
336
337
			w.WriteHeader(http.StatusNotFound)
338
		}),
339
	)
340
}
341
342
func startSocks5TestServer(t *testing.T) net.Listener {
343
	conf := &socks5.Config{}
344
	server, err := socks5.New(conf)
345
	if err != nil {
346
		t.Fatalf("failed to create socks5: %s", err.Error())
347
	}
348
349
	listener, err := net.Listen("tcp", socks5TestServerHost)
350
	if err != nil {
351
		t.Fatalf("failed to create listener: %s", err.Error())
352
	}
353
354
	go func() {
355
		// Create SOCKS5 proxy on localhost port 8000
356
		if err := server.Serve(listener); err != nil {
357
			t.Logf("socks5 stopped serving: %s", err.Error())
358
		}
359
	}()
360
361
	return listener
362
}
363
364
func storeRequest(requestMap *sync.Map, method, path string) {
365
	requestMap.Store(methodAndPathToString(method, path), true)
366
}
367
368
func hasRequest(requestMap *sync.Map, method, path string) bool {
369
	_, ok := requestMap.Load(methodAndPathToString(method, path))
370
	return ok
371
}
372
373
func methodAndPathToString(method, path string) string {
374
	return fmt.Sprintf("%s_%s", method, path)
375
}
376