summaryrefslogtreecommitdiff
path: root/analytics/analyzer.go
blob: ac8ff03129ab71e4b1414ebe19ac216550fc8cd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package analytics

import (
	"cmp"
	"slices"
	"strings"

	"github.com/x-way/crawlerdetect"
	"src.eltr.ac/eltrac/audidi/data"
)

type AnalyticsResult struct {
	Client string
	// 0 = Human
	// 1 = Human operating non-browser tools
	// 2 = Automated crawler
	// 3 = Malicious crawler
	BotIndex float32
}

func Analyze(log data.LogEntry) *AnalyticsResult {
	uaResult := analyzeUA(log.UserAgent)
	ja4Client, ja4Result := analyzeJA4(log.JA4)
	matchScore := analyzeClientMatch(log.UserAgent, ja4Client)
	ASNScore := analyzeASN(log.ASN)

	return &AnalyticsResult{
		Client:   cmp.Or(ja4Client, log.UserAgent),
		BotIndex: (uaResult + ja4Result + matchScore + ASNScore) / 4,
	}
}

func analyzeUA(ua string) float32 {
	if crawlerdetect.IsCrawler(ua) {
		return 2
	} else {
		return 0
	}
}

func analyzeJA4(ja4 string) (client string, botIndex float32) {
	client, clientType := LookupClientByJA4(ja4)

	return client, float32(clientType)
}

func analyzeClientMatch(ua string, ja4client string) float32 {
	uaClient := strings.ToLower(ua)
	ja4Client := strings.ToLower(ja4client)

	if strings.Contains(uaClient, ja4Client) {
		return 0
	} else {
		return 3
	}
}

func analyzeASN(asn int) float32 {
	DataCenterASNs := []int{
		16509, 14618, 8987, 38895, 8075, 8068, 12076, 15169, 396982, 19527, 31898, 36351,
		37963, 45102, 24429, 132203, 45090, 55990, 136907, 38365,
		13335, 20940, 16625, 21342, 63949, 54113, 60068, 212238, 15133, 19551,
		14061, 20473, 16276, 24940, 51167, 12876, 60781, 28753, 9009,
		53667, 49505, 9123, 197540, 8560, 6724, 210644, 55293,
		26496, 46606, 32244, 26347, 33070, 19994, 22612, 19871, 2635,
	}
	if slices.Contains(DataCenterASNs, asn) {
		return 2
	}
	return 0
}