mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Add audit system with sink based dispatcher (#211)
* chore: Dependency update * feat: Add audit system with eventlog as a sink * fix: Linter fixes * fix: Code review fixes
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safedep/dry/log"
|
||||
)
|
||||
|
||||
type session struct {
|
||||
mu sync.Mutex
|
||||
startTime time.Time
|
||||
packageManager string
|
||||
args []string
|
||||
totalAnalyzed uint32
|
||||
allowedCount uint32
|
||||
blockedCount uint32
|
||||
confirmedCount uint32
|
||||
trustedSkipped uint32
|
||||
insecureBypassed uint32
|
||||
}
|
||||
|
||||
type auditor struct {
|
||||
sinks []Sink
|
||||
session *session
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func newAuditor(sinks ...Sink) *auditor {
|
||||
return &auditor{sinks: sinks}
|
||||
}
|
||||
|
||||
func (a *auditor) dispatch(ctx context.Context, event AuditEvent) {
|
||||
if event.Timestamp.IsZero() {
|
||||
event.Timestamp = time.Now()
|
||||
}
|
||||
|
||||
for _, s := range a.sinks {
|
||||
if err := s.Handle(ctx, event); err != nil {
|
||||
log.Warnf("audit sink error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *auditor) close() error {
|
||||
var firstErr error
|
||||
for _, s := range a.sinks {
|
||||
if err := s.Close(); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
return firstErr
|
||||
}
|
||||
|
||||
func (a *auditor) startSession(packageManager string, args []string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.session = &session{
|
||||
startTime: time.Now(),
|
||||
packageManager: packageManager,
|
||||
args: args,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *auditor) getSession() *session {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.session
|
||||
}
|
||||
|
||||
func (a *auditor) recordAllowed() {
|
||||
s := a.getSession()
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.allowedCount++
|
||||
s.totalAnalyzed++
|
||||
}
|
||||
|
||||
func (a *auditor) recordBlocked() {
|
||||
s := a.getSession()
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.blockedCount++
|
||||
s.totalAnalyzed++
|
||||
}
|
||||
|
||||
// recordConfirmed tracks that a user confirmed a suspicious package. It does not
|
||||
// increment totalAnalyzed because the subsequent LogInstallAllowed call for the
|
||||
// same package already does that.
|
||||
func (a *auditor) recordConfirmed() {
|
||||
s := a.getSession()
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.confirmedCount++
|
||||
}
|
||||
|
||||
func (a *auditor) recordTrustedSkipped() {
|
||||
s := a.getSession()
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.trustedSkipped++
|
||||
s.totalAnalyzed++
|
||||
}
|
||||
|
||||
func (a *auditor) recordInsecureBypassed() {
|
||||
s := a.getSession()
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.insecureBypassed++
|
||||
s.totalAnalyzed++
|
||||
}
|
||||
Reference in New Issue
Block a user