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 }