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 cooldownBlockedCount 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++ } func (a *auditor) recordCooldownBlocked() { s := a.getSession() if s == nil { return } s.mu.Lock() defer s.mu.Unlock() s.cooldownBlockedCount++ s.totalAnalyzed++ }