From a7d430c523a591607a0b2fd40dc7827bc19b3145 Mon Sep 17 00:00:00 2001 From: vro <168573323+golangisfun123@users.noreply.github.com> Date: Thu, 9 May 2024 08:36:54 -0500 Subject: [PATCH] feat(webhook): add webhook (#2538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add models and endpoint, lacking logic * just stuff * just stuff * made general db interface * cleanup * trying to test * trying to test * trying ot fix test * remove interface{} from gorm models, start tests, rework db interface * add signature * secret * look away for now * finish db test * finish tests * add auth * remove debugging log * comments and nits * lint * appsecret and appid * resolve comments * swagger, lint * feat(synapse-interface): maintenance aggregator using PAUSED_CHAINS (#2345) * Aggregate maintenance events for banners and warning message * Dynamically render countdown progress bars based on PAUSED_CHAIN * Dynamically rendering banners * Slightly organize * ChainPause type applied to enforce maintenance event structure, pass in component messages as a prop * Working with multiple events * Add dev comments to MaintenanceBanner; refactor * Add dev comments for MaintenanceWarningMessage; refactor * Dev comments * Organize components * isChainIncluded util * Clean * Add ability to specify paused chains by from/to side (#2346) * Allow indefinite maintenance components by setting end date to null * Banners to show indefinitely as well * Add props to disable banner / warning / countdown * Implement disable warning * Implement disable countdown, bridge pause still working * Example * Clean * Update naming on Bridge page * Update comment for isChainIncluded * Create maintenance events reading from pausedChains.json * Remove custom margins to allow Bridge parent gap styling to handle spacing * Require all props to be defined * Add Swap to maintenance warning messages * Update useMaintenanceCountdownProgresses to allow distinction between Swap and Bridge pauses * Move MaintenanceBanners into LandingPageWrapper so banner appears on all pages * Add ability to specify whether to pause bridge / swap with maintenance event in json * Clean * Unused code * Update dev comments * Update pause start/end time name for legibility * Create type guard to check for paused bridge module * usePausedBridgeModules * usePausedBridgeModules to filter out SDK quotes * Initialize paused routes to handle specific route pauses instead of grouping with chain pauses * Update paused route structure * Filter for valid quotes based on paused routes * Create a Set with paused bridge module names to improve time complexity * Allow for all bridge modules to be paused with ALL * Add ability to pause bridge modules for all chains, if chainId is left undefined * Move json files to /v1/ version control folder * Compare quotes against paused bridge modules more cleanly * Paused bridge modules json control working * Fix pausedChains json * Create examples folder for pause jsons * Retrigger build * Fix banner flashing after clearing * Add padding to banner Close button * Update text sizing on progress bar * Update prop naming to prevent confusion on start/end * Clear chain pauses to ready PR * Change json file naming to be more readable * Use inputWarningMessage prop name to indicate warning placement * Pause Doge activity using Maintenance, to replace prior Chain pause mechanism * Doge chain paused chain prop values * Remove paused from/to chainId constants * Publish - @synapsecns/synapse-interface@0.21.0 * Exempt gh pages (#2541) Co-authored-by: Trajan0x * Deploy: `FastBridge` to Scroll (#2525) * chore: add Base to `.env.example` * chore: add Scroll config * chore: bump devops dependency * chore: yarn * feat: deploy `FastBridge` on scroll * Publish - FastBridge@0.2.1 * fix: update `forge-std` to 1.8.1, remove `ds-test`, use `solhint` for linting (#2545) * chore: forge-std v1.8.1, remove ds-test dep * chore: remove ds-test from remappings * refactor: state mutability * chore: add solhint * chore: yarn * fix: unused imports * fix: max line length * Publish - contracts-communication@1.3.1 - FastBridge@0.2.2 - @synapsecns/solidity-devops@0.3.3 * chore: remove submodules from `contracts-rfq` (#2547) * build: install OZ as npm module * chore: update remappings * refactor: fix compiler warnings in test contract * chore: remove forge-std submodule * chore: remove `openzeppelin-contracts` submodule * fix: restore padding in `.gitmodules` * Publish - FastBridge@0.2.3 * gogenerate * Revert "gogenerate" This reverts commit b40e602b590d0a74193f0a004712fd9bb3d2595c. * im dumb * generate * tidy * update swagger doc * [goreleaser] * [goreleaser] --------- Co-authored-by: shampoobera Co-authored-by: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Co-authored-by: bigboydiamonds Co-authored-by: trajan0x <83933037+trajan0x@users.noreply.github.com> Co-authored-by: Trajan0x Co-authored-by: χ² <88190723+ChiTimesChi@users.noreply.github.com> Co-authored-by: ChiTimesChi --- .vscode/settings.json | 14 +- contrib/screener-api/client/client.go | 93 ++++++++++ contrib/screener-api/config/config.go | 4 + contrib/screener-api/db/db.go | 27 ++- contrib/screener-api/db/db_test.go | 57 +++++- contrib/screener-api/db/models.go | 19 +- contrib/screener-api/db/sql/base/base.go | 61 ++++++- contrib/screener-api/db/sql/base/namer.go | 15 ++ contrib/screener-api/db/sql/mysql/store.go | 4 +- contrib/screener-api/db/sql/store.go | 3 +- contrib/screener-api/db/suite_test.go | 17 +- contrib/screener-api/docs/docs.go | 190 ++++++++++++++++++++ contrib/screener-api/docs/swagger.json | 161 +++++++++++++++++ contrib/screener-api/docs/swagger.yaml | 108 +++++++++++ contrib/screener-api/go.mod | 10 ++ contrib/screener-api/go.sum | 37 ++++ contrib/screener-api/main.go | 5 +- contrib/screener-api/screener/screener.go | 132 +++++++++++++- contrib/screener-api/screener/suite_test.go | 43 ++++- ethergo/go.mod | 2 +- services/rfq/go.mod | 5 +- services/rfq/go.sum | 7 +- 22 files changed, 974 insertions(+), 40 deletions(-) create mode 100644 contrib/screener-api/docs/docs.go create mode 100644 contrib/screener-api/docs/swagger.json create mode 100644 contrib/screener-api/docs/swagger.yaml diff --git a/.vscode/settings.json b/.vscode/settings.json index 403538f3f6..b494f296d2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,10 +4,13 @@ "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "eslint.workingDirectories": [ - { "directory": "packages/contracts", "changeProcessCWD": true } + { + "directory": "packages/contracts", + "changeProcessCWD": true + } ], "eslint.nodePath": "./node_modules/eslint/bin/", "eslint.format.enable": true, @@ -15,6 +18,7 @@ "files.trimTrailingWhitespace": true, "solidity.packageDefaultDependenciesContractsDirectory": "contracts", "solidity.packageDefaultDependenciesDirectory": "lib", - "solidity.compileUsingRemoteVersion": "v0.8.17", - "solidity.formatter": "prettier" -} + "solidity.compileUsingRemoteVersion": "v0.8.17+commit.8df45f5f", + "solidity.formatter": "prettier", + "solidity.defaultCompiler": "remote" +} \ No newline at end of file diff --git a/contrib/screener-api/client/client.go b/contrib/screener-api/client/client.go index c6f841622d..48e438175f 100644 --- a/contrib/screener-api/client/client.go +++ b/contrib/screener-api/client/client.go @@ -3,7 +3,13 @@ package client import ( "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" "fmt" + "strings" + "time" + "github.com/dubonzi/otelresty" "github.com/go-resty/resty/v2" "github.com/google/uuid" @@ -11,9 +17,15 @@ import ( "github.com/synapsecns/sanguine/core/metrics" ) +var ( + // BlacklistEndpoint is the endpoint for blacklisting an address. + BlacklistEndpoint = "/api/data/sync/" +) + // ScreenerClient is an interface for the Screener API. type ScreenerClient interface { ScreenAddress(ctx context.Context, ruleset, address string) (blocked bool, err error) + BlacklistAddress(ctx context.Context, appsecret string, appid string, body BlackListBody) (string, error) } type clientImpl struct { @@ -37,6 +49,7 @@ type blockedResponse struct { Blocked bool `json:"risk"` } +// ScreenAddress checks if an address is blocked by the screener. func (c clientImpl) ScreenAddress(ctx context.Context, ruleset, address string) (bool, error) { var blockedRes blockedResponse resp, err := c.rClient.R(). @@ -54,6 +67,54 @@ func (c clientImpl) ScreenAddress(ctx context.Context, ruleset, address string) return blockedRes.Blocked, nil } +// BlackListBody is the json payload that represents a blacklisted address. +type BlackListBody struct { + TypeReq string `json:"typereq"` + ID string `json:"id"` + Data string `json:"data"` + Address string `json:"address"` + Network string `json:"network"` + Tag string `json:"tag"` + Remark string `json:"remark"` +} + +type blacklistResponse struct { + Status string `json:"status"` + Error string `json:"error"` +} + +func (c clientImpl) BlacklistAddress(ctx context.Context, appsecret string, appid string, body BlackListBody) (string, error) { + var blacklistRes blacklistResponse + + nonce := strings.ReplaceAll(uuid.New().String(), "-", "")[:32] + timestamp := fmt.Sprintf("%d", time.Now().Unix()) + queryString := "" + + signature := GenerateSignature(appsecret, appid, timestamp, nonce, queryString, body) + + resp, err := c.rClient.R(). + SetContext(ctx). + SetHeader("Content-Type", "application/json"). + SetHeader("appid", appid). + SetHeader("timestamp", timestamp). + SetHeader("nonce", nonce). + SetHeader("queryString", queryString). + SetHeader("signature", signature). + SetResult(&blacklistRes). + SetBody(body). + Post(BlacklistEndpoint) + + if err != nil { + return resp.Status(), fmt.Errorf("error from server: %s: %w", resp.String(), err) + } + + if resp.IsError() { + return resp.Status(), fmt.Errorf("error from server: %s", resp.String()) + } + + return blacklistRes.Status, nil +} + // NewNoOpClient creates a new no-op client for the Screener API. // it returns false for every address. func NewNoOpClient() (ScreenerClient, error) { @@ -66,4 +127,36 @@ func (n noOpClient) ScreenAddress(_ context.Context, _, _ string) (bool, error) return false, nil } +func (n noOpClient) BlacklistAddress(_ context.Context, _ string, _ string, _ BlackListBody) (string, error) { + return "", nil +} + +// GenerateSignature generates a signature for the request. +func GenerateSignature(secret string, + appid string, + timestamp string, + nonce string, + queryString string, + body BlackListBody, +) string { + key := []byte(secret) + + // Concatenate the body. + message := fmt.Sprintf( + "%s%s%s%s%s%s%s", + appid, + timestamp, + nonce, + "POST", + BlacklistEndpoint, + queryString, + body, + ) + + h := hmac.New(sha256.New, key) + h.Write([]byte(message)) + + return strings.ToLower(hex.EncodeToString(h.Sum(nil))) +} + var _ ScreenerClient = noOpClient{} diff --git a/contrib/screener-api/config/config.go b/contrib/screener-api/config/config.go index 564d75d74e..4b5bb10283 100644 --- a/contrib/screener-api/config/config.go +++ b/contrib/screener-api/config/config.go @@ -4,6 +4,10 @@ import "time" // Config is the configuration for the screener. type Config struct { + // AppSecret is the app secret + AppSecret string `yaml:"app-secret"` + // AppID is the app id + AppID string `yaml:"app-id"` // TRMKey is the api key for trmlabs TRMKey string `yaml:"trm-key"` // Rules of [caller_type]->risk_type diff --git a/contrib/screener-api/db/db.go b/contrib/screener-api/db/db.go index f52cfd4aa4..f7bc78d33a 100644 --- a/contrib/screener-api/db/db.go +++ b/contrib/screener-api/db/db.go @@ -4,10 +4,29 @@ package db import ( "context" "errors" - "github.com/synapsecns/sanguine/contrib/screener-api/trmlabs" "time" + + "github.com/synapsecns/sanguine/contrib/screener-api/trmlabs" ) +// BlacklistedAddressWriterDB provides methods to write blacklisted addresses to the database. +type BlacklistedAddressWriterDB interface { + PutBlacklistedAddress(ctx context.Context, body BlacklistedAddress) error + DeleteBlacklistedAddress(ctx context.Context, id string) error + UpdateBlacklistedAddress(ctx context.Context, id string, body BlacklistedAddress) error +} + +// BlacklistedAddressReaderDB provides methods to read blacklisted addresses from the database. +type BlacklistedAddressReaderDB interface { + GetBlacklistedAddress(ctx context.Context, address string) (*BlacklistedAddress, error) +} + +// BlacklistedAddressDB is the interface for reading and writing blacklisted addresses to the database. +type BlacklistedAddressDB interface { + BlacklistedAddressWriterDB + BlacklistedAddressReaderDB +} + // RuleWriterDB is the interface for writing rules to the database. type RuleWriterDB interface { PutAddressIndicators(ctx context.Context, address string, riskIndicator []trmlabs.AddressRiskIndicator) error @@ -24,5 +43,11 @@ type RuleDB interface { RuleReaderDB } +// DB is the general database interface for the screener-api. +type DB interface { + BlacklistedAddressDB + RuleDB +} + // ErrNoAddressNotCached is returned when an address is not cached. var ErrNoAddressNotCached = errors.New("address not cached") diff --git a/contrib/screener-api/db/db_test.go b/contrib/screener-api/db/db_test.go index bc98c43f34..05eaabcaa3 100644 --- a/contrib/screener-api/db/db_test.go +++ b/contrib/screener-api/db/db_test.go @@ -1,14 +1,16 @@ package db_test import ( + "time" + "github.com/brianvoe/gofakeit/v6" "github.com/synapsecns/sanguine/contrib/screener-api/db" "github.com/synapsecns/sanguine/contrib/screener-api/trmlabs" - "time" + "gorm.io/gorm" ) func (d *DBSuite) TestEmpty() { - d.RunOnAllDBs(func(testDB db.RuleDB) { + d.RunOnAllDBs(func(testDB db.DB) { testAddress := gofakeit.BitcoinAddress() // 5 mins ago @@ -28,8 +30,8 @@ func (d *DBSuite) TestEmpty() { }) } -func (d *DBSuite) TestAdressUpdate() { - d.RunOnAllDBs(func(testDB db.RuleDB) { +func (d *DBSuite) TestAddressUpdate() { + d.RunOnAllDBs(func(testDB db.DB) { testAddress := gofakeit.BitcoinAddress() // 5 mins ago @@ -64,3 +66,50 @@ func (d *DBSuite) TestAdressUpdate() { d.Require().Error(err, db.ErrNoAddressNotCached) }) } + +func (d *DBSuite) TestBlacklist() { + d.RunOnAllDBs(func(testDB db.DB) { + testAddress := gofakeit.BitcoinAddress() + + blacklistBody := db.BlacklistedAddress{ + TypeReq: "create", + ID: "testId", + Address: testAddress, + Network: "bitcoin", + Tag: "testTag", + Remark: "testRemark", + } + + // blacklist the address + err := testDB.PutBlacklistedAddress(d.GetTestContext(), blacklistBody) + d.Require().NoError(err) + blacklistedAddress, err := testDB.GetBlacklistedAddress(d.GetTestContext(), blacklistBody.Address) + d.Require().NoError(err) + d.Require().NotNil(blacklistedAddress) + + // update the address + blacklistBody.TypeReq = "update" + blacklistBody.Remark = "testRemarkUpdated" + err = testDB.UpdateBlacklistedAddress(d.GetTestContext(), blacklistBody.ID, blacklistBody) + d.Require().NoError(err) + + // check to make sure it updated + blacklistedAddress, err = testDB.GetBlacklistedAddress(d.GetTestContext(), blacklistBody.Address) + d.Require().NoError(err) + d.Require().NotNil(blacklistedAddress) + d.Require().Equal("testRemarkUpdated", blacklistedAddress.Remark) + + // check for non blacklisted address + res, err := testDB.GetBlacklistedAddress(d.GetTestContext(), gofakeit.BitcoinAddress()) + d.Require().EqualError(err, gorm.ErrRecordNotFound.Error()) + d.Require().Nil(res) + + // delete it + err = testDB.DeleteBlacklistedAddress(d.GetTestContext(), blacklistBody.ID) + d.Require().NoError(err) + + // delete nonexistent + err = testDB.DeleteBlacklistedAddress(d.GetTestContext(), "NonexistentId") + d.Require().NoError(err) + }) +} diff --git a/contrib/screener-api/db/models.go b/contrib/screener-api/db/models.go index 9c458221a3..bcc89e3f4c 100644 --- a/contrib/screener-api/db/models.go +++ b/contrib/screener-api/db/models.go @@ -8,12 +8,27 @@ import ( "encoding/json" "errors" "fmt" - "github.com/synapsecns/sanguine/contrib/screener-api/trmlabs" - "gorm.io/gorm/schema" "strings" "time" + + "github.com/synapsecns/sanguine/contrib/screener-api/trmlabs" + "gorm.io/gorm/schema" ) +// BlacklistedAddress is a blacklisted address. +type BlacklistedAddress struct { + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + TypeReq string `gorm:"column:typereq" json:"typereq"` + ID string `gorm:"column:id;primary_key" json:"id"` + Data string `gorm:"column:data" json:"data"` + Address string `gorm:"column:address" json:"address"` + Network string `gorm:"column:network" json:"network"` + Tag string `gorm:"column:tag" json:"tag"` + Remark string `gorm:"column:remark" json:"remark"` +} + // AddressIndicators is the address indicators for a given address. type AddressIndicators struct { CreatedAt time.Time diff --git a/contrib/screener-api/db/sql/base/base.go b/contrib/screener-api/db/sql/base/base.go index 6325234d96..91e9fca664 100644 --- a/contrib/screener-api/db/sql/base/base.go +++ b/contrib/screener-api/db/sql/base/base.go @@ -5,13 +5,14 @@ import ( "context" "errors" "fmt" + "strings" + "time" + "github.com/synapsecns/sanguine/contrib/screener-api/db" "github.com/synapsecns/sanguine/contrib/screener-api/trmlabs" "github.com/synapsecns/sanguine/core/metrics" "gorm.io/gorm" "gorm.io/gorm/clause" - "strings" - "time" ) // Store is a store that implements an underlying gorm db. @@ -29,10 +30,66 @@ func NewStore(db *gorm.DB, metrics metrics.Handler) *Store { // see: https://medium.com/@SaifAbid/slice-interfaces-8c78f8b6345d for an explanation of why we can't do this at initialization time func GetAllModels() (allModels []interface{}) { allModels = append(allModels, &db.AddressIndicators{}) + allModels = append(allModels, &db.BlacklistedAddress{}) return allModels } +// GetBlacklistedAddress queries the db for the blacklisted address. +// Returns true if the address is blacklisted, false otherwise. +// Not used currently. +func (s *Store) GetBlacklistedAddress(ctx context.Context, address string) (*db.BlacklistedAddress, error) { + var blacklistedAddress db.BlacklistedAddress + + if err := s.db.WithContext(ctx).Where("address = ?", address). + First(&blacklistedAddress).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return nil, fmt.Errorf("failed to get blacklisted address: %w", err) + } + + return &blacklistedAddress, nil +} + +// PutBlacklistedAddress puts the blacklisted address in the underlying db. +func (s *Store) PutBlacklistedAddress(ctx context.Context, body db.BlacklistedAddress) error { + dbTx := s.db.WithContext(ctx).Model(&db.BlacklistedAddress{}). + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: idName}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + idName, typeReqName, dataName, addressName, networkName, tagName, remarkName}, + ), + }).Create(&body) + if dbTx.Error != nil { + return fmt.Errorf("failed to store blacklisted address: %w", dbTx.Error) + } + + return nil +} + +// UpdateBlacklistedAddress updates the blacklisted address in the underlying db. +func (s *Store) UpdateBlacklistedAddress(ctx context.Context, id string, body db.BlacklistedAddress) error { + dbTx := s.db.WithContext(ctx).Model(&db.BlacklistedAddress{}). + Where("id = ?", id).Updates(body) + if dbTx.Error != nil { + return fmt.Errorf("failed to update blacklisted address: %w", dbTx.Error) + } + + return nil +} + +// DeleteBlacklistedAddress deletes the blacklisted address from the underlying db. +func (s *Store) DeleteBlacklistedAddress(ctx context.Context, id string) error { + if dbTx := s.db.WithContext(ctx).Where( + "id = ?", id).Delete(&db.BlacklistedAddress{}); dbTx.Error != nil { + return fmt.Errorf("failed to delete blacklisted address: %w", dbTx.Error) + } + return nil +} + // GetAddressIndicators gets the address indicators for the given address. func (s *Store) GetAddressIndicators(ctx context.Context, address string, since time.Time) ([]trmlabs.AddressRiskIndicator, error) { var addressIndicators db.AddressIndicators diff --git a/contrib/screener-api/db/sql/base/namer.go b/contrib/screener-api/db/sql/base/namer.go index 7df79ffbc3..7d305d7052 100644 --- a/contrib/screener-api/db/sql/base/namer.go +++ b/contrib/screener-api/db/sql/base/namer.go @@ -6,11 +6,26 @@ import ( func init() { namer := dbcommon.NewNamer(GetAllModels()) + addressName = namer.GetConsistentName("Address") indicatorName = namer.GetConsistentName("Indicators") + + typeReqName = namer.GetConsistentName("TypeReq") + idName = namer.GetConsistentName("ID") + dataName = namer.GetConsistentName("Data") + networkName = namer.GetConsistentName("Network") + tagName = namer.GetConsistentName("Tag") + remarkName = namer.GetConsistentName("Remark") } var ( addressName string indicatorName string + + typeReqName string + idName string + dataName string + networkName string + tagName string + remarkName string ) diff --git a/contrib/screener-api/db/sql/mysql/store.go b/contrib/screener-api/db/sql/mysql/store.go index dc6a60db3f..fbfd5b58e9 100644 --- a/contrib/screener-api/db/sql/mysql/store.go +++ b/contrib/screener-api/db/sql/mysql/store.go @@ -3,9 +3,10 @@ package mysql import ( "context" "fmt" + "time" + "github.com/synapsecns/sanguine/contrib/screener-api/db" "github.com/synapsecns/sanguine/contrib/screener-api/db/sql/base" - "time" "github.com/ipfs/go-log" common_base "github.com/synapsecns/sanguine/core/dbcommon" @@ -64,3 +65,4 @@ var MaxIdleConns = 10 var NamingStrategy = schema.NamingStrategy{} var _ db.RuleDB = &Store{} +var _ db.BlacklistedAddressDB = &Store{} diff --git a/contrib/screener-api/db/sql/store.go b/contrib/screener-api/db/sql/store.go index 432e7e249a..a21924333a 100644 --- a/contrib/screener-api/db/sql/store.go +++ b/contrib/screener-api/db/sql/store.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "github.com/synapsecns/sanguine/contrib/screener-api/db" "github.com/synapsecns/sanguine/contrib/screener-api/db/sql/mysql" "github.com/synapsecns/sanguine/contrib/screener-api/db/sql/sqlite" @@ -13,7 +14,7 @@ import ( ) // Connect connects to the database. -func Connect(ctx context.Context, dbType dbcommon.DBType, path string, metrics metrics.Handler) (db.RuleDB, error) { +func Connect(ctx context.Context, dbType dbcommon.DBType, path string, metrics metrics.Handler) (db.DB, error) { switch dbType { case dbcommon.Mysql: store, err := mysql.NewMysqlStore(ctx, path, metrics) diff --git a/contrib/screener-api/db/suite_test.go b/contrib/screener-api/db/suite_test.go index b88c76dd38..42114e3731 100644 --- a/contrib/screener-api/db/suite_test.go +++ b/contrib/screener-api/db/suite_test.go @@ -3,13 +3,14 @@ package db_test import ( dbSQL "database/sql" "fmt" + "os" + "sync" + "testing" + "github.com/synapsecns/sanguine/contrib/screener-api/db" "github.com/synapsecns/sanguine/contrib/screener-api/db/sql" "github.com/synapsecns/sanguine/contrib/screener-api/db/sql/mysql" "github.com/synapsecns/sanguine/contrib/screener-api/metadata" - "os" - "sync" - "testing" "github.com/Flaque/filet" . "github.com/stretchr/testify/assert" @@ -24,7 +25,7 @@ import ( type DBSuite struct { *testsuite.TestSuite - dbs []db.RuleDB + dbs []db.DB metrics metrics.Handler } @@ -33,7 +34,7 @@ func NewDBSuite(tb testing.TB) *DBSuite { tb.Helper() return &DBSuite{ TestSuite: testsuite.NewTestSuite(tb), - dbs: []db.RuleDB{}, + dbs: []db.DB{}, } } func (d *DBSuite) SetupSuite() { @@ -60,7 +61,7 @@ func (d *DBSuite) SetupTest() { sqliteStore, err := sql.Connect(d.GetTestContext(), dbcommon.Sqlite, filet.TmpDir(d.T(), ""), d.metrics) Nil(d.T(), err) - d.dbs = []db.RuleDB{sqliteStore} + d.dbs = []db.DB{sqliteStore} d.setupMysqlDB() } @@ -89,14 +90,14 @@ func (d *DBSuite) setupMysqlDB() { d.dbs = append(d.dbs, mysqlStore) } -func (d *DBSuite) RunOnAllDBs(testFunc func(testDB db.RuleDB)) { +func (d *DBSuite) RunOnAllDBs(testFunc func(testDB db.DB)) { d.T().Helper() wg := sync.WaitGroup{} for _, testDB := range d.dbs { wg.Add(1) // capture the value - go func(testDB db.RuleDB) { + go func(testDB db.DB) { defer wg.Done() testFunc(testDB) }(testDB) diff --git a/contrib/screener-api/docs/docs.go b/contrib/screener-api/docs/docs.go new file mode 100644 index 0000000000..ec00e7b428 --- /dev/null +++ b/contrib/screener-api/docs/docs.go @@ -0,0 +1,190 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/data/sync": { + "post": { + "description": "blacklist an address", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "blacklist an address", + "parameters": [ + { + "type": "string", + "description": "Application ID", + "name": "appid", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Timestamp of the request", + "name": "timestamp", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "A unique nonce for the request", + "name": "nonce", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Query string parameters included in the request", + "name": "queryString", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Signature for request validation", + "name": "signature", + "in": "header", + "required": true + }, + { + "description": "Blacklist request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/db.BlacklistedAddress" + } + } + ], + "responses": {} + } + }, + "/screen/{ruleset}/{address}": { + "get": { + "description": "Assess the risk associated with a given address using specified rulesets.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "address" + ], + "summary": "Screen address for risk", + "parameters": [ + { + "type": "string", + "description": "Ruleset to use for screening the address", + "name": "ruleset", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Address to be screened", + "name": "address", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "Returns the risk assessment result", + "schema": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + } + }, + "400": { + "description": "Returns error if the required parameters are missing or invalid", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Returns error if there are problems processing the indicators", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + }, + "definitions": { + "db.BlacklistedAddress": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "data": { + "type": "string" + }, + "id": { + "type": "string" + }, + "network": { + "type": "string" + }, + "remark": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "typereq": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "", + Description: "", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/contrib/screener-api/docs/swagger.json b/contrib/screener-api/docs/swagger.json new file mode 100644 index 0000000000..ef9bf8b0e5 --- /dev/null +++ b/contrib/screener-api/docs/swagger.json @@ -0,0 +1,161 @@ +{ + "swagger": "2.0", + "info": { + "contact": {} + }, + "paths": { + "/api/data/sync": { + "post": { + "description": "blacklist an address", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "blacklist an address", + "parameters": [ + { + "type": "string", + "description": "Application ID", + "name": "appid", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Timestamp of the request", + "name": "timestamp", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "A unique nonce for the request", + "name": "nonce", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Query string parameters included in the request", + "name": "queryString", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Signature for request validation", + "name": "signature", + "in": "header", + "required": true + }, + { + "description": "Blacklist request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/db.BlacklistedAddress" + } + } + ], + "responses": {} + } + }, + "/screen/{ruleset}/{address}": { + "get": { + "description": "Assess the risk associated with a given address using specified rulesets.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "address" + ], + "summary": "Screen address for risk", + "parameters": [ + { + "type": "string", + "description": "Ruleset to use for screening the address", + "name": "ruleset", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Address to be screened", + "name": "address", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "Returns the risk assessment result", + "schema": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + } + }, + "400": { + "description": "Returns error if the required parameters are missing or invalid", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Returns error if there are problems processing the indicators", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + }, + "definitions": { + "db.BlacklistedAddress": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "data": { + "type": "string" + }, + "id": { + "type": "string" + }, + "network": { + "type": "string" + }, + "remark": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "typereq": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/contrib/screener-api/docs/swagger.yaml b/contrib/screener-api/docs/swagger.yaml new file mode 100644 index 0000000000..aa75918ffc --- /dev/null +++ b/contrib/screener-api/docs/swagger.yaml @@ -0,0 +1,108 @@ +definitions: + db.BlacklistedAddress: + properties: + address: + type: string + createdAt: + type: string + data: + type: string + id: + type: string + network: + type: string + remark: + type: string + tag: + type: string + typereq: + type: string + updatedAt: + type: string + type: object +info: + contact: {} +paths: + /api/data/sync: + post: + consumes: + - application/json + description: blacklist an address + parameters: + - description: Application ID + in: header + name: appid + required: true + type: string + - description: Timestamp of the request + in: header + name: timestamp + required: true + type: string + - description: A unique nonce for the request + in: header + name: nonce + required: true + type: string + - description: Query string parameters included in the request + in: header + name: queryString + required: true + type: string + - description: Signature for request validation + in: header + name: signature + required: true + type: string + - description: Blacklist request + in: body + name: request + required: true + schema: + $ref: '#/definitions/db.BlacklistedAddress' + produces: + - application/json + responses: {} + summary: blacklist an address + /screen/{ruleset}/{address}: + get: + consumes: + - application/json + description: Assess the risk associated with a given address using specified + rulesets. + parameters: + - description: Ruleset to use for screening the address + in: query + name: ruleset + required: true + type: string + - description: Address to be screened + in: query + name: address + required: true + type: string + produces: + - application/json + responses: + "200": + description: Returns the risk assessment result + schema: + additionalProperties: + type: boolean + type: object + "400": + description: Returns error if the required parameters are missing or invalid + schema: + additionalProperties: + type: string + type: object + "500": + description: Returns error if there are problems processing the indicators + schema: + additionalProperties: + type: string + type: object + summary: Screen address for risk + tags: + - address +swagger: "2.0" diff --git a/contrib/screener-api/go.mod b/contrib/screener-api/go.mod index ea8eabce04..dc228d24f7 100644 --- a/contrib/screener-api/go.mod +++ b/contrib/screener-api/go.mod @@ -18,6 +18,9 @@ require ( github.com/ipfs/go-log v1.0.5 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/stretchr/testify v1.8.4 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + github.com/swaggo/swag v1.16.3 github.com/synapsecns/sanguine/core v0.0.0-00010101000000-000000000000 github.com/urfave/cli/v2 v2.27.1 go.opentelemetry.io/otel v1.23.1 @@ -33,6 +36,7 @@ require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect @@ -70,6 +74,10 @@ require ( github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.3 // indirect @@ -93,12 +101,14 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.6 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect diff --git a/contrib/screener-api/go.sum b/contrib/screener-api/go.sum index 5587ef50bf..5263afa49b 100644 --- a/contrib/screener-api/go.sum +++ b/contrib/screener-api/go.sum @@ -46,6 +46,8 @@ github.com/Flaque/filet v0.0.0-20201012163910-45f684403088 h1:PnnQln5IGbhLeJOi6h github.com/Flaque/filet v0.0.0-20201012163910-45f684403088/go.mod h1:TK+jB3mBs+8ZMWhU5BqZKnZWJ1MrLo8etNVg51ueTBo= github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 h1:ClzzXMDDuUbWfNNZqGeYq4PnYOlwlOVIvSyNaIy0ykg= github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3/go.mod h1:we0YA5CsBbH5+/NUzC/AlMmxaDtWlXeNsqrwXjTzmzA= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -53,6 +55,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -144,6 +148,8 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/requestid v0.0.6 h1:mGcxTnHQ45F6QU5HQRgQUDsAfHprD3P7g2uZ4cSZo9o= github.com/gin-contrib/requestid v0.0.6/go.mod h1:9i4vKATX/CdggbkY252dPVasgVucy/ggBeELXuQztm4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -178,6 +184,18 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -302,6 +320,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -334,6 +354,11 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -366,6 +391,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -451,6 +477,12 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= +github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg= github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= @@ -636,10 +668,12 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= @@ -713,6 +747,7 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -933,6 +968,7 @@ google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -945,6 +981,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/contrib/screener-api/main.go b/contrib/screener-api/main.go index a7fd843b33..9d8017c0d0 100644 --- a/contrib/screener-api/main.go +++ b/contrib/screener-api/main.go @@ -2,11 +2,14 @@ package main import ( + "os" + "github.com/synapsecns/sanguine/contrib/screener-api/cmd" "github.com/synapsecns/sanguine/contrib/screener-api/metadata" - "os" ) +//go:generate go run github.com/swaggo/swag/cmd/swag init + func main() { cmd.Start(os.Args, metadata.BuildInfo()) } diff --git a/contrib/screener-api/screener/screener.go b/contrib/screener-api/screener/screener.go index 4d7ea3a3a4..50fde4ae68 100644 --- a/contrib/screener-api/screener/screener.go +++ b/contrib/screener-api/screener/screener.go @@ -12,10 +12,13 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "github.com/ipfs/go-log" + "github.com/synapsecns/sanguine/contrib/screener-api/client" "github.com/synapsecns/sanguine/contrib/screener-api/config" "github.com/synapsecns/sanguine/contrib/screener-api/db" "github.com/synapsecns/sanguine/contrib/screener-api/db/sql" + "github.com/synapsecns/sanguine/contrib/screener-api/docs" "github.com/synapsecns/sanguine/contrib/screener-api/screener/internal" "github.com/synapsecns/sanguine/contrib/screener-api/trmlabs" "github.com/synapsecns/sanguine/core" @@ -26,6 +29,9 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" "golang.org/x/exp/slices" + + swaggerfiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) // Screener is the interface for the screener. @@ -36,7 +42,7 @@ type Screener interface { type screenerImpl struct { rulesManager internal.RulesetManager thresholds []config.VolumeThreshold - db db.RuleDB + db db.DB router *gin.Engine metrics metrics.Handler cfg config.Config @@ -55,6 +61,9 @@ func NewScreener(ctx context.Context, cfg config.Config, metricHandler metrics.H cfg: cfg, } + docs.SwaggerInfo.Title = "Screener API" + docs.SwaggerInfo.Host = fmt.Sprintf("localhost:%d", cfg.Port) + screener.client, err = trmlabs.NewClient(cfg.TRMKey, core.GetEnv("TRM_URL", "https://api.trmlabs.com")) if err != nil { return nil, fmt.Errorf("could not create trm client: %w", err) @@ -77,12 +86,16 @@ func NewScreener(ctx context.Context, cfg config.Config, metricHandler metrics.H screener.db, err = sql.Connect(ctx, dbType, cfg.Database.DSN, metricHandler) if err != nil { - return nil, fmt.Errorf("could not connect to db: %w", err) + return nil, fmt.Errorf("could not connect to rules db: %w", err) } screener.router = ginhelper.New(logger) screener.router.Handle(http.MethodGet, "/:ruleset/address/:address", screener.screenAddress) + screener.router.Handle(http.MethodPost, "/api/data/sync", screener.authMiddleware(cfg), screener.blacklistAddress) + + screener.router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) + return &screener, nil } @@ -118,6 +131,107 @@ func (s *screenerImpl) fetchBlacklist(ctx context.Context) { } } +// @dev Protected Method +// @Summary blacklist an address +// @Description blacklist an address +// @Param appid header string true "Application ID" +// @Param timestamp header string true "Timestamp of the request" +// @Param nonce header string true "A unique nonce for the request" +// @Param queryString header string true "Query string parameters included in the request" +// @Param signature header string true "Signature for request validation" +// @Param request body db.BlacklistedAddress true "Blacklist request" +// @Accept json +// @Produce json +// @Router /api/data/sync [post]. +func (s *screenerImpl) blacklistAddress(c *gin.Context) { + var blacklistBody client.BlackListBody + + // Grab the body of the JSON request and unmarshal it into the blacklistBody struct. + if err := c.ShouldBindBodyWith(&blacklistBody, binding.JSON); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + blacklistedAddress := db.BlacklistedAddress{ + TypeReq: blacklistBody.TypeReq, + ID: blacklistBody.ID, + Data: blacklistBody.Data, + Network: blacklistBody.Network, + Tag: blacklistBody.Tag, + Remark: blacklistBody.Remark, + Address: strings.ToLower(blacklistBody.Address), + } + + switch blacklistBody.TypeReq { + case "create": + if err := s.db.PutBlacklistedAddress(c, blacklistedAddress); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "success"}) + return + + case "update": + if err := s.db.UpdateBlacklistedAddress(c, blacklistedAddress.ID, blacklistedAddress); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "success"}) + return + + case "delete": + if err := s.db.DeleteBlacklistedAddress(c, blacklistedAddress.Address); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "success"}) + return + + default: + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid type"}) + return + } +} + +// This function takes the HTTP headers and the body of the request and reconstructs the signature to +// compare it with the signature provided. If they match, the request is allowed to pass through. +func (s *screenerImpl) authMiddleware(cfg config.Config) gin.HandlerFunc { + return func(c *gin.Context) { + var blacklistBody client.BlackListBody + + if err := c.ShouldBindBodyWith(&blacklistBody, binding.JSON); err != nil { + c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()}) + c.Abort() + return + } + + appid := cfg.AppID + appsecret := cfg.AppSecret + + nonce := c.GetHeader("nonce") + timestamp := c.GetHeader("timestamp") + queryString := c.GetHeader("queryString") + if nonce == "" || timestamp == "" || appid == "" { + c.JSON(http.StatusConflict, gin.H{"error": "missing headers"}) + c.Abort() + return + } + + // reconstruct signature + expected := client.GenerateSignature(appsecret, appid, timestamp, nonce, queryString, blacklistBody) + + if c.GetHeader("Signature") != expected { + c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) + c.Abort() + return + } + c.Next() + } +} + func (s *screenerImpl) Start(ctx context.Context) error { // TODO: potential race condition here, if the blacklist is not fetched before the first request // in practice trm will catch @@ -132,11 +246,23 @@ func (s *screenerImpl) Start(ctx context.Context) error { connection := baseServer.Server{} err := connection.ListenAndServe(ctx, fmt.Sprintf(":%d", s.cfg.Port), s.router) if err != nil { - return fmt.Errorf("could not start gqlServer: %w", err) + return fmt.Errorf("could not start server: %w", err) } return nil } +// screenAddress returns whether an address is risky or not given a ruleset. +// @Summary Screen address for risk +// @Description Assess the risk associated with a given address using specified rulesets. +// @Tags address +// @Accept json +// @Produce json +// @Param ruleset query string true "Ruleset to use for screening the address" +// @Param address query string true "Address to be screened" +// @Success 200 {object} map[string]bool "Returns the risk assessment result" +// @Failure 400 {object} map[string]string "Returns error if the required parameters are missing or invalid" +// @Failure 500 {object} map[string]string "Returns error if there are problems processing the indicators" +// @Router /screen/{ruleset}/{address} [get] func (s *screenerImpl) screenAddress(c *gin.Context) { var err error diff --git a/contrib/screener-api/screener/suite_test.go b/contrib/screener-api/screener/suite_test.go index b5edbc9d5a..aa759d400e 100644 --- a/contrib/screener-api/screener/suite_test.go +++ b/contrib/screener-api/screener/suite_test.go @@ -80,7 +80,9 @@ func (s *ScreenerSuite) TestScreener() { s.T().Setenv("TRM_URL", "") cfg := config.Config{ - TRMKey: "", + AppSecret: "appsecret", + AppID: "appid", + TRMKey: "", Rulesets: map[string]config.RulesetConfig{ "testrule": { Filename: s.makeTestCSV([]screener.Set{ @@ -109,7 +111,7 @@ func (s *ScreenerSuite) TestScreener() { realScreener, err := screener.NewTestScreener(s.GetTestContext(), cfg, s.metrics) Nil(s.T(), err) - + NotNil(s.T(), realScreener) go func() { err = realScreener.Start(s.GetTestContext()) if !errors.Is(err, context.Canceled) { @@ -150,6 +152,43 @@ func (s *ScreenerSuite) TestScreener() { out, err = apiClient.ScreenAddress(s.GetTestContext(), "testrule", "0x00") Nil(s.T(), err) False(s.T(), out) + + // now test crud screener + blacklistBody := client.BlackListBody{ + TypeReq: "create", + ID: "1", + Data: "{\"test\":\"data\"}", + Address: "0x123", + Network: "eth", + Tag: "tag", + Remark: "remark", + } + + // post to the blacklist + status, err := apiClient.BlacklistAddress(s.GetTestContext(), cfg.AppSecret, cfg.AppID, blacklistBody) + Equal(s.T(), "success", status) + Nil(s.T(), err) + + // update an address on the blacklist + blacklistBody.TypeReq = "update" + blacklistBody.Remark = "new remark" + + status, err = apiClient.BlacklistAddress(s.GetTestContext(), cfg.AppSecret, cfg.AppID, blacklistBody) + Equal(s.T(), "success", status) + Nil(s.T(), err) + + // delete the address on the blacklist + blacklistBody.TypeReq = "delete" + blacklistBody.ID = "1" + + status, err = apiClient.BlacklistAddress(s.GetTestContext(), cfg.AppSecret, cfg.AppID, blacklistBody) + Equal(s.T(), "success", status) + Nil(s.T(), err) + + // unauthorized + status, err = apiClient.BlacklistAddress(s.GetTestContext(), "bad", cfg.AppID, blacklistBody) + NotEqual(s.T(), "success", status) + NotNil(s.T(), err) } type mockClient struct { diff --git a/ethergo/go.mod b/ethergo/go.mod index 34df6631d8..5e5b1efaa5 100644 --- a/ethergo/go.mod +++ b/ethergo/go.mod @@ -55,6 +55,7 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 github.com/viant/toolbox v0.24.0 go.opentelemetry.io/otel v1.23.1 + go.opentelemetry.io/otel/metric v1.23.1 go.opentelemetry.io/otel/sdk v1.21.0 go.opentelemetry.io/otel/trace v1.23.1 go.uber.org/atomic v1.11.0 @@ -248,7 +249,6 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect - go.opentelemetry.io/otel/metric v1.23.1 // indirect go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/services/rfq/go.mod b/services/rfq/go.mod index 00f8c07b5a..23877ddacc 100644 --- a/services/rfq/go.mod +++ b/services/rfq/go.mod @@ -56,8 +56,6 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/Soft/iter v0.1.0 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect @@ -135,7 +133,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -306,7 +304,6 @@ require ( k8s.io/apimachinery v0.25.5 // indirect k8s.io/klog/v2 v2.80.1 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) replace ( diff --git a/services/rfq/go.sum b/services/rfq/go.sum index c63f8aa8ec..c0f61ba124 100644 --- a/services/rfq/go.sum +++ b/services/rfq/go.sum @@ -102,9 +102,7 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Soft/iter v0.1.0 h1:fEWgwubt0cnnJo3Vd2IzYcaJ5ORI/dJDkaC3loI7Ys8= @@ -448,8 +446,9 @@ github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -1759,5 +1758,3 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=