forked from nathanielc/morgoth
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdetector.go
More file actions
94 lines (82 loc) · 2.64 KB
/
Copy pathdetector.go
File metadata and controls
94 lines (82 loc) · 2.64 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
package morgoth
import (
"errors"
"log"
"github.com/nathanielc/morgoth/counter"
)
type Detector struct {
consensus float64
minSupport float64
errorTolerance float64
counters []fingerprinterCounter
Stats DetectorStats
}
type DetectorStats struct {
WindowCount uint64
DataPointCount uint64
AnomalousCount uint64
}
// Pair of fingerprinter and counter
type fingerprinterCounter struct {
fingerprinter Fingerprinter
counter counter.Counter
}
// Create a new Lossy couting based detector
// The consensus is a percentage of the fingerprinters that must agree in order to flag a window as anomalous.
// If the consensus is -1 then the average support from each fingerprinter is compared to minSupport instead of using a consensus.
// The minSupport defines a minimum frequency as a percentage for a window to be considered normal.
// The errorTolerance defines a frequency as a precentage for the smallest frequency that will be retained in memory.
// The errorTolerance must be less than the minSupport.
func NewDetector(consensus, minSupport, errorTolerance float64, fingerprinters []Fingerprinter) (*Detector, error) {
if (consensus != -1 && consensus < 0) || consensus > 1 {
return nil, errors.New("consensus must be in the range [0,1) or equal to -1")
}
if minSupport <= errorTolerance {
return nil, errors.New("minSupport must be greater than errorTolerance")
}
counters := make([]fingerprinterCounter, len(fingerprinters))
for i, fingerprinter := range fingerprinters {
counters[i] = fingerprinterCounter{
fingerprinter,
counter.NewLossyCounter(errorTolerance),
}
}
return &Detector{
consensus: consensus,
minSupport: minSupport,
errorTolerance: errorTolerance,
counters: counters,
}, nil
}
// Determine if the window is anomalous
func (self *Detector) IsAnomalous(window *Window) (bool, float64) {
self.Stats.WindowCount++
self.Stats.DataPointCount += uint64(len(window.Data))
vote := 0.0
avgSupport := 0.0
n := 0.0
for _, fc := range self.counters {
fingerprint := fc.fingerprinter.Fingerprint(window.Copy())
support := fc.counter.Count(fingerprint)
anomalous := support <= self.minSupport
if anomalous {
vote++
}
log.Printf("D! %T anomalous? %v support: %f", fc.fingerprinter, anomalous, support)
avgSupport = ((avgSupport * n) + support) / (n + 1)
n++
}
anomalous := false
if self.consensus != -1 {
// Use voting consensus
vote /= float64(len(self.counters))
anomalous = vote >= self.consensus
} else {
// Use average suppport
anomalous = avgSupport <= self.minSupport
}
if anomalous {
self.Stats.AnomalousCount++
}
return anomalous, avgSupport
}