forked from Nextron-Labs/thunderstorm-stub-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscanner.go
More file actions
98 lines (86 loc) · 2.34 KB
/
scanner.go
File metadata and controls
98 lines (86 loc) · 2.34 KB
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package main
import (
"math"
"sort"
)
// StringMatch is a single string match within a YARA rule match.
type StringMatch struct {
Identifier string
Offset int
Data []byte
}
// Match represents one YARA rule that fired.
type Match struct {
RuleName string
Tags []string
Score int // from YARA meta: score (default 75 per THOR manual)
Description string
Author string
Strings []StringMatch
}
// ScanResult holds all YARA matches for a single file.
type ScanResult struct {
Matches []Match
StubMode bool // true when running without real YARA
}
// Scanner is the interface for YARA scanning.
type Scanner interface {
Scan(data []byte) (ScanResult, error)
IsStub() bool
YARAVersion() string
}
// StubScanner always returns no matches. Used when YARA is not compiled in.
type StubScanner struct{}
func (StubScanner) Scan(_ []byte) (ScanResult, error) {
return ScanResult{StubMode: true}, nil
}
func (StubScanner) IsStub() bool { return true }
func (StubScanner) YARAVersion() string { return "unavailable (stub mode)" }
const defaultRuleScore = 75
// AccumulateScores combines YARA sub-scores using the THOR accumulation formula:
//
// total = 100 × (1 − ∏ (1 − s_i/100 / 2^i))
//
// scores must be sorted descending (highest first). Result is capped at 100.
// Single score returns that score unchanged.
// Reference: THOR manual, "Scoring" section.
func AccumulateScores(scores []int) int {
if len(scores) == 0 {
return 0
}
sorted := make([]int, len(scores))
copy(sorted, scores)
sort.Sort(sort.Reverse(sort.IntSlice(sorted)))
product := 1.0
for i, s := range sorted {
product *= 1.0 - float64(s)/100.0/math.Pow(2, float64(i))
}
result := int(math.Round(100.0 * (1.0 - product)))
if result > 100 {
result = 100
}
return result
}
// ScoreToLevel maps a total score and its sub-scores to a THOR severity level.
// Per THOR manual:
// - > 80 AND any subscore > 75 → Alert
// - ≥ 60 → Warning
// - ≥ 40 → Notice
// - < 40 → Info
func ScoreToLevel(totalScore int, subScores []int) string {
if totalScore > 80 {
for _, s := range subScores {
if s > 75 {
return "Alert"
}
}
return "Warning"
}
if totalScore >= 60 {
return "Warning"
}
if totalScore >= 40 {
return "Notice"
}
return "Info"
}