Skip to content

Commit

Permalink
chore: add gosec step in gha
Browse files Browse the repository at this point in the history
  • Loading branch information
aramalipoor authored and OBlackmon3 committed Sep 13, 2024
1 parent b95b9be commit ad34c22
Show file tree
Hide file tree
Showing 19 changed files with 93 additions and 45 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ jobs:
run: make build
- name: Test with the Go CLI
run: make test
- name: Run Gosec Security Scanner
uses: securego/gosec@master
with:
args: -exclude-dir=test -tests=false ./...
- name: Generate coverage report
run: make coverage
- name: Upload coverage reports to Codecov
Expand Down
12 changes: 6 additions & 6 deletions common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,11 @@ type RetryPolicyConfig struct {
}

type CircuitBreakerPolicyConfig struct {
FailureThresholdCount int `yaml:"failureThresholdCount" json:"failureThresholdCount"`
FailureThresholdCapacity int `yaml:"failureThresholdCapacity" json:"failureThresholdCapacity"`
FailureThresholdCount uint `yaml:"failureThresholdCount" json:"failureThresholdCount"`
FailureThresholdCapacity uint `yaml:"failureThresholdCapacity" json:"failureThresholdCapacity"`
HalfOpenAfter string `yaml:"halfOpenAfter" json:"halfOpenAfter"`
SuccessThresholdCount int `yaml:"successThresholdCount" json:"successThresholdCount"`
SuccessThresholdCapacity int `yaml:"successThresholdCapacity" json:"successThresholdCapacity"`
SuccessThresholdCount uint `yaml:"successThresholdCount" json:"successThresholdCount"`
SuccessThresholdCapacity uint `yaml:"successThresholdCapacity" json:"successThresholdCapacity"`
}

