Files
pmg/proxy/interceptors/stats.go
T
2026-04-03 21:25:50 +05:30

164 lines
4.5 KiB
Go

package interceptors
import (
"sync"
"time"
"github.com/safedep/pmg/analyzer"
)
// CooldownBlock records a package blocked by the dependency cooldown policy.
type CooldownBlock struct {
Name string
Version string
PublishDate time.Time
DaysAgo int
DaysLeft int
CooldownDays int
}
// AnalysisStats contains aggregated statistics from analysis results
type AnalysisStats struct {
TotalAnalyzed int
AllowedCount int
ConfirmedCount int
BlockedCount int
UserCancelledCount int
CooldownBlockedCount int
}
// AnalysisStatsCollector tracks analysis statistics during proxy execution.
// It is separate from the cache to allow different cache implementations
// without coupling them to reporting concerns.
type AnalysisStatsCollector struct {
mu sync.RWMutex
stats AnalysisStats
blockedPackages []*analyzer.PackageVersionAnalysisResult
confirmedPackages []*analyzer.PackageVersionAnalysisResult
cooldownBlocks []CooldownBlock
}
// NewAnalysisStatsCollector creates a new stats collector
func NewAnalysisStatsCollector() *AnalysisStatsCollector {
return &AnalysisStatsCollector{}
}
// RecordAllowed records a package that was allowed (safe)
func (c *AnalysisStatsCollector) RecordAllowed(result *analyzer.PackageVersionAnalysisResult) {
if result == nil {
return
}
c.mu.Lock()
defer c.mu.Unlock()
c.stats.TotalAnalyzed++
c.stats.AllowedCount++
}
// RecordBlocked records a package that was automatically blocked (ActionBlock)
func (c *AnalysisStatsCollector) RecordBlocked(result *analyzer.PackageVersionAnalysisResult) {
if result == nil {
return
}
c.mu.Lock()
defer c.mu.Unlock()
c.stats.TotalAnalyzed++
c.stats.BlockedCount++
c.blockedPackages = append(c.blockedPackages, result)
}
// RecordUserCancelled records a package that was blocked because user declined confirmation (ActionConfirm declined)
func (c *AnalysisStatsCollector) RecordUserCancelled(result *analyzer.PackageVersionAnalysisResult) {
if result == nil {
return
}
c.mu.Lock()
defer c.mu.Unlock()
c.stats.TotalAnalyzed++
c.stats.UserCancelledCount++
// User cancelled packages are counted as blocked as well
c.stats.BlockedCount++
c.blockedPackages = append(c.blockedPackages, result)
}
// RecordConfirmed records a package where user confirmed installation despite warning
func (c *AnalysisStatsCollector) RecordConfirmed(result *analyzer.PackageVersionAnalysisResult) {
if result == nil {
return
}
c.mu.Lock()
defer c.mu.Unlock()
c.stats.TotalAnalyzed++
c.stats.ConfirmedCount++
c.confirmedPackages = append(c.confirmedPackages, result)
}
// GetStats returns the current statistics
func (c *AnalysisStatsCollector) GetStats() AnalysisStats {
c.mu.RLock()
defer c.mu.RUnlock()
return c.stats
}
// GetBlockedPackages returns all blocked packages
func (c *AnalysisStatsCollector) GetBlockedPackages() []*analyzer.PackageVersionAnalysisResult {
c.mu.RLock()
defer c.mu.RUnlock()
// Return a copy to avoid race conditions
result := make([]*analyzer.PackageVersionAnalysisResult, len(c.blockedPackages))
copy(result, c.blockedPackages)
return result
}
// GetConfirmedPackages returns all confirmed packages
func (c *AnalysisStatsCollector) GetConfirmedPackages() []*analyzer.PackageVersionAnalysisResult {
c.mu.RLock()
defer c.mu.RUnlock()
// Return a copy to avoid race conditions
result := make([]*analyzer.PackageVersionAnalysisResult, len(c.confirmedPackages))
copy(result, c.confirmedPackages)
return result
}
// RecordCooldownBlocked records a package blocked by the dependency cooldown policy.
// It increments both the cooldown-specific counter and the overall BlockedCount so that
// inferOutcome correctly treats cooldown-only sessions as OutcomeBlocked.
func (c *AnalysisStatsCollector) RecordCooldownBlocked(name, version string, publishDate time.Time, daysAgo, daysLeft, cooldownDays int) {
c.mu.Lock()
defer c.mu.Unlock()
c.stats.TotalAnalyzed++
c.stats.BlockedCount++
c.stats.CooldownBlockedCount++
c.cooldownBlocks = append(c.cooldownBlocks, CooldownBlock{
Name: name,
Version: version,
PublishDate: publishDate,
DaysAgo: daysAgo,
DaysLeft: daysLeft,
CooldownDays: cooldownDays,
})
}
// GetCooldownBlocks returns all packages blocked by the cooldown policy.
func (c *AnalysisStatsCollector) GetCooldownBlocks() []CooldownBlock {
c.mu.RLock()
defer c.mu.RUnlock()
result := make([]CooldownBlock, len(c.cooldownBlocks))
copy(result, c.cooldownBlocks)
return result
}