Skip to content

Commit

Permalink
Add notify threshold for app related notification
Browse files Browse the repository at this point in the history
  • Loading branch information
dhaavi committed Mar 30, 2022
1 parent bff3dc8 commit 7d1f7a0
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 4 deletions.
3 changes: 3 additions & 0 deletions compat/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ func start() error {
MaxDelay(selfcheckTaskRetryAfter).
Schedule(time.Now().Add(selfcheckTaskRetryAfter))

module.NewTask("clean notify thresholds", cleanNotifyThreshold).
Repeat(10 * time.Minute)

return module.RegisterEventHook(
netenv.ModuleName,
netenv.NetworkChangedEvent,
Expand Down
65 changes: 61 additions & 4 deletions compat/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
"github.com/safing/portbase/notifications"
"github.com/safing/portmaster/process"
"github.com/safing/portmaster/profile"
Expand Down Expand Up @@ -124,17 +125,22 @@ func (issue *appIssue) notify(proc *process.Process) {
proc.Path,
)

// Build message.
message := strings.ReplaceAll(issue.message, "[APPNAME]", p.Name)

// Check if we already have this notification.
eventID := fmt.Sprintf(issue.id, p.ID)
n := notifications.Get(eventID)
if n != nil {
return
}

// Otherwise, create a new one.
// Check if we reach the threshold to actually send a notification.
if !isOverThreshold(eventID) {
return
}

// Build message.
message := strings.ReplaceAll(issue.message, "[APPNAME]", p.Name)

// Create a new notification.
n = &notifications.Notification{
EventID: eventID,
Type: issue.level,
Expand Down Expand Up @@ -171,3 +177,54 @@ func (issue *appIssue) notify(proc *process.Process) {
return nil
})
}

const (
notifyThresholdMinIncidents = 11
notifyThresholdResetAfter = 2 * time.Minute
)

var (
notifyThresholds = make(map[string]*notifyThreshold)
notifyThresholdsLock sync.Mutex
)

type notifyThreshold struct {
FirstSeen time.Time
Incidents uint
}

func (nt *notifyThreshold) expired() bool {
return time.Now().Add(-notifyThresholdResetAfter).After(nt.FirstSeen)
}

func isOverThreshold(id string) bool {
notifyThresholdsLock.Lock()
defer notifyThresholdsLock.Unlock()

// Get notify threshold and check if we reach the minimum incidents.
nt, ok := notifyThresholds[id]
if ok && !nt.expired() {
nt.Incidents++
return nt.Incidents >= notifyThresholdMinIncidents
}

// Add new entry.
notifyThresholds[id] = &notifyThreshold{
FirstSeen: time.Now(),
Incidents: 1,
}
return false
}

func cleanNotifyThreshold(ctx context.Context, task *modules.Task) error {
notifyThresholdsLock.Lock()
defer notifyThresholdsLock.Unlock()

for id, nt := range notifyThresholds {
if nt.expired() {
delete(notifyThresholds, id)
}
}

return nil
}

0 comments on commit 7d1f7a0

Please sign in to comment.