type TimeoutPolicyConfig struct {
Expand All @@ -240,7 +240,7 @@ type RateLimitBudgetConfig struct {

type RateLimitRuleConfig struct {
Method string `yaml:"method" json:"method"`
MaxCount int `yaml:"maxCount" json:"maxCount"`
MaxCount uint `yaml:"maxCount" json:"maxCount"`
Period string `yaml:"period" json:"period"`
WaitTime string `yaml:"waitTime" json:"waitTime"`
}
Expand Down Expand Up @@ -367,7 +367,7 @@ func (c *Config) GetProjectConfig(projectId string) *ProjectConfig {

func (c *RateLimitRuleConfig) MarshalZerologObject(e *zerolog.Event) {
e.Str("method", c.Method).
Int("maxCount", c.MaxCount).
Uint("maxCount", c.MaxCount).
Str("period", c.Period).
Str("waitTime", c.WaitTime)
}
Expand Down
10 changes: 5 additions & 5 deletions common/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,10 @@ func (r *NormalizedRequest) ApplyDirectivesFromHttp(
queryArgs *fasthttp.Args,
) {
drc := &RequestDirectives{
RetryEmpty: string(headers.Peek("X-ERPC-Retry-Empty")) != "false",
RetryPending: string(headers.Peek("X-ERPC-Retry-Pending")) != "false",
SkipCacheRead: string(headers.Peek("X-ERPC-Skip-Cache-Read")) == "true",
UseUpstream: string(headers.Peek("X-ERPC-Use-Upstream")),
RetryEmpty: string(headers.Peek("X-ERPC-Retry-Empty")) != "false",
RetryPending: string(headers.Peek("X-ERPC-Retry-Pending")) != "false",
SkipCacheRead: string(headers.Peek("X-ERPC-Skip-Cache-Read")) == "true",
UseUpstream: string(headers.Peek("X-ERPC-Use-Upstream")),
}

if useUpstream := string(queryArgs.Peek("use-upstream")); useUpstream != "" {
Expand Down Expand Up @@ -235,7 +235,7 @@ func (r *NormalizedRequest) JsonRpcRequest() (*JsonRpcRequest, error) {
}

if rpcReq.ID == nil {
rpcReq.ID = float64(rand.Intn(math.MaxInt32))
rpcReq.ID = rand.Intn(math.MaxInt32) // #nosec G404
}

r.jsonRpcRequest = rpcReq
Expand Down
2 changes: 1 addition & 1 deletion data/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (r *RedisConnector) connect(ctx context.Context, cfg *common.RedisConnector

func createTLSConfig(tlsCfg *common.TLSConfig) (*tls.Config, error) {
config := &tls.Config{
InsecureSkipVerify: tlsCfg.InsecureSkipVerify,
InsecureSkipVerify: tlsCfg.InsecureSkipVerify, // #nosec G402
}

if tlsCfg.CertFile != "" && tlsCfg.KeyFile != "" {
Expand Down
33 changes: 27 additions & 6 deletions erpc/http_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,23 @@ func (s *HttpServer) createRequestHandler(mainCtx context.Context, reqMaxTimeout

if isBatch {
fastCtx.SetStatusCode(fasthttp.StatusOK)
encoder.Encode(responses)
err = encoder.Encode(responses)
if err != nil {
fastCtx.SetStatusCode(fasthttp.StatusInternalServerError)
fastCtx.Response.Header.Set("Content-Type", "application/json")
fastCtx.SetBodyString(fmt.Sprintf(`{"jsonrpc":"2.0","error":{"code":-32603,"message":"%s"}}`, err.Error()))
return
}
} else {
res := responses[0]
setResponseHeaders(res, fastCtx)
setResponseStatusCode(res, fastCtx)
encoder.Encode(res)
err = encoder.Encode(res)
if err != nil {
fastCtx.SetStatusCode(fasthttp.StatusInternalServerError)
fastCtx.SetBodyString(fmt.Sprintf(`{"jsonrpc":"2.0","error":{"code":-32603,"message":"%s"}}`, err.Error()))
return
}
}

fastCtx.SetBody(buf.Bytes())
Expand Down Expand Up @@ -450,9 +461,16 @@ func decideErrorStatusCode(err error) int {
func handleErrorResponse(logger *zerolog.Logger, nq *common.NormalizedRequest, err error, ctx *fasthttp.RequestCtx, encoder sonic.Encoder, buf *bytes.Buffer) {
resp := processErrorBody(logger, nq, err)
setResponseStatusCode(err, ctx)
encoder.Encode(resp)
ctx.Response.Header.Set("Content-Type", "application/json")
ctx.SetBody(buf.Bytes())
err = encoder.Encode(resp)
if err != nil {
logger.Error().Err(err).Msgf("failed to encode error response")
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
ctx.Response.Header.Set("Content-Type", "application/json")
ctx.SetBodyString(fmt.Sprintf(`{"jsonrpc":"2.0","error":{"code":-32603,"message":"%s"}}`, err.Error()))
} else {
ctx.Response.Header.Set("Content-Type", "application/json")
ctx.SetBody(buf.Bytes())
}
}

func (s *HttpServer) Start(logger *zerolog.Logger) error {
Expand All @@ -476,7 +494,10 @@ func (s *HttpServer) Start(logger *zerolog.Logger) error {
ln6, err = net.Listen("tcp6", addrV6)
if err != nil {
if ln4 != nil {
ln4.Close()
err := ln4.Close()
if err != nil {
logger.Error().Err(err).Msgf("failed to close IPv4 listener")
}
}
return fmt.Errorf("error listening on IPv6: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions erpc/http_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func TestHttpServer_RaceTimeouts(t *testing.T) {
r.Status(200)
r.JSON(map[string]interface{}{
"jsonrpc": "2.0",
"id": rand.Intn(100000000),
"id": rand.Intn(100_000_000),
"result": map[string]interface{}{
"blockNumber": rand.Intn(100000000),
},
Expand Down Expand Up @@ -522,7 +522,7 @@ func TestHttpServer_MultipleUpstreams(t *testing.T) {
},
Upstreams: []*common.UpstreamConfig{
{
Id: "rpc1",
Id: "rpc1",
Type: common.UpstreamTypeEvm,
Endpoint: "http://rpc1.localhost",
Evm: &common.EvmUpstreamConfig{
Expand All @@ -531,7 +531,7 @@ func TestHttpServer_MultipleUpstreams(t *testing.T) {
VendorName: "llama",
},
{
Id: "rpc2",
Id: "rpc2",
Type: common.UpstreamTypeEvm,
Endpoint: "http://rpc2.localhost",
Evm: &common.EvmUpstreamConfig{
Expand Down Expand Up @@ -612,4 +612,4 @@ func TestHttpServer_MultipleUpstreams(t *testing.T) {

assert.True(t, gock.IsDone(), "All mocks should have been called")
})
}
}
5 changes: 3 additions & 2 deletions erpc/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ func Init(
addrV6 := fmt.Sprintf("%s:%d", cfg.Metrics.HostV6, cfg.Metrics.Port)
logger.Info().Msgf("starting metrics server on port: %d addrV4: %s addrV6: %s", cfg.Metrics.Port, addrV4, addrV6)
srv := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Metrics.Port),
Handler: promhttp.Handler(),
Addr: fmt.Sprintf(":%d", cfg.Metrics.Port),
Handler: promhttp.Handler(),
ReadHeaderTimeout: 10 * time.Second,
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
Expand Down
1 change: 1 addition & 0 deletions test/fake_server.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// #nosec
package test

import (
Expand Down
2 changes: 1 addition & 1 deletion upstream/envio_http_json_rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (c *EnvioHttpJsonRpcClient) SupportsNetwork(networkId string) (bool, error)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
rid := rand.Intn(1000000)
rid := rand.Intn(100_000_000) // #nosec G404
pr := common.NewNormalizedRequest([]byte(fmt.Sprintf(`{"jsonrpc":"2.0","id":%d,"method":"eth_chainId","params":[]}`, rid)))
resp, err := client.SendRequest(ctx, pr)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion upstream/evm_state_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ func (e *EvmStatePoller) fetchFinalizedBlockNumber(ctx context.Context) (int64,
}

func (e *EvmStatePoller) fetchBlock(ctx context.Context, blockTag string) (int64, error) {
randId := rand.Intn(10_000_000)
randId := rand.Intn(100_000_000) // #nosec G404
pr := common.NewNormalizedRequest([]byte(
fmt.Sprintf(`{"jsonrpc":"2.0","id":%d,"method":"eth_getBlockByNumber","params":["%s",false]}`, randId, blockTag),
))
Expand Down
8 changes: 4 additions & 4 deletions upstream/failsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,17 @@ func createCircuitBreakerPolicy(logger *zerolog.Logger, component string, cfg *c

if cfg.FailureThresholdCount > 0 {
if cfg.FailureThresholdCapacity > 0 {
builder = builder.WithFailureThresholdRatio(uint(cfg.FailureThresholdCount), uint(cfg.FailureThresholdCapacity))
builder = builder.WithFailureThresholdRatio(cfg.FailureThresholdCount, cfg.FailureThresholdCapacity)
} else {
builder = builder.WithFailureThreshold(uint(cfg.FailureThresholdCount))
builder = builder.WithFailureThreshold(cfg.FailureThresholdCount)
}
}

if cfg.SuccessThresholdCount > 0 {
if cfg.SuccessThresholdCapacity > 0 {
builder = builder.WithSuccessThresholdRatio(uint(cfg.SuccessThresholdCount), uint(cfg.SuccessThresholdCapacity))
builder = builder.WithSuccessThresholdRatio(cfg.SuccessThresholdCount, cfg.SuccessThresholdCapacity)
} else {
builder = builder.WithSuccessThreshold(uint(cfg.SuccessThresholdCount))
builder = builder.WithSuccessThreshold(cfg.SuccessThresholdCount)
}
}

Expand Down
15 changes: 11 additions & 4 deletions upstream/http_json_rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@ func (c *GenericHttpJsonRpcClient) processBatch() {
go func() {
resp, err := c.httpClient.Do(httpReq)
if err != nil {
if resp != nil {
er := resp.Body.Close()
if er != nil {
c.logger.Error().Err(er).Msgf("failed to close response body")
}
}
batchErrChan <- err
} else {
batchRespChan <- resp
Expand Down Expand Up @@ -290,16 +296,18 @@ func (c *GenericHttpJsonRpcClient) processBatch() {
}

func (c *GenericHttpJsonRpcClient) processBatchResponse(requests map[interface{}]*batchRequest, resp *http.Response) {
respBody, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
for _, req := range requests {
req.err <- err
}
return
}

c.logger.Debug().Str("body", string(respBody)).Msgf("received batch response")
if c.logger.GetLevel() == zerolog.DebugLevel {
c.logger.Debug().Str("body", string(respBody)).Msgf("received batch response")
}

// Usually when upstream is dead and returns a non-JSON response body
if respBody[0] == '<' {
Expand Down Expand Up @@ -419,9 +427,8 @@ func (c *GenericHttpJsonRpcClient) sendSingleRequest(ctx context.Context, req *c
}
return nil, err
}

respBody, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion upstream/pimlico_http_json_rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (c *PimlicoHttpJsonRpcClient) SupportsNetwork(networkId string) (bool, erro
}
ctx, cancel := context.WithTimeoutCause(context.Background(), 10*time.Second, errors.New("pimlico client timeout during eth_chainId"))
defer cancel()
rid := rand.Intn(1000000)
rid := rand.Intn(100_000_000) // #nosec G404
pr := common.NewNormalizedRequest([]byte(fmt.Sprintf(`{"jsonrpc":"2.0","id":%d,"method":"eth_chainId","params":[]}`, rid)))
resp, err := client.SendRequest(ctx, pr)
if err != nil {
Expand Down
15 changes: 11 additions & 4 deletions upstream/ratelimiter_autotuner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"math"
"sync"
"time"

"github.com/rs/zerolog"
)

type RateLimitAutoTuner struct {
logger *zerolog.Logger
budget *RateLimiterBudget
errorCounts map[string]*ErrorCounter
lastAdjustments map[string]time.Time
Expand All @@ -26,6 +29,7 @@ type ErrorCounter struct {
}

func NewRateLimitAutoTuner(
logger *zerolog.Logger,
budget *RateLimiterBudget,
adjustmentPeriod time.Duration,
errorRateThreshold,
Expand Down Expand Up @@ -88,16 +92,19 @@ func (arl *RateLimitAutoTuner) adjustBudget(method string) {

errorRate := float64(erc) / float64(ttc)

var newMaxCount int
var newMaxCount uint
if errorRate > arl.errorRateThreshold {
newMaxCount = int(math.Ceil(float64(currentMax) * arl.decreaseFactor))
newMaxCount = uint(math.Ceil(float64(currentMax) * arl.decreaseFactor))
} else if errorRate == 0 {
newMaxCount = int(math.Ceil(float64(currentMax) * arl.increaseFactor))
newMaxCount = uint(math.Ceil(float64(currentMax) * arl.increaseFactor))
} else {
continue
}

arl.budget.AdjustBudget(rule, newMaxCount)
err := arl.budget.AdjustBudget(rule, newMaxCount)
if err != nil {
arl.logger.Warn().Err(err).Msgf("failed to adjust budget for method %s", method)
}
}

arl.lastAdjustments[method] = time.Now()
Expand Down
2 changes: 1 addition & 1 deletion upstream/ratelimiter_budget.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (b *RateLimiterBudget) GetRulesByMethod(method string) []*RateLimitRule {
return rules
}

func (b *RateLimiterBudget) AdjustBudget(rule *RateLimitRule, newMaxCount int) error {
func (b *RateLimiterBudget) AdjustBudget(rule *RateLimitRule, newMaxCount uint) error {
b.rulesMu.Lock()
defer b.rulesMu.Unlock()

Expand Down
2 changes: 1 addition & 1 deletion upstream/ratelimiter_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (r *RateLimitersRegistry) createRateLimiter(rule *common.RateLimitRuleConfi
return nil, common.NewErrRateLimitInvalidConfig(fmt.Errorf("failed to parse duration for limit %v: %w", rule, err))
}

builder := ratelimiter.BurstyBuilder[interface{}](uint(rule.MaxCount), duration)
builder := ratelimiter.BurstyBuilder[interface{}](rule.MaxCount, duration)

if rule.WaitTime != "" {
waitTime, err := time.ParseDuration(rule.WaitTime)
Expand Down
5 changes: 4 additions & 1 deletion upstream/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,10 @@ func (u *UpstreamsRegistry) scheduleScoreCalculationTimers(ctx context.Context)
case <-ctx.Done():
return
case <-ticker.C:
u.RefreshUpstreamNetworkMethodScores()
err := u.RefreshUpstreamNetworkMethodScores()
if err != nil {
u.logger.Warn().Err(err).Msgf("failed to refresh upstream network method scores")
}
}
}
}()
Expand Down
2 changes: 1 addition & 1 deletion upstream/thirdweb_http_json_rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (c *ThirdwebHttpJsonRpcClient) SupportsNetwork(networkId string) (bool, err
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

rid := rand.Intn(1000000)
rid := rand.Intn(100_000_000) // #nosec G404
pr := common.NewNormalizedRequest([]byte(fmt.Sprintf(`{"jsonrpc":"2.0","id":%d,"method":"eth_chainId","params":[]}`, rid)))
resp, err := client.SendRequest(ctx, pr)
if err != nil {
Expand Down
8 changes: 6 additions & 2 deletions upstream/upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ func NewUpstream(
}
}

pup.guessUpstreamType()
err = pup.guessUpstreamType()
if err != nil {
return nil, err
}
if client, err := cr.GetOrCreateClient(pup); err != nil {
return nil, err
} else {
Expand Down Expand Up @@ -456,6 +459,7 @@ func (u *Upstream) initRateLimitAutoTuner() {
return
}
u.rateLimiterAutoTuner = NewRateLimitAutoTuner(
&u.Logger,
budget,
dur,
cfg.ErrorRateThreshold,
Expand Down Expand Up @@ -621,7 +625,7 @@ func (u *Upstream) shouldSkip(req *common.NormalizedRequest) (reason error, skip
return common.NewErrUpstreamSyncing(u.config.Id), true
}
}

if !u.shouldHandleMethod(method) {
u.Logger.Debug().Str("method", method).Msg("method not allowed or ignored by upstread")
return common.NewErrUpstreamMethodIgnored(method, u.config.Id), true
Expand Down

0 comments on commit ad34c22

Please sign in to comment.