forked from mirrors/pkg-proxy
* Fix all golangci-lint issues across the codebase Resolve 77 lint issues reported by golangci-lint with gocritic, gocognit, gocyclo, maintidx, dupl, mnd, unparam, ireturn, goconst, and errcheck enabled. Net reduction of ~175 lines through shared helpers and deduplication. * Suppress staticcheck SA1019 for intentional deprecated field usage The Storage.Path field is deprecated but still read for backwards compatibility with existing configs that haven't migrated to the URL field.
125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
package cooldown
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const hoursPerDay = 24
|
|
|
|
// Config holds cooldown settings for version filtering.
|
|
// Cooldown hides package versions published too recently, giving the community
|
|
// time to spot malicious releases before they're pulled into projects.
|
|
type Config struct {
|
|
// Default is the global default cooldown duration (e.g., "3d", "48h").
|
|
Default string `json:"default" yaml:"default"`
|
|
|
|
// Ecosystems overrides the default for specific ecosystems.
|
|
// Keys are ecosystem names (e.g., "npm", "pypi").
|
|
Ecosystems map[string]string `json:"ecosystems" yaml:"ecosystems"`
|
|
|
|
// Packages overrides the cooldown for specific packages.
|
|
// Keys are PURLs (e.g., "pkg:npm/lodash", "pkg:npm/@babel/core").
|
|
Packages map[string]string `json:"packages" yaml:"packages"`
|
|
|
|
defaultDuration time.Duration
|
|
ecosystemDurations map[string]time.Duration
|
|
packageDurations map[string]time.Duration
|
|
parsed bool
|
|
}
|
|
|
|
// parse resolves all string durations into time.Duration values.
|
|
// Called lazily on first use.
|
|
func (c *Config) parse() {
|
|
if c.parsed {
|
|
return
|
|
}
|
|
c.parsed = true
|
|
|
|
c.defaultDuration, _ = ParseDuration(c.Default)
|
|
|
|
c.ecosystemDurations = make(map[string]time.Duration, len(c.Ecosystems))
|
|
for k, v := range c.Ecosystems {
|
|
d, _ := ParseDuration(v)
|
|
c.ecosystemDurations[k] = d
|
|
}
|
|
|
|
c.packageDurations = make(map[string]time.Duration, len(c.Packages))
|
|
for k, v := range c.Packages {
|
|
d, _ := ParseDuration(v)
|
|
c.packageDurations[k] = d
|
|
}
|
|
}
|
|
|
|
// For returns the effective cooldown duration for a given ecosystem and package PURL.
|
|
// Resolution order: package override > ecosystem override > global default.
|
|
func (c *Config) For(ecosystem, packagePURL string) time.Duration {
|
|
c.parse()
|
|
|
|
if d, ok := c.packageDurations[packagePURL]; ok {
|
|
return d
|
|
}
|
|
if d, ok := c.ecosystemDurations[ecosystem]; ok {
|
|
return d
|
|
}
|
|
return c.defaultDuration
|
|
}
|
|
|
|
// IsAllowed returns true if a version with the given publish time has passed
|
|
// the cooldown period for this ecosystem/package.
|
|
func (c *Config) IsAllowed(ecosystem, packagePURL string, publishedAt time.Time) bool {
|
|
d := c.For(ecosystem, packagePURL)
|
|
if d == 0 {
|
|
return true
|
|
}
|
|
if publishedAt.IsZero() {
|
|
return true
|
|
}
|
|
return time.Since(publishedAt) >= d
|
|
}
|
|
|
|
// Enabled returns true if any cooldown is configured.
|
|
func (c *Config) Enabled() bool {
|
|
c.parse()
|
|
if c.defaultDuration > 0 {
|
|
return true
|
|
}
|
|
for _, d := range c.ecosystemDurations {
|
|
if d > 0 {
|
|
return true
|
|
}
|
|
}
|
|
for _, d := range c.packageDurations {
|
|
if d > 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ParseDuration parses a duration string supporting days (e.g., "3d"),
|
|
// in addition to Go's standard time.ParseDuration formats ("48h", "30m").
|
|
// "0" means disabled (returns 0).
|
|
func ParseDuration(s string) (time.Duration, error) {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" || s == "0" {
|
|
return 0, nil
|
|
}
|
|
|
|
// Handle day suffix
|
|
if numStr, ok := strings.CutSuffix(s, "d"); ok {
|
|
days, err := strconv.ParseFloat(numStr, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid duration %q: %w", s, err)
|
|
}
|
|
return time.Duration(days * float64(hoursPerDay*time.Hour)), nil
|
|
}
|
|
|
|
d, err := time.ParseDuration(s)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid duration %q: %w", s, err)
|
|
}
|
|
return d, nil
|
|
}
|