Passed
Pull Request — master (#52)
by Stefano
02:15
created

cmd.startScan   B

Complexity

Conditions 4

Size

Total Lines 54
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 41
nop 3
dl 0
loc 54
rs 8.896
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
2
3
import (
4
	"fmt"
5
	"net/http"
6
	"net/url"
7
8
	"github.com/pkg/errors"
9
	"github.com/sirupsen/logrus"
10
	"github.com/spf13/cobra"
11
	"github.com/stefanoj3/dirstalk/pkg/dictionary"
12
	"github.com/stefanoj3/dirstalk/pkg/scan"
13
	"github.com/stefanoj3/dirstalk/pkg/scan/client"
14
	"github.com/stefanoj3/dirstalk/pkg/scan/producer"
15
)
16
17
func NewScanCommand(logger *logrus.Logger) (*cobra.Command, error) {
0 ignored issues
show
introduced by
exported function NewScanCommand should have comment or be unexported
Loading history...
18
	cmd := &cobra.Command{
19
		Use:   "scan [url]",
20
		Short: "Scan the given URL",
21
		RunE:  buildScanFunction(logger),
22
	}
23
24
	cmd.Flags().StringP(
25
		flagDictionary,
26
		flagDictionaryShort,
27
		"",
28
		"dictionary to use for the scan (path to local file or remote url)",
29
	)
30
	err := cmd.MarkFlagFilename(flagDictionary)
31
	if err != nil {
32
		return nil, err
33
	}
34
35
	err = cmd.MarkFlagRequired(flagDictionary)
36
	if err != nil {
37
		return nil, err
38
	}
39
40
	cmd.Flags().StringSlice(
41
		flagHTTPMethods,
42
		[]string{"GET"},
43
		"comma separated list of http methods to use; eg: GET,POST,PUT",
44
	)
45
46
	cmd.Flags().IntP(
47
		flagThreads,
48
		flagThreadsShort,
49
		3,
50
		"amount of threads for concurrent requests",
51
	)
52
53
	cmd.Flags().IntP(
54
		flagHTTPTimeout,
55
		"",
56
		5000,
57
		"timeout in milliseconds",
58
	)
59
60
	cmd.Flags().IntP(
61
		flagScanDepth,
62
		"",
63
		3,
64
		"scan depth",
65
	)
66
67
	cmd.Flags().StringP(
68
		flagSocks5Host,
69
		"",
70
		"",
71
		"socks5 host to use",
72
	)
73
74
	cmd.Flags().StringP(
75
		flagUserAgent,
76
		"",
77
		"",
78
		"user agent to use for http requests",
79
	)
80
81
	cmd.Flags().BoolP(
82
		flagCookieJar,
83
		"",
84
		false,
85
		"enables the use of a cookie jar: it will retain any cookie sent "+
86
			"from the server and send them for the following requests",
87
	)
88
89
	cmd.Flags().StringArray(
90
		flagCookie,
91
		[]string{},
92
		"cookie to add to each request; eg name=value (can be specified multiple times)",
93
	)
94
95
	cmd.Flags().StringArray(
96
		flagHeader,
97
		[]string{},
98
		"header to add to each request; eg name=value (can be specified multiple times)",
99
	)
100
101
	return cmd, nil
102
}
103
104
func buildScanFunction(logger *logrus.Logger) func(cmd *cobra.Command, args []string) error {
105
	f := func(cmd *cobra.Command, args []string) error {
106
		u, err := getURL(args)
107
		if err != nil {
108
			return err
109
		}
110
111
		cnf, err := scanConfigFromCmd(cmd)
112
		if err != nil {
113
			return errors.Wrap(err, "failed to build config")
114
		}
115
116
		return startScan(logger, cnf, u)
117
	}
118
119
	return f
120
}
121
122
func getURL(args []string) (*url.URL, error) {
123
	if len(args) == 0 {
124
		return nil, errors.New("no URL provided")
125
	}
126
127
	arg := args[0]
128
129
	u, err := url.ParseRequestURI(arg)
130
	if err != nil {
131
		return nil, errors.Wrap(err, "the first argument must be a valid url")
132
	}
133
134
	return u, nil
135
}
136
137
// startScan is a convenience method that wires together all the dependencies needed to start a scan
138
func startScan(logger *logrus.Logger, cnf *scan.Config, u *url.URL) error {
139
	c, err := client.NewClientFromConfig(
140
		cnf.TimeoutInMilliseconds,
141
		cnf.Socks5Url,
142
		cnf.UserAgent,
143
		cnf.UseCookieJar,
144
		cnf.Cookies,
145
		cnf.Headers,
146
		u,
147
	)
148
	if err != nil {
149
		return errors.Wrap(err, "failed to build client")
150
	}
151
152
	dict, err := dictionary.NewDictionaryFrom(cnf.DictionaryPath, c)
153
	if err != nil {
154
		return errors.Wrap(err, "failed to build dictionary")
155
	}
156
157
	targetProducer := producer.NewDictionaryProducer(cnf.HTTPMethods, dict, cnf.ScanDepth)
158
	reproducer := producer.NewReProducer(targetProducer)
159
160
	s := scan.NewScanner(
161
		c,
162
		targetProducer,
163
		reproducer,
164
		logger,
165
	)
166
167
	logger.WithFields(logrus.Fields{
168
		"url":               u.String(),
169
		"threads":           cnf.Threads,
170
		"dictionary-length": len(dict),
171
		"scan-depth":        cnf.ScanDepth,
172
		"timeout":           cnf.TimeoutInMilliseconds,
173
		"socks5":            cnf.Socks5Url,
174
		"cookies":           strigifyCookies(cnf.Cookies),
175
		"cookie-jar":        cnf.UseCookieJar,
176
		"headers":           stringyfyHeaders(cnf.Headers),
177
		"user-agent":        cnf.UserAgent,
178
	}).Info("Starting scan")
179
180
	resultLogger := scan.NewResultLogger(logger)
181
	summarizer := scan.NewResultSummarizer(logger.Out)
182
	for result := range s.Scan(u, cnf.Threads) {
183
		resultLogger.Log(result)
184
		summarizer.Add(result)
185
	}
186
187
	summarizer.Summarize()
188
189
	logger.Info("Finished scan")
190
191
	return nil
192
}
193
194
func strigifyCookies(cookies []*http.Cookie) string {
195
	result := ""
196
197
	for _, cookie := range cookies {
198
		result += fmt.Sprintf("{%s=%s}", cookie.Name, cookie.Value)
199
	}
200
201
	return result
202
}
203
204
func stringyfyHeaders(headers map[string]string) string {
205
	result := ""
206
207
	for name, value := range headers {
208
		result += fmt.Sprintf("{%s:%s}", name, value)
209
	}
210
211
	return result
212
}
213