From f03ffe04bc3b91a3df1fd68d9238f21178f5b67d Mon Sep 17 00:00:00 2001 From: zepatrik Date: Fri, 18 Oct 2024 14:19:32 +0200 Subject: [PATCH 1/5] chore: bump ristretto Breaking change: ristretto now uses generics in a breaking way. Any dependents have to also update ristretto and add the generic initializers. --- .github/workflows/cve-scan.yaml | 2 +- .github/workflows/format.yml | 2 +- .github/workflows/licenses.yml | 2 +- .github/workflows/test.yml | 4 ++-- configx/koanf_test.go | 15 ++++++--------- configx/schema_cache.go | 16 ++++++---------- configx/schema_path_cache.go | 16 ++++++---------- fetcher/fetcher.go | 11 +++++------ fetcher/fetcher_test.go | 2 +- go.mod | 9 ++++----- go.sum | 23 ++++++++++------------- jwksx/fetcher_v2.go | 6 +++--- jwksx/fetcher_v2_test.go | 8 ++++++-- 13 files changed, 52 insertions(+), 64 deletions(-) diff --git a/.github/workflows/cve-scan.yaml b/.github/workflows/cve-scan.yaml index 7bb9c189..ba94025d 100644 --- a/.github/workflows/cve-scan.yaml +++ b/.github/workflows/cve-scan.yaml @@ -30,7 +30,7 @@ jobs: uses: golang/govulncheck-action@v1 with: go-package: ./... - go-version-input: "1.22" + go-version-input: "1.23.2" - name: Run Trivy vulnerability scanner in repo mode continue-on-error: true uses: aquasecurity/trivy-action@master diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 87e1655b..bc41a4ba 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: "1.22" + go-version: "1.23.2" - run: make format - name: Indicate formatting issues run: git diff HEAD --exit-code --color diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 8a864860..6fb85398 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "1.22" + go-version: "1.23.2" - uses: actions/setup-node@v2 with: node-version: "18" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d4b53a58..d63c39b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "1.22" + go-version: "1.23.2" - run: | go test -tags sqlite -failfast -short -timeout=20m $(go list ./... | grep -v sqlcon | grep -v watcherx | grep -v pkgerx | grep -v configx) shell: bash @@ -55,7 +55,7 @@ jobs: uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "1.22" + go-version: "1.23.2" - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: diff --git a/configx/koanf_test.go b/configx/koanf_test.go index 56e0d31e..f4e2372c 100644 --- a/configx/koanf_test.go +++ b/configx/koanf_test.go @@ -87,7 +87,7 @@ func BenchmarkKoanf(b *testing.B) { }) b.Run("cache=true", func(b *testing.B) { - for i, c := range []*ristretto.Config{ + for i, c := range []*ristretto.Config[string, any]{ { NumCounters: int64(numKeys), MaxCost: 500000, @@ -104,19 +104,16 @@ func BenchmarkKoanf(b *testing.B) { BufferItems: 64, }, } { - cache, err := ristretto.NewCache(c) + cache, err := ristretto.NewCache[string, any](c) require.NoError(b, err) b.Run(fmt.Sprintf("config=%d", i), func(b *testing.B) { - var key string - var found bool - var val interface{} - b.ResetTimer() - for i := 0; i < b.N; i++ { - key = keys[i%numKeys] + for i := range b.N { + key := keys[i%numKeys] - if val, found = cache.Get(key); !found { + val, found := cache.Get(key) + if !found { val = k.Koanf.Get(key) _ = cache.Set(key, val, 0) } diff --git a/configx/schema_cache.go b/configx/schema_cache.go index f2b69955..7ed33a73 100644 --- a/configx/schema_cache.go +++ b/configx/schema_cache.go @@ -6,21 +6,20 @@ package configx import ( "context" "crypto/sha256" - "fmt" "github.com/dgraph-io/ristretto" "github.com/ory/jsonschema/v3" ) -var schemaCacheConfig = &ristretto.Config{ +var schemaCacheConfig = &ristretto.Config[[]byte, *jsonschema.Schema]{ // Hold up to 25 schemas in cache. Usually we only need one. MaxCost: 25, NumCounters: 250, BufferItems: 64, Metrics: false, IgnoreInternalCost: true, - Cost: func(value interface{}) int64 { + Cost: func(*jsonschema.Schema) int64 { return 1 }, } @@ -28,12 +27,9 @@ var schemaCacheConfig = &ristretto.Config{ var schemaCache, _ = ristretto.NewCache(schemaCacheConfig) func getSchema(ctx context.Context, schema []byte) (*jsonschema.Schema, error) { - key := fmt.Sprintf("%x", sha256.Sum256(schema)) - if val, found := schemaCache.Get(key); found { - if validator, ok := val.(*jsonschema.Schema); ok { - return validator, nil - } - schemaCache.Del(key) + key := sha256.Sum256(schema) + if val, found := schemaCache.Get(key[:]); found { + return val, nil } schemaID, comp, err := newCompiler(schema) @@ -46,7 +42,7 @@ func getSchema(ctx context.Context, schema []byte) (*jsonschema.Schema, error) { return nil, err } - schemaCache.Set(key, validator, 1) + schemaCache.Set(key[:], validator, 1) schemaCache.Wait() return validator, nil } diff --git a/configx/schema_path_cache.go b/configx/schema_path_cache.go index 15d9110c..7ed253fa 100644 --- a/configx/schema_path_cache.go +++ b/configx/schema_path_cache.go @@ -5,7 +5,6 @@ package configx import ( "crypto/sha256" - "fmt" "github.com/ory/x/jsonschemax" @@ -14,7 +13,7 @@ import ( "github.com/ory/jsonschema/v3" ) -var schemaPathCacheConfig = &ristretto.Config{ +var schemaPathCacheConfig = &ristretto.Config[[]byte, []jsonschemax.Path]{ // Hold up to 25 schemas in cache. Usually we only need one. MaxCost: 250, NumCounters: 2500, @@ -23,15 +22,12 @@ var schemaPathCacheConfig = &ristretto.Config{ IgnoreInternalCost: true, } -var schemaPathCache, _ = ristretto.NewCache(schemaPathCacheConfig) +var schemaPathCache, _ = ristretto.NewCache[[]byte, []jsonschemax.Path](schemaPathCacheConfig) func getSchemaPaths(rawSchema []byte, schema *jsonschema.Schema) ([]jsonschemax.Path, error) { - key := fmt.Sprintf("%x", sha256.Sum256(rawSchema)) - if val, found := schemaPathCache.Get(key); found { - if validator, ok := val.([]jsonschemax.Path); ok { - return validator, nil - } - schemaPathCache.Del(key) + key := sha256.Sum256(rawSchema) + if val, found := schemaPathCache.Get(key[:]); found { + return val, nil } keys, err := jsonschemax.ListPathsWithInitializedSchemaAndArraysIncluded(schema) @@ -39,7 +35,7 @@ func getSchemaPaths(rawSchema []byte, schema *jsonschema.Schema) ([]jsonschemax. return nil, err } - schemaPathCache.Set(key, keys, 1) + schemaPathCache.Set(key[:], keys, 1) schemaPathCache.Wait() return keys, nil } diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 0ae8f20e..136df8df 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -27,14 +27,14 @@ import ( type Fetcher struct { hc *retryablehttp.Client limit int64 - cache *ristretto.Cache + cache *ristretto.Cache[[]byte, []byte] ttl time.Duration } type opts struct { hc *retryablehttp.Client limit int64 - cache *ristretto.Cache + cache *ristretto.Cache[[]byte, []byte] ttl time.Duration } @@ -55,7 +55,7 @@ func WithMaxHTTPMaxBytes(limit int64) Modifier { } } -func WithCache(cache *ristretto.Cache, ttl time.Duration) Modifier { +func WithCache(cache *ristretto.Cache[[]byte, []byte], ttl time.Duration) Modifier { return func(o *opts) { if ttl < 0 { return @@ -120,9 +120,8 @@ func (f *Fetcher) fetchRemote(ctx context.Context, source string) (b []byte, err if f.cache != nil { cacheKey := sha256.Sum256([]byte(source)) if v, ok := f.cache.Get(cacheKey[:]); ok { - cached := v.([]byte) - b = make([]byte, len(cached)) - copy(b, cached) + b = make([]byte, len(v)) + copy(b, v) return b, nil } defer func() { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 62241bee..37ccd57f 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -104,7 +104,7 @@ func TestFetcher(t *testing.T) { })) t.Cleanup(srv.Close) - cache, err := ristretto.NewCache(&ristretto.Config{ + cache, err := ristretto.NewCache[[]byte, []byte](&ristretto.Config[[]byte, []byte]{ NumCounters: 100 * 10, MaxCost: 100, BufferItems: 64, diff --git a/go.mod b/go.mod index ad67bb26..2ad40d55 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/bradleyjkemp/cupaloy/v2 v2.8.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cockroachdb/cockroach-go/v2 v2.3.5 - github.com/dgraph-io/ristretto v0.1.1 + github.com/dgraph-io/ristretto v1.0.0 github.com/docker/docker v26.1.4+incompatible github.com/evanphx/json-patch/v5 v5.6.0 github.com/fatih/structs v1.1.0 @@ -116,7 +116,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/continuity v0.4.3 // indirect github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -125,7 +125,7 @@ require ( github.com/docker/cli v26.1.4+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -150,7 +150,6 @@ require ( github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/glog v1.1.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/pprof v0.0.0-20221010195024-131d412537ea // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect @@ -214,7 +213,7 @@ require ( go.uber.org/atomic v1.10.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/image v0.18.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.4.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect diff --git a/go.sum b/go.sum index 034c0f01..542089ee 100644 --- a/go.sum +++ b/go.sum @@ -93,8 +93,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -123,10 +123,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= -github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgraph-io/ristretto v1.0.0 h1:SYG07bONKMlFDUYu5pEu3DGAh8c2OFNzKm6G9J4Si84= +github.com/dgraph-io/ristretto v1.0.0/go.mod h1:jTi2FiYEhQ1NsMmA7DeBykizjOuY88NhKBkepyu1jPc= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v26.1.4+incompatible h1:I8PHdc0MtxEADqYJZvhBrW9bo8gawKwwenxRM7/rLu8= @@ -138,8 +138,8 @@ github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -313,8 +313,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1086,14 +1084,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/jwksx/fetcher_v2.go b/jwksx/fetcher_v2.go index a0ea7590..2806bf53 100644 --- a/jwksx/fetcher_v2.go +++ b/jwksx/fetcher_v2.go @@ -36,14 +36,14 @@ type ( } // FetcherNext is a JWK fetcher that can be used to fetch JWKs from multiple locations. FetcherNext struct { - cache *ristretto.Cache + cache *ristretto.Cache[[]byte, jwk.Set] } // FetcherNextOption is a functional option for the FetcherNext. FetcherNextOption func(*fetcherNextOptions) ) // NewFetcherNext returns a new FetcherNext instance. -func NewFetcherNext(cache *ristretto.Cache) *FetcherNext { +func NewFetcherNext(cache *ristretto.Cache[[]byte, jwk.Set]) *FetcherNext { return &FetcherNext{ cache: cache, } @@ -142,7 +142,7 @@ func (f *FetcherNext) fetch(ctx context.Context, location string, opts *fetcherN cacheKey := sha256.Sum256([]byte(location)) if opts.useCache { if result, found := f.cache.Get(cacheKey[:]); found { - return result.(jwk.Set), nil + return result, nil } } diff --git a/jwksx/fetcher_v2_test.go b/jwksx/fetcher_v2_test.go index 6bf23f02..42144756 100644 --- a/jwksx/fetcher_v2_test.go +++ b/jwksx/fetcher_v2_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/lestrrat-go/jwx/jwk" + "github.com/hashicorp/go-retryablehttp" "github.com/pkg/errors" @@ -59,16 +61,18 @@ func (b brokenTransport) RoundTrip(_ *http.Request) (*http.Response, error) { func TestFetcherNext(t *testing.T) { ctx := context.Background() - cache, _ := ristretto.NewCache(&ristretto.Config{ + cache, err := ristretto.NewCache[[]byte, jwk.Set](&ristretto.Config[[]byte, jwk.Set]{ NumCounters: 100 * 10, MaxCost: 100, BufferItems: 64, Metrics: true, IgnoreInternalCost: true, - Cost: func(value interface{}) int64 { + Cost: func(jwk.Set) int64 { return 1 }, }) + require.NoError(t, err) + f := NewFetcherNext(cache) createRemoteProvider := func(called *int, payload string) *httptest.Server { From e870867f8dcddd05706f7f3aef5a96427ac8410c Mon Sep 17 00:00:00 2001 From: zepatrik Date: Fri, 18 Oct 2024 14:54:23 +0200 Subject: [PATCH 2/5] chore: bump golang-ci lint and fix int overflows --- .github/workflows/test.yml | 5 ++--- Makefile | 2 +- hasherx/hash_comparator.go | 39 +++++++++++++++++++++++++++++------ hasherx/hasher_argon2.go | 23 +++++++++++++++------ jsonnetsecure/jsonnet_pool.go | 2 +- mapx/type_assert.go | 36 ++++++++++++++------------------ 6 files changed, 69 insertions(+), 38 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d63c39b9..f4079506 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,10 +57,9 @@ jobs: with: go-version: "1.23.2" - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: - version: v1.59.1 - skip-go-installation: true + version: v1.61.0 args: --timeout 5m - name: Install cockroach DB run: | diff --git a/Makefile b/Makefile index 972644bc..5643963e 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ licenses: .bin/licenses node_modules # checks open-source licenses GOBIN=$(shell pwd)/.bin go install golang.org/x/tools/cmd/goimports@latest .bin/golangci-lint: Makefile - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b .bin v1.59.1 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b .bin v1.61.0 .bin/licenses: Makefile curl https://raw.githubusercontent.com/ory/ci/master/licenses/install | sh diff --git a/hasherx/hash_comparator.go b/hasherx/hash_comparator.go index 3030a59b..e9abaee2 100644 --- a/hasherx/hash_comparator.go +++ b/hasherx/hash_comparator.go @@ -5,6 +5,7 @@ import ( "crypto/subtle" "encoding/base64" "fmt" + "math" "regexp" "strings" @@ -53,8 +54,13 @@ func CompareArgon2id(_ context.Context, password []byte, hash []byte) error { return err } + mem := uint64(p.Memory) + if mem > math.MaxUint32 { + return errors.WithStack(ErrInvalidHash) + } + // Derive the key from the other password using the same parameters. - otherHash := argon2.IDKey([]byte(password), salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength) + otherHash := argon2.IDKey(password, salt, p.Iterations, uint32(mem), p.Parallelism, p.KeyLength) // Check that the contents of the hashed passwords are identical. Note // that we are using the subtle.ConstantTimeCompare() function for this @@ -73,8 +79,13 @@ func CompareArgon2i(_ context.Context, password []byte, hash []byte) error { return err } + mem := uint64(p.Memory) + if mem > math.MaxUint32 { + return errors.WithStack(ErrInvalidHash) + } + // Derive the key from the other password using the same parameters. - otherHash := argon2.Key([]byte(password), salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength) + otherHash := argon2.Key(password, salt, p.Iterations, uint32(mem), p.Parallelism, p.KeyLength) // Check that the contents of the hashed passwords are identical. Note // that we are using the subtle.ConstantTimeCompare() function for this @@ -153,13 +164,21 @@ func decodeArgon2idHash(encodedHash string) (p *Argon2Config, salt, hash []byte, if err != nil { return nil, nil, nil, err } - p.SaltLength = uint32(len(salt)) + saltLength := len(salt) + if saltLength > math.MaxUint32 { + return nil, nil, nil, ErrInvalidHash + } + p.SaltLength = uint32(saltLength) hash, err = base64.RawStdEncoding.Strict().DecodeString(parts[5]) if err != nil { return nil, nil, nil, err } - p.KeyLength = uint32(len(hash)) + keyLength := len(hash) + if keyLength > math.MaxUint32 { + return nil, nil, nil, ErrInvalidHash + } + p.KeyLength = uint32(keyLength) return p, salt, hash, nil } @@ -188,13 +207,21 @@ func decodePbkdf2Hash(encodedHash string) (p *PBKDF2Config, salt, hash []byte, e if err != nil { return nil, nil, nil, err } - p.SaltLength = uint32(len(salt)) + saltLength := len(salt) + if saltLength > math.MaxUint32 { + return nil, nil, nil, ErrInvalidHash + } + p.SaltLength = uint32(saltLength) hash, err = base64.RawStdEncoding.Strict().DecodeString(parts[4]) if err != nil { return nil, nil, nil, err } - p.KeyLength = uint32(len(hash)) + keyLength := len(hash) + if keyLength > math.MaxUint32 { + return nil, nil, nil, ErrInvalidHash + } + p.KeyLength = uint32(keyLength) return p, salt, hash, nil } diff --git a/hasherx/hasher_argon2.go b/hasherx/hasher_argon2.go index 62e196bd..238ab395 100644 --- a/hasherx/hasher_argon2.go +++ b/hasherx/hasher_argon2.go @@ -6,8 +6,11 @@ import ( "crypto/rand" "encoding/base64" "fmt" + "math" "time" + "github.com/ory/x/otelx" + "github.com/inhies/go-bytesize" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -64,14 +67,18 @@ func NewHasherArgon2(c Argon2Configurator) *Argon2 { return &Argon2{c: c} } -func toKB(mem bytesize.ByteSize) uint32 { - return uint32(mem / bytesize.KB) +func toKB(mem bytesize.ByteSize) (uint32, error) { + kb := uint64(mem / bytesize.KB) + if kb > math.MaxUint32 { + return 0, errors.Errorf("memory %v is too large", mem) + } + return uint32(kb), nil } // Generate generates a hash for the given password. -func (h *Argon2) Generate(ctx context.Context, password []byte) ([]byte, error) { +func (h *Argon2) Generate(ctx context.Context, password []byte) (_ []byte, err error) { ctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Argon2.Generate") - defer span.End() + defer otelx.End(span, &err) p := h.c.HasherArgon2Config(ctx) span.SetAttributes(attribute.String("argon2.config", fmt.Sprintf("#%v", p))) @@ -80,16 +87,20 @@ func (h *Argon2) Generate(ctx context.Context, password []byte) ([]byte, error) return nil, err } + mem, err := toKB(p.Memory) + if err != nil { + return nil, err + } // Pass the plaintext password, salt and parameters to the argon2.IDKey // function. This will generate a hash of the password using the Argon2id // variant. - hash := argon2.IDKey([]byte(password), salt, p.Iterations, toKB(p.Memory), p.Parallelism, p.KeyLength) + hash := argon2.IDKey(password, salt, p.Iterations, mem, p.Parallelism, p.KeyLength) var b bytes.Buffer if _, err := fmt.Fprintf( &b, "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", - argon2.Version, toKB(p.Memory), p.Iterations, p.Parallelism, + argon2.Version, mem, p.Iterations, p.Parallelism, base64.RawStdEncoding.EncodeToString(salt), base64.RawStdEncoding.EncodeToString(hash), ); err != nil { diff --git a/jsonnetsecure/jsonnet_pool.go b/jsonnetsecure/jsonnet_pool.go index 1a53ff6a..b0455650 100644 --- a/jsonnetsecure/jsonnet_pool.go +++ b/jsonnetsecure/jsonnet_pool.go @@ -58,7 +58,7 @@ var ( func NewProcessPool(size int) Pool { size = max(5, min(size, math.MaxInt32)) pud, err := puddle.NewPool(&puddle.Config[worker]{ - MaxSize: int32(size), + MaxSize: int32(size), //nolint:gosec // disable G115 // because of the previous min/max, 5 <= size <= math.MaxInt32 Constructor: newWorker, Destructor: worker.destroy, }) diff --git a/mapx/type_assert.go b/mapx/type_assert.go index 7a9ba97b..97ad3faa 100644 --- a/mapx/type_assert.go +++ b/mapx/type_assert.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "time" ) @@ -103,17 +104,14 @@ func GetInt32Default(values map[interface{}]interface{}, key interface{}, defaul // GetInt32 returns an int32 for a given key in values. func GetInt32(values map[interface{}]interface{}, key interface{}) (int32, error) { - if v, ok := values[key]; !ok { - return 0, ErrKeyDoesNotExist - } else if sv, ok := v.(int32); ok { - return sv, nil - } else if sv, ok := v.(int); ok { - return int32(sv), nil - } else if j, ok := v.(json.Number); ok { - v, err := j.Int64() - return int32(v), err + v, err := GetInt64(values, key) + if err != nil { + return 0, err } - return 0, ErrKeyCanNotBeTypeAsserted + if v > math.MaxInt32 || v < math.MinInt32 { + return 0, errors.New("value is out of range") + } + return int32(v), nil } // GetIntDefault returns a int or the default value for a given key in values. @@ -127,18 +125,14 @@ func GetIntDefault(values map[interface{}]interface{}, key interface{}, defaultV // GetInt returns an int for a given key in values. func GetInt(values map[interface{}]interface{}, key interface{}) (int, error) { - if v, ok := values[key]; !ok { - return 0, ErrKeyDoesNotExist - } else if sv, ok := v.(int32); ok { - return int(sv), nil - } else if sv, ok := v.(int); ok { - return sv, nil - } else if j, ok := v.(json.Number); ok { - v, err := j.Int64() - return int(v), err + v, err := GetInt64(values, key) + if err != nil { + return 0, err } - return 0, ErrKeyCanNotBeTypeAsserted - + if v > math.MaxInt || v < math.MinInt { + return 0, errors.New("value is out of range") + } + return int(v), nil } // GetFloat32Default returns a float32 or the default value for a given key in values. From 4ab69b38083f7ff7b3a78007618ddc13fb13fe34 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Fri, 18 Oct 2024 15:02:27 +0200 Subject: [PATCH 3/5] test: ignore with generics --- configx/testmain_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configx/testmain_test.go b/configx/testmain_test.go index 7ec6b936..e4ab8f30 100644 --- a/configx/testmain_test.go +++ b/configx/testmain_test.go @@ -13,7 +13,7 @@ func TestMain(m *testing.M) { goleak.VerifyTestMain(m, goleak.IgnoreCurrent(), // We have the global schema cache that is never closed. - goleak.IgnoreTopFunction("github.com/dgraph-io/ristretto.(*defaultPolicy).processItems"), - goleak.IgnoreTopFunction("github.com/dgraph-io/ristretto.(*Cache).processItems"), + goleak.IgnoreTopFunction("github.com/dgraph-io/ristretto.(*defaultPolicy[...]).processItems"), + goleak.IgnoreTopFunction("github.com/dgraph-io/ristretto.(*Cache[...]).processItems"), ) } From 0acf560fcd4072920191a61b6f01bb785926e330 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Mon, 21 Oct 2024 10:26:56 +0200 Subject: [PATCH 4/5] fix(mapx): handle other int types --- mapx/type_assert.go | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/mapx/type_assert.go b/mapx/type_assert.go index 97ad3faa..67118303 100644 --- a/mapx/type_assert.go +++ b/mapx/type_assert.go @@ -83,12 +83,31 @@ func GetInt64Default(values map[interface{}]interface{}, key interface{}, defaul // GetInt64 returns an int64 for a given key in values. func GetInt64(values map[interface{}]interface{}, key interface{}) (int64, error) { - if v, ok := values[key]; !ok { + v, ok := values[key] + if !ok { return 0, ErrKeyDoesNotExist - } else if j, ok := v.(json.Number); ok { - return j.Int64() - } else if sv, ok := v.(int64); ok { - return sv, nil + } + switch v := v.(type) { + case json.Number: + return v.Int64() + case int64: + return v, nil + case int: + return int64(v), nil + case int32: + return int64(v), nil + case uint: + if v > math.MaxInt64 { + return 0, errors.New("value is out of range") + } + return int64(v), nil + case uint32: + return int64(v), nil + case uint64: + if v > math.MaxInt64 { + return 0, errors.New("value is out of range") + } + return int64(v), nil } return 0, ErrKeyCanNotBeTypeAsserted } From fdfc83a7ea69e5a355de806d12fb24e50b23b1a2 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Mon, 21 Oct 2024 10:31:32 +0200 Subject: [PATCH 5/5] feat(mapx): make keys generic --- mapx/type_assert.go | 52 +++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/mapx/type_assert.go b/mapx/type_assert.go index 67118303..f76de985 100644 --- a/mapx/type_assert.go +++ b/mapx/type_assert.go @@ -18,7 +18,7 @@ var ErrKeyDoesNotExist = errors.New("key is not present in map") var ErrKeyCanNotBeTypeAsserted = errors.New("key could not be type asserted") // GetString returns a string for a given key in values. -func GetString(values map[interface{}]interface{}, key interface{}) (string, error) { +func GetString[K comparable](values map[K]any, key K) (string, error) { if v, ok := values[key]; !ok { return "", ErrKeyDoesNotExist } else if sv, ok := v.(string); !ok { @@ -29,12 +29,12 @@ func GetString(values map[interface{}]interface{}, key interface{}) (string, err } // GetStringSlice returns a string slice for a given key in values. -func GetStringSlice(values map[interface{}]interface{}, key interface{}) ([]string, error) { +func GetStringSlice[K comparable](values map[K]any, key K) ([]string, error) { if v, ok := values[key]; !ok { return []string{}, ErrKeyDoesNotExist } else if sv, ok := v.([]string); ok { return sv, nil - } else if sv, ok := v.([]interface{}); ok { + } else if sv, ok := v.([]any); ok { vs := make([]string, len(sv)) for k, v := range sv { vv, ok := v.(string) @@ -49,7 +49,7 @@ func GetStringSlice(values map[interface{}]interface{}, key interface{}) ([]stri } // GetTime returns a string slice for a given key in values. -func GetTime(values map[interface{}]interface{}, key interface{}) (time.Time, error) { +func GetTime[K comparable](values map[K]any, key K) (time.Time, error) { v, ok := values[key] if !ok { return time.Time{}, ErrKeyDoesNotExist @@ -73,7 +73,7 @@ func GetTime(values map[interface{}]interface{}, key interface{}) (time.Time, er } // GetInt64Default returns a int64 or the default value for a given key in values. -func GetInt64Default(values map[interface{}]interface{}, key interface{}, defaultValue int64) int64 { +func GetInt64Default[K comparable](values map[K]any, key K, defaultValue int64) int64 { f, err := GetInt64(values, key) if err != nil { return defaultValue @@ -82,7 +82,7 @@ func GetInt64Default(values map[interface{}]interface{}, key interface{}, defaul } // GetInt64 returns an int64 for a given key in values. -func GetInt64(values map[interface{}]interface{}, key interface{}) (int64, error) { +func GetInt64[K comparable](values map[K]any, key K) (int64, error) { v, ok := values[key] if !ok { return 0, ErrKeyDoesNotExist @@ -113,7 +113,7 @@ func GetInt64(values map[interface{}]interface{}, key interface{}) (int64, error } // GetInt32Default returns a int32 or the default value for a given key in values. -func GetInt32Default(values map[interface{}]interface{}, key interface{}, defaultValue int32) int32 { +func GetInt32Default[K comparable](values map[K]any, key K, defaultValue int32) int32 { f, err := GetInt32(values, key) if err != nil { return defaultValue @@ -122,7 +122,7 @@ func GetInt32Default(values map[interface{}]interface{}, key interface{}, defaul } // GetInt32 returns an int32 for a given key in values. -func GetInt32(values map[interface{}]interface{}, key interface{}) (int32, error) { +func GetInt32[K comparable](values map[K]any, key K) (int32, error) { v, err := GetInt64(values, key) if err != nil { return 0, err @@ -134,7 +134,7 @@ func GetInt32(values map[interface{}]interface{}, key interface{}) (int32, error } // GetIntDefault returns a int or the default value for a given key in values. -func GetIntDefault(values map[interface{}]interface{}, key interface{}, defaultValue int) int { +func GetIntDefault[K comparable](values map[K]any, key K, defaultValue int) int { f, err := GetInt(values, key) if err != nil { return defaultValue @@ -143,7 +143,7 @@ func GetIntDefault(values map[interface{}]interface{}, key interface{}, defaultV } // GetInt returns an int for a given key in values. -func GetInt(values map[interface{}]interface{}, key interface{}) (int, error) { +func GetInt[K comparable](values map[K]any, key K) (int, error) { v, err := GetInt64(values, key) if err != nil { return 0, err @@ -155,7 +155,7 @@ func GetInt(values map[interface{}]interface{}, key interface{}) (int, error) { } // GetFloat32Default returns a float32 or the default value for a given key in values. -func GetFloat32Default(values map[interface{}]interface{}, key interface{}, defaultValue float32) float32 { +func GetFloat32Default[K comparable](values map[K]any, key K, defaultValue float32) float32 { f, err := GetFloat32(values, key) if err != nil { return defaultValue @@ -164,7 +164,7 @@ func GetFloat32Default(values map[interface{}]interface{}, key interface{}, defa } // GetFloat32 returns a float32 for a given key in values. -func GetFloat32(values map[interface{}]interface{}, key interface{}) (float32, error) { +func GetFloat32[K comparable](values map[K]any, key K) (float32, error) { if v, ok := values[key]; !ok { return 0, ErrKeyDoesNotExist } else if j, ok := v.(json.Number); ok { @@ -177,7 +177,7 @@ func GetFloat32(values map[interface{}]interface{}, key interface{}) (float32, e } // GetFloat64Default returns a float64 or the default value for a given key in values. -func GetFloat64Default(values map[interface{}]interface{}, key interface{}, defaultValue float64) float64 { +func GetFloat64Default[K comparable](values map[K]any, key K, defaultValue float64) float64 { f, err := GetFloat64(values, key) if err != nil { return defaultValue @@ -186,7 +186,7 @@ func GetFloat64Default(values map[interface{}]interface{}, key interface{}, defa } // GetFloat64 returns a float64 for a given key in values. -func GetFloat64(values map[interface{}]interface{}, key interface{}) (float64, error) { +func GetFloat64[K comparable](values map[K]any, key K) (float64, error) { if v, ok := values[key]; !ok { return 0, ErrKeyDoesNotExist } else if j, ok := v.(json.Number); ok { @@ -198,7 +198,7 @@ func GetFloat64(values map[interface{}]interface{}, key interface{}) (float64, e } // GetStringDefault returns a string or the default value for a given key in values. -func GetStringDefault(values map[interface{}]interface{}, key interface{}, defaultValue string) string { +func GetStringDefault[K comparable](values map[K]any, key K, defaultValue string) string { if s, err := GetString(values, key); err == nil { return s } @@ -206,37 +206,39 @@ func GetStringDefault(values map[interface{}]interface{}, key interface{}, defau } // GetStringSliceDefault returns a string slice or the default value for a given key in values. -func GetStringSliceDefault(values map[interface{}]interface{}, key interface{}, defaultValue []string) []string { +func GetStringSliceDefault[K comparable](values map[K]any, key K, defaultValue []string) []string { if s, err := GetStringSlice(values, key); err == nil { return s } return defaultValue } -// KeyStringToInterface converts map[string]interface{} to map[interface{}]interface{} -func KeyStringToInterface(i map[string]interface{}) map[interface{}]interface{} { - o := make(map[interface{}]interface{}) +// KeyStringToInterface converts map[string]any to map[any]any +// Deprecated: with generics, this should not be necessary anymore. +func KeyStringToInterface(i map[string]any) map[any]any { + o := make(map[any]any) for k, v := range i { o[k] = v } return o } -// ToJSONMap converts all map[interface{}]interface{} occurrences (nested as well) to map[string]interface{}. -func ToJSONMap(i interface{}) interface{} { +// ToJSONMap converts all map[any]any occurrences (nested as well) to map[string]any. +// Deprecated: with generics, this should not be necessary anymore. +func ToJSONMap(i any) any { switch t := i.(type) { - case []interface{}: + case []any: for k, v := range t { t[k] = ToJSONMap(v) } return t - case map[string]interface{}: + case map[string]any: for k, v := range t { t[k] = ToJSONMap(v) } return t - case map[interface{}]interface{}: - res := make(map[string]interface{}) + case map[any]any: + res := make(map[string]any) for k, v := range t { res[fmt.Sprintf("%s", k)] = ToJSONMap(v) }