mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-06-02 08:38:17 -04:00
Compare commits
1 commit
main
...
registries
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe89cbd3f1 |
16 changed files with 19 additions and 274 deletions
|
|
@ -283,9 +283,9 @@ HTTP server setup, web UI, and API handlers.
|
||||||
|
|
||||||
Prometheus metrics for cache performance, upstream latency, storage operations, and active requests. See the Monitoring section of the README for the full metric list.
|
Prometheus metrics for cache performance, upstream latency, storage operations, and active requests. See the Monitoring section of the README for the full metric list.
|
||||||
|
|
||||||
### `internal/cooldown`
|
### Cooldown
|
||||||
|
|
||||||
Version age filtering for supply chain attack mitigation. Configurable at global, ecosystem, and per-package levels. Supported by npm, PyPI, pub.dev, and Composer handlers.
|
Version age filtering for supply chain attack mitigation, provided by [github.com/git-pkgs/cooldown](https://github.com/git-pkgs/cooldown). Configurable at global, ecosystem, and per-package levels. Supported by npm, PyPI, pub.dev, and Composer handlers.
|
||||||
|
|
||||||
### `internal/enrichment`
|
### `internal/enrichment`
|
||||||
|
|
||||||
|
|
|
||||||
3
go.mod
3
go.mod
|
|
@ -5,9 +5,10 @@ go 1.25.6
|
||||||
require (
|
require (
|
||||||
github.com/CycloneDX/cyclonedx-go v0.10.0
|
github.com/CycloneDX/cyclonedx-go v0.10.0
|
||||||
github.com/git-pkgs/archives v0.3.0
|
github.com/git-pkgs/archives v0.3.0
|
||||||
|
github.com/git-pkgs/cooldown v0.1.1
|
||||||
github.com/git-pkgs/enrichment v0.2.2
|
github.com/git-pkgs/enrichment v0.2.2
|
||||||
github.com/git-pkgs/purl v0.1.12
|
github.com/git-pkgs/purl v0.1.12
|
||||||
github.com/git-pkgs/registries v0.5.1
|
github.com/git-pkgs/registries v0.6.0
|
||||||
github.com/git-pkgs/spdx v0.1.3
|
github.com/git-pkgs/spdx v0.1.3
|
||||||
github.com/git-pkgs/vers v0.2.5
|
github.com/git-pkgs/vers v0.2.5
|
||||||
github.com/git-pkgs/vulns v0.1.5
|
github.com/git-pkgs/vulns v0.1.5
|
||||||
|
|
|
||||||
6
go.sum
6
go.sum
|
|
@ -252,6 +252,8 @@ github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1
|
||||||
github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI=
|
github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI=
|
||||||
github.com/git-pkgs/archives v0.3.0 h1:iXKyO83jEFub1PGEDlHmk2tQ7XeV5LySTc0sEkH3x78=
|
github.com/git-pkgs/archives v0.3.0 h1:iXKyO83jEFub1PGEDlHmk2tQ7XeV5LySTc0sEkH3x78=
|
||||||
github.com/git-pkgs/archives v0.3.0/go.mod h1:LTJ1iQVFA7otizWMOyiI82NYVmyBWAPRzwu/e30rcXU=
|
github.com/git-pkgs/archives v0.3.0/go.mod h1:LTJ1iQVFA7otizWMOyiI82NYVmyBWAPRzwu/e30rcXU=
|
||||||
|
github.com/git-pkgs/cooldown v0.1.1 h1:9OqqzCB8gANz/y44SmqGD0Jp8Qtu81D1sCbKl6Ehg7w=
|
||||||
|
github.com/git-pkgs/cooldown v0.1.1/go.mod h1:v7APuK/UouTiu8mWQZbdDmj7DfxxkGUeuhjaRB5gv9E=
|
||||||
github.com/git-pkgs/enrichment v0.2.2 h1:vaQu5vs3tjQB5JI0gzBrUCynUc9z3l5byPhgKFaNZrc=
|
github.com/git-pkgs/enrichment v0.2.2 h1:vaQu5vs3tjQB5JI0gzBrUCynUc9z3l5byPhgKFaNZrc=
|
||||||
github.com/git-pkgs/enrichment v0.2.2/go.mod h1:5JWGmlHWcv5HQHUrctcpnRUNpEF5VAixD2z4zvqKejs=
|
github.com/git-pkgs/enrichment v0.2.2/go.mod h1:5JWGmlHWcv5HQHUrctcpnRUNpEF5VAixD2z4zvqKejs=
|
||||||
github.com/git-pkgs/packageurl-go v0.3.1 h1:WM3RBABQZLaRBxgKyYughc3cVBE8KyQxbSC6Jt5ak7M=
|
github.com/git-pkgs/packageurl-go v0.3.1 h1:WM3RBABQZLaRBxgKyYughc3cVBE8KyQxbSC6Jt5ak7M=
|
||||||
|
|
@ -260,8 +262,8 @@ github.com/git-pkgs/pom v0.1.4 h1:C6st+XSbF75eKuwfdkDZZtYHoTcaWRIEQYar5VtszUo=
|
||||||
github.com/git-pkgs/pom v0.1.4/go.mod h1:ufdMBe1lKzqOeP9IUb9NPZ458xKV8E8NvuyBMxOfwIk=
|
github.com/git-pkgs/pom v0.1.4/go.mod h1:ufdMBe1lKzqOeP9IUb9NPZ458xKV8E8NvuyBMxOfwIk=
|
||||||
github.com/git-pkgs/purl v0.1.12 h1:qCskrEU1LWQhCkIVZd992W5++Bsxazvx2Cx1/65qCvU=
|
github.com/git-pkgs/purl v0.1.12 h1:qCskrEU1LWQhCkIVZd992W5++Bsxazvx2Cx1/65qCvU=
|
||||||
github.com/git-pkgs/purl v0.1.12/go.mod h1:ofp4mHsR0cUeVONQaf33n6Wxg2QTEvtUdRfCedI8ouA=
|
github.com/git-pkgs/purl v0.1.12/go.mod h1:ofp4mHsR0cUeVONQaf33n6Wxg2QTEvtUdRfCedI8ouA=
|
||||||
github.com/git-pkgs/registries v0.5.1 h1:UPE42CyZAsOfqO3N5bDelu28wS4Ifx/aOj0XZS4qYeI=
|
github.com/git-pkgs/registries v0.6.0 h1:ttQC8via9XAoLk9vqysf0K7uWl1bAyHPBWRBavRpAqs=
|
||||||
github.com/git-pkgs/registries v0.5.1/go.mod h1:BY0YW+V0WDGBMuDR2aSMR3NzOPFK4K+F3j6+ch+cq3M=
|
github.com/git-pkgs/registries v0.6.0/go.mod h1:BY0YW+V0WDGBMuDR2aSMR3NzOPFK4K+F3j6+ch+cq3M=
|
||||||
github.com/git-pkgs/spdx v0.1.3 h1:YQou23mLfzbW//6JlHUuc5x1P5VNIIDSku5gvauf86I=
|
github.com/git-pkgs/spdx v0.1.3 h1:YQou23mLfzbW//6JlHUuc5x1P5VNIIDSku5gvauf86I=
|
||||||
github.com/git-pkgs/spdx v0.1.3/go.mod h1:4HGGWyC8tg4DjOhrtBTYl4Lu+5i2BFuauGX8zcVcYPg=
|
github.com/git-pkgs/spdx v0.1.3/go.mod h1:4HGGWyC8tg4DjOhrtBTYl4Lu+5i2BFuauGX8zcVcYPg=
|
||||||
github.com/git-pkgs/vers v0.2.5 h1:tDtUMik9Iw1lyPHdT5V6LXjLo9LsJc0xOawURz7ibQU=
|
github.com/git-pkgs/vers v0.2.5 h1:tDtUMik9Iw1lyPHdT5V6LXjLo9LsJc0xOawURz7ibQU=
|
||||||
|
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
package cooldown
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseDuration(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
want time.Duration
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"", 0, false},
|
|
||||||
{"0", 0, false},
|
|
||||||
{"3d", 3 * 24 * time.Hour, false},
|
|
||||||
{"7d", 7 * 24 * time.Hour, false},
|
|
||||||
{"14d", 14 * 24 * time.Hour, false},
|
|
||||||
{"1.5d", 36 * time.Hour, false},
|
|
||||||
{"48h", 48 * time.Hour, false},
|
|
||||||
{"30m", 30 * time.Minute, false},
|
|
||||||
{"1h30m", 90 * time.Minute, false},
|
|
||||||
{"invalid", 0, true},
|
|
||||||
{"d", 0, true},
|
|
||||||
{"xd", 0, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
got, err := ParseDuration(tt.input)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("ParseDuration(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("ParseDuration(%q) = %v, want %v", tt.input, got, tt.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigFor(t *testing.T) {
|
|
||||||
c := &Config{
|
|
||||||
Default: "3d",
|
|
||||||
Ecosystems: map[string]string{
|
|
||||||
"npm": "7d",
|
|
||||||
"cargo": "0",
|
|
||||||
},
|
|
||||||
Packages: map[string]string{
|
|
||||||
"pkg:npm/lodash": "0",
|
|
||||||
"pkg:npm/@babel/core": "14d",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
ecosystem string
|
|
||||||
packagePURL string
|
|
||||||
want time.Duration
|
|
||||||
}{
|
|
||||||
// Package override takes priority
|
|
||||||
{"npm", "pkg:npm/lodash", 0},
|
|
||||||
{"npm", "pkg:npm/@babel/core", 14 * 24 * time.Hour},
|
|
||||||
// Ecosystem override
|
|
||||||
{"npm", "pkg:npm/express", 7 * 24 * time.Hour},
|
|
||||||
{"cargo", "pkg:cargo/serde", 0},
|
|
||||||
// Global default
|
|
||||||
{"pypi", "pkg:pypi/requests", 3 * 24 * time.Hour},
|
|
||||||
{"pub", "pkg:pub/flutter", 3 * 24 * time.Hour},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
got := c.For(tt.ecosystem, tt.packagePURL)
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("For(%q, %q) = %v, want %v", tt.ecosystem, tt.packagePURL, got, tt.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigIsAllowed(t *testing.T) {
|
|
||||||
c := &Config{
|
|
||||||
Default: "3d",
|
|
||||||
Packages: map[string]string{
|
|
||||||
"pkg:npm/lodash": "0",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
ecosystem string
|
|
||||||
packagePURL string
|
|
||||||
publishedAt time.Time
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"old enough", "npm", "pkg:npm/express", now.Add(-4 * 24 * time.Hour), true},
|
|
||||||
{"too recent", "npm", "pkg:npm/express", now.Add(-1 * 24 * time.Hour), false},
|
|
||||||
{"exactly at boundary", "npm", "pkg:npm/express", now.Add(-3 * 24 * time.Hour), true},
|
|
||||||
{"exempt package", "npm", "pkg:npm/lodash", now.Add(-1 * time.Minute), true},
|
|
||||||
{"zero time", "npm", "pkg:npm/express", time.Time{}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got := c.IsAllowed(tt.ecosystem, tt.packagePURL, tt.publishedAt)
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("IsAllowed(%q, %q, %v) = %v, want %v",
|
|
||||||
tt.ecosystem, tt.packagePURL, tt.publishedAt, got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigEnabled(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
cfg Config
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"empty config", Config{}, false},
|
|
||||||
{"default only", Config{Default: "3d"}, true},
|
|
||||||
{"ecosystem only", Config{Ecosystems: map[string]string{"npm": "7d"}}, true},
|
|
||||||
{"package only", Config{Packages: map[string]string{"pkg:npm/x": "1d"}}, true},
|
|
||||||
{"all zero", Config{Default: "0", Ecosystems: map[string]string{"npm": "0"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got := tt.cfg.Enabled()
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("Enabled() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cargoTestProxy() *Proxy {
|
func cargoTestProxy() *Proxy {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestComposerRewriteMetadata(t *testing.T) {
|
func TestComposerRewriteMetadata(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCondaParseFilename(t *testing.T) {
|
func TestCondaParseFilename(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGemParseFilename(t *testing.T) {
|
func TestGemParseFilename(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
"github.com/git-pkgs/proxy/internal/database"
|
"github.com/git-pkgs/proxy/internal/database"
|
||||||
"github.com/git-pkgs/proxy/internal/metrics"
|
"github.com/git-pkgs/proxy/internal/metrics"
|
||||||
"github.com/git-pkgs/proxy/internal/storage"
|
"github.com/git-pkgs/proxy/internal/storage"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
"google.golang.org/protobuf/encoding/protowire"
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testVersion100 = "1.0.0"
|
const testVersion100 = "1.0.0"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func nugetTestProxy() *Proxy {
|
func nugetTestProxy() *Proxy {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPubRewriteMetadata(t *testing.T) {
|
func TestPubRewriteMetadata(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
"github.com/git-pkgs/registries/fetch"
|
"github.com/git-pkgs/registries/fetch"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ import (
|
||||||
|
|
||||||
swaggerdoc "github.com/git-pkgs/proxy/docs/swagger"
|
swaggerdoc "github.com/git-pkgs/proxy/docs/swagger"
|
||||||
"github.com/git-pkgs/proxy/internal/config"
|
"github.com/git-pkgs/proxy/internal/config"
|
||||||
"github.com/git-pkgs/proxy/internal/cooldown"
|
"github.com/git-pkgs/cooldown"
|
||||||
"github.com/git-pkgs/proxy/internal/database"
|
"github.com/git-pkgs/proxy/internal/database"
|
||||||
"github.com/git-pkgs/proxy/internal/enrichment"
|
"github.com/git-pkgs/proxy/internal/enrichment"
|
||||||
"github.com/git-pkgs/proxy/internal/handler"
|
"github.com/git-pkgs/proxy/internal/handler"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue