Passed
Push — master ( fc31a0...e87dc6 )
by Stefano
02:12
created

summarizer.*ResultSummarizer.Summarize   A

Complexity

Conditions 3

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 15
nop 0
dl 0
loc 19
rs 9.65
c 0
b 0
f 0
1
package summarizer
2
3
import (
4
	"fmt"
5
	"net/http"
6
	"sort"
7
	"strings"
8
	"sync"
9
10
	"github.com/sirupsen/logrus"
11
	"github.com/stefanoj3/dirstalk/pkg/scan"
12
13
	gotree "github.com/DiSiqueira/GoTree"
14
)
15
16
const (
17
	breakingText = "Found something breaking"
18
	foundText    = "Found"
19
	notFoundText = "Not found"
20
)
21
22
func NewResultSummarizer(logger *logrus.Logger) *ResultSummarizer {
0 ignored issues
show
introduced by
exported function NewResultSummarizer should have comment or be unexported
Loading history...
23
	return &ResultSummarizer{logger: logger, resultMap: make(map[string]struct{})}
24
}
25
26
type ResultSummarizer struct {
0 ignored issues
show
introduced by
exported type ResultSummarizer should have comment or be unexported
Loading history...
27
	logger          *logrus.Logger
28
	results         []scan.Result
29
	resultMap       map[string]struct{}
30
	resultsReceived int
31
	mux             sync.RWMutex
32
}
33
34
func (s *ResultSummarizer) Add(result scan.Result) {
0 ignored issues
show
introduced by
exported method ResultSummarizer.Add should have comment or be unexported
Loading history...
35
	s.mux.Lock()
36
	defer s.mux.Unlock()
37
38
	s.resultsReceived++
39
40
	key := keyForResult(result)
41
	_, found := s.resultMap[key]
42
	if found {
43
		return
44
	}
45
46
	s.log(result)
47
	if result.StatusCode == http.StatusNotFound {
48
		return
49
	}
50
51
	s.resultMap[key] = struct{}{}
52
53
	s.results = append(s.results, result)
54
}
55
56
func (s *ResultSummarizer) Summarize() {
0 ignored issues
show
introduced by
exported method ResultSummarizer.Summarize should have comment or be unexported
Loading history...
57
	s.mux.Lock()
58
	defer s.mux.Unlock()
59
60
	sort.Slice(s.results, func(i, j int) bool {
61
		return s.results[i].Target.Path < s.results[j].Target.Path
62
	})
63
64
	s.printSummary()
65
	s.printTree()
66
67
	for _, r := range s.results {
68
		fmt.Fprintln(
69
			s.logger.Out,
70
			fmt.Sprintf(
71
				"%s [%d] [%s]",
72
				r.URL.String(),
73
				r.StatusCode,
74
				r.Target.Method,
75
			),
76
		)
77
	}
78
}
79
80
func (s *ResultSummarizer) printSummary() {
81
	fmt.Fprintln(
82
		s.logger.Out,
83
		fmt.Sprintf("%d requests made, %d results found", s.resultsReceived, len(s.results)),
84
	)
85
}
86
87
func (s *ResultSummarizer) printTree() {
88
	root := gotree.New("/")
89
90
	// TODO: improve efficiency
91
	for _, r := range s.results {
92
		currentBranch := root
93
94
		parts := strings.Split(r.URL.Path, "/")
95
		for _, p := range parts {
96
			if len(p) == 0 {
97
				continue
98
			}
99
100
			found := false
101
102
			for _, item := range currentBranch.Items() {
103
				if item.Text() != p {
104
					continue
105
				}
106
107
				currentBranch = item
108
				found = true
109
				break
110
			}
111
112
			if found {
113
				continue
114
			}
115
116
			newTree := gotree.New(p)
117
			currentBranch.AddTree(newTree)
118
			currentBranch = newTree
119
		}
120
	}
121
122
	fmt.Fprintln(s.logger.Out, root.Print())
123
}
124
125
func (s *ResultSummarizer) log(result scan.Result) {
126
	statusCode := result.StatusCode
127
128
	l := s.logger.WithFields(logrus.Fields{
129
		"status-code": statusCode,
130
		"method":      result.Target.Method,
131
		"url":         result.URL.String(),
132
	})
133
134
	if statusCode == http.StatusNotFound {
135
		l.Debug(notFoundText)
136
	} else if statusCode >= http.StatusInternalServerError {
137
		l.Warn(breakingText)
138
	} else {
139
		l.Info(foundText)
140
	}
141
}
142
143
func keyForResult(result scan.Result) string {
144
	return fmt.Sprintf("%s~%s", result.URL.String(), result.Target.Method)
145
}
146