diff --git a/db/db.go b/db/db.go index 9a101cb..33baff6 100644 --- a/db/db.go +++ b/db/db.go @@ -85,29 +85,6 @@ func newDB(dbType string) (DB, error) { return nil, fmt.Errorf("Invalid database dialect: %s", dbType) } -// IndexChunk has a starting point and an ending point for Chunk -type IndexChunk struct { - From, To int -} - -func chunkSlice(length int, chunkSize int) <-chan IndexChunk { - ch := make(chan IndexChunk) - - go func() { - defer close(ch) - - for i := 0; i < length; i += chunkSize { - idx := IndexChunk{i, i + chunkSize} - if length < idx.To { - idx.To = length - } - ch <- idx - } - }() - - return ch -} - func parseCpeURI(cpe22uri string) (*models.CpeBase, error) { wfn, err := naming.UnbindURI(cpe22uri) if err != nil { diff --git a/db/rdb.go b/db/rdb.go index 7f6f291..611c103 100644 --- a/db/rdb.go +++ b/db/rdb.go @@ -7,7 +7,9 @@ import ( "fmt" "io" "log" + "maps" "os" + "slices" "time" "github.com/cheggaaa/pb/v3" @@ -15,7 +17,6 @@ import ( "github.com/knqyf263/go-cpe/common" "github.com/knqyf263/go-cpe/naming" "github.com/spf13/viper" - "golang.org/x/exp/maps" "golang.org/x/xerrors" "gorm.io/driver/mysql" "gorm.io/driver/postgres" @@ -438,7 +439,7 @@ func (r *RDBDriver) GetCveIDs() ([]string, error) { cveIDs[cveID] = struct{}{} } - return maps.Keys(cveIDs), nil + return slices.Collect(maps.Keys(cveIDs)), nil } func (r *RDBDriver) getCveIDsByPartVendorProduct(uri string) ([]string, error) { @@ -677,8 +678,8 @@ func insertJvn(tx *gorm.DB, cves []models.Jvn, batchSize int) error { cve.Cpes[i].JvnID = uint(cve.ID) } - for idx := range chunkSlice(len(cve.Cpes), batchSize) { - if err := tx.Create(cve.Cpes[idx.From:idx.To]).Error; err != nil { + for chunk := range slices.Chunk(cve.Cpes, batchSize) { + if err := tx.Create(chunk).Error; err != nil { return xerrors.Errorf("Failed to insert. err: %w", err) } } @@ -781,8 +782,8 @@ func insertNvd(tx *gorm.DB, cves []models.Nvd, batchSize int) error { cve.Cpes[i].NvdID = uint(cve.ID) } - for idx := range chunkSlice(len(cve.Cpes), batchSize) { - if err := tx.Create(cve.Cpes[idx.From:idx.To]).Error; err != nil { + for chunk := range slices.Chunk(cve.Cpes, batchSize) { + if err := tx.Create(chunk).Error; err != nil { return xerrors.Errorf("Failed to insert. err: %w", err) } } @@ -856,8 +857,8 @@ func (r *RDBDriver) InsertFortinet(advs []models.Fortinet) (err error) { adv.Cpes[i].FortinetID = uint(adv.ID) } - for idx := range chunkSlice(len(adv.Cpes), batchSize) { - if err := tx.Create(adv.Cpes[idx.From:idx.To]).Error; err != nil { + for chunk := range slices.Chunk(adv.Cpes, batchSize) { + if err := tx.Create(chunk).Error; err != nil { return xerrors.Errorf("Failed to insert. err: %w", err) } } diff --git a/db/redis.go b/db/redis.go index a30dbde..af94cac 100644 --- a/db/redis.go +++ b/db/redis.go @@ -6,7 +6,9 @@ import ( "errors" "fmt" "io" + "maps" "os" + "slices" "strconv" "strings" "time" @@ -16,7 +18,6 @@ import ( "github.com/knqyf263/go-cpe/common" "github.com/knqyf263/go-cpe/naming" "github.com/spf13/viper" - "golang.org/x/exp/maps" "golang.org/x/xerrors" "github.com/vulsio/go-cve-dictionary/config" @@ -480,9 +481,9 @@ func (r *RedisDriver) InsertJvn(years []string) error { } return os.Stderr }()) - for idx := range chunkSlice(len(cves), batchSize) { + for chunk := range slices.Chunk(cves, batchSize) { pipe := r.conn.Pipeline() - for _, cve := range cves[idx.From:idx.To] { + for _, cve := range chunk { var jn []byte if jn, err = json.Marshal(cve); err != nil { return xerrors.Errorf("Failed to marshal json. err: %w", err) @@ -518,7 +519,7 @@ func (r *RedisDriver) InsertJvn(years []string) error { if _, err = pipe.Exec(ctx); err != nil { return xerrors.Errorf("Failed to exec pipeline. err: %w", err) } - bar.Add(idx.To - idx.From) + bar.Add(len(chunk)) } bar.Finish() log.Infof("Refreshed %d CVEs.", len(cves)) @@ -531,10 +532,10 @@ func (r *RedisDriver) InsertJvn(years []string) error { } return os.Stderr }()) - keys := maps.Keys(advs) - for idx := range chunkSlice(len(keys), batchSize) { + keys := slices.Collect(maps.Keys(advs)) + for chunk := range slices.Chunk(keys, batchSize) { pipe := r.conn.Pipeline() - for _, adv := range keys[idx.From:idx.To] { + for _, adv := range chunk { var aj []byte if aj, err = json.Marshal(advs[adv]); err != nil { return xerrors.Errorf("Failed to marshal json. err: %w", err) @@ -545,7 +546,7 @@ func (r *RedisDriver) InsertJvn(years []string) error { if _, err = pipe.Exec(ctx); err != nil { return xerrors.Errorf("Failed to exec pipeline. err: %w", err) } - bar.Add(idx.To - idx.From) + bar.Add(len(chunk)) } bar.Finish() log.Infof("Refreshed %d Advisories.", len(advs)) @@ -646,9 +647,9 @@ func (r *RedisDriver) InsertNvd(years []string) error { } return os.Stderr }()) - for idx := range chunkSlice(len(cves), batchSize) { + for chunk := range slices.Chunk(cves, batchSize) { pipe := r.conn.Pipeline() - for _, cve := range cves[idx.From:idx.To] { + for _, cve := range chunk { var jn []byte if jn, err = json.Marshal(cve); err != nil { return xerrors.Errorf("Failed to marshal json. err: %w", err) @@ -676,7 +677,7 @@ func (r *RedisDriver) InsertNvd(years []string) error { if _, err = pipe.Exec(ctx); err != nil { return xerrors.Errorf("Failed to exec pipeline. err: %w", err) } - bar.Add(idx.To - idx.From) + bar.Add(len(chunk)) } bar.Finish() log.Infof("Refreshed %d CVEs.", len(cves)) @@ -764,9 +765,9 @@ func (r *RedisDriver) InsertFortinet(advs []models.Fortinet) error { } return os.Stderr }()) - for idx := range chunkSlice(len(advs), batchSize) { + for chunk := range slices.Chunk(advs, batchSize) { pipe := r.conn.Pipeline() - for _, adv := range advs[idx.From:idx.To] { + for _, adv := range chunk { var jn []byte if jn, err = json.Marshal(adv); err != nil { return xerrors.Errorf("Failed to marshal json. err: %w", err) @@ -803,7 +804,7 @@ func (r *RedisDriver) InsertFortinet(advs []models.Fortinet) error { if _, err = pipe.Exec(ctx); err != nil { return xerrors.Errorf("Failed to exec pipeline. err: %w", err) } - bar.Add(idx.To - idx.From) + bar.Add(len(chunk)) } bar.Finish() log.Infof("Refreshed %d CVEs.", len(advs)) @@ -815,10 +816,10 @@ func (r *RedisDriver) InsertFortinet(advs []models.Fortinet) error { } return os.Stderr }()) - keys := maps.Keys(advtoCVEs) - for idx := range chunkSlice(len(keys), batchSize) { + keys := slices.Collect(maps.Keys(advtoCVEs)) + for chunk := range slices.Chunk(keys, batchSize) { pipe := r.conn.Pipeline() - for _, adv := range keys[idx.From:idx.To] { + for _, adv := range chunk { var aj []byte if aj, err = json.Marshal(advtoCVEs[adv]); err != nil { return xerrors.Errorf("Failed to marshal json. err: %w", err) @@ -829,7 +830,7 @@ func (r *RedisDriver) InsertFortinet(advs []models.Fortinet) error { if _, err = pipe.Exec(ctx); err != nil { return xerrors.Errorf("Failed to exec pipeline. err: %w", err) } - bar.Add(idx.To - idx.From) + bar.Add(len(chunk)) } bar.Finish() log.Infof("Refreshed %d Advisories.", len(advtoCVEs)) @@ -930,9 +931,9 @@ func (r *RedisDriver) InsertMitre(years []string) error { } return os.Stderr }()) - for idx := range chunkSlice(len(cves), batchSize) { + for chunk := range slices.Chunk(cves, batchSize) { pipe := r.conn.Pipeline() - for _, cve := range cves[idx.From:idx.To] { + for _, cve := range chunk { bs, err := json.Marshal(cve) if err != nil { return xerrors.Errorf("Failed to marshal json. err: %w", err) @@ -945,7 +946,7 @@ func (r *RedisDriver) InsertMitre(years []string) error { if _, err = pipe.Exec(ctx); err != nil { return xerrors.Errorf("Failed to exec pipeline. err: %w", err) } - bar.Add(idx.To - idx.From) + bar.Add(len(chunk)) } bar.Finish() log.Infof("Refreshed %d CVEs.", len(cves)) diff --git a/fetcher/fortinet/fortinet.go b/fetcher/fortinet/fortinet.go index a1b6e1f..a20a6f2 100644 --- a/fetcher/fortinet/fortinet.go +++ b/fetcher/fortinet/fortinet.go @@ -5,11 +5,12 @@ import ( "bytes" "encoding/json" "fmt" + "maps" + "slices" "strings" "github.com/knqyf263/go-cpe/common" "github.com/knqyf263/go-cpe/naming" - "golang.org/x/exp/maps" "golang.org/x/xerrors" "github.com/vulsio/go-cve-dictionary/fetcher" @@ -93,7 +94,7 @@ func convert(a advisory) ([]models.Fortinet, error) { m[cpe] = struct{}{} } } - cs.Cpes = maps.Keys(m) + cs.Cpes = slices.Collect(maps.Keys(m)) if d.CVSSv3 != nil { parsed := parseCvss3VectorStr(d.CVSSv3.Vector) diff --git a/fetcher/nvd/nvd.go b/fetcher/nvd/nvd.go index eda9cd0..40e8fc5 100644 --- a/fetcher/nvd/nvd.go +++ b/fetcher/nvd/nvd.go @@ -11,12 +11,12 @@ import ( "os" "path/filepath" "runtime" + "slices" "strings" "time" "github.com/hashicorp/go-version" "github.com/spf13/viper" - "golang.org/x/exp/slices" "golang.org/x/xerrors" "github.com/vulsio/go-cve-dictionary/fetcher" diff --git a/go.mod b/go.mod index 8943dfe..5ac935a 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 gorm.io/driver/mysql v1.5.5 gorm.io/driver/postgres v1.5.7 @@ -66,6 +65,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.22.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.19.0 // indirect diff --git a/go.sum b/go.sum index bd7cb3a..5583ebc 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -21,6 +22,7 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= @@ -37,7 +39,9 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= @@ -65,7 +69,9 @@ github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f/go.mod h1:4cVhzV/T github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM= github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -84,8 +90,11 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -93,12 +102,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -193,10 +204,13 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3j golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=