diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 40d5624dab..74bdbcb67d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -293,7 +293,7 @@ jobs: - name: Formatting checks run: ./scripts/lint.sh -l -g format - name: Install linters - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.60.1 + run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.63.4 - name: Run linters run: make generate && golangci-lint --version && ./scripts/lint.sh -g lint - name: Ensure generated proto matches diff --git a/node/cmd/ccq/http.go b/node/cmd/ccq/http.go index c20a2c3843..17cb896e8c 100644 --- a/node/cmd/ccq/http.go +++ b/node/cmd/ccq/http.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "math" "net/http" "sort" "strings" @@ -201,9 +202,16 @@ func (s *httpServer) handleQuery(w http.ResponseWriter, r *http.Request) { return res.Signatures[i].Index < res.Signatures[j].Index }) signatures := make([]string, 0, len(res.Signatures)) - for _, s := range res.Signatures { + for _, sig := range res.Signatures { + if sig.Index > math.MaxUint8 { + s.logger.Error("Signature index out of bounds", zap.Int("sig.Index", sig.Index)) + http.Error(w, err.Error(), http.StatusInternalServerError) + invalidQueryRequestReceived.WithLabelValues("failed_to_marshal_response").Inc() + failedQueriesByUser.WithLabelValues(permEntry.userName).Inc() + break + } // ECDSA signature + a byte for the index of the guardian in the guardian set - signature := fmt.Sprintf("%s%02x", s.Signature, uint8(s.Index)) + signature := fmt.Sprintf("%s%02x", sig.Signature, uint8(sig.Index)) // #nosec G115 -- This is validated above signatures = append(signatures, signature) } w.Header().Add("Content-Type", "application/json") diff --git a/node/cmd/ccq/query_server.go b/node/cmd/ccq/query_server.go index 023c39721c..bab3de54a6 100644 --- a/node/cmd/ccq/query_server.go +++ b/node/cmd/ccq/query_server.go @@ -258,9 +258,10 @@ func runQueryServer(cmd *cobra.Command, args []string) { if statServer != nil && *shutdownDelay1 != 0 { logger.Info("Received sigterm. disabling health checks and pausing.") statServer.disableHealth() - time.Sleep(time.Duration(*shutdownDelay1) * time.Second) + time.Sleep(time.Duration(*shutdownDelay1) * time.Second) // #nosec G115 -- Defaults to 25 seconds, overflowing is infeasible numPending := 0 logger.Info("Waiting for any outstanding requests to complete before shutting down.") + // #nosec G115 -- Defaults to 65 seconds, overflowing is infeasible for count := 0; count < int(*shutdownDelay2); count++ { time.Sleep(time.Second) numPending = pendingResponses.NumPending() diff --git a/node/cmd/guardiand/adminclient.go b/node/cmd/guardiand/adminclient.go index 34c9b65eef..10c18a9195 100644 --- a/node/cmd/guardiand/adminclient.go +++ b/node/cmd/guardiand/adminclient.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "log" + "math" "os" "strconv" "strings" @@ -301,6 +302,7 @@ func runFindMissingMessages(cmd *cobra.Command, args []string) { if err != nil { log.Fatalf("invalid chain ID: %v", err) } + emitterAddress := args[1] ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) @@ -312,8 +314,12 @@ func runFindMissingMessages(cmd *cobra.Command, args []string) { } defer conn.Close() + if chainID > math.MaxUint16 { + log.Fatalf("chain ID is not a valid 16 bit unsigned integer: %v", err) + } + msg := nodev1.FindMissingMessagesRequest{ - EmitterChain: uint32(chainID), + EmitterChain: uint32(chainID), // #nosec G115 -- This conversion is checked above EmitterAddress: emitterAddress, RpcBackfill: *shouldBackfill, BackfillNodes: sdk.PublicRPCEndpoints, @@ -343,6 +349,10 @@ func runDumpVAAByMessageID(cmd *cobra.Command, args []string) { if err != nil { log.Fatalf("invalid chain ID: %v", err) } + // We only constrain this to int32 now, not uint16, given the ChainID protobuf definition + if chainID > math.MaxInt32 { + log.Fatalf("chain id must not exceed the max int32: %v", chainID) + } emitterAddress := parts[1] seq, err := strconv.ParseUint(parts[2], 10, 64) if err != nil { @@ -560,11 +570,11 @@ func runChainGovernorResetReleaseTimer(cmd *cobra.Command, args []string) { if len(args) > 1 { numDaysArg, err := strconv.Atoi(args[1]) - if err != nil { + if numDaysArg > math.MaxUint32 || err != nil { log.Fatalf("invalid num_days: %v", err) } - numDays = uint32(numDaysArg) + numDays = uint32(numDaysArg) // #nosec G115 -- This is validated above } msg := nodev1.ChainGovernorResetReleaseTimerRequest{ diff --git a/node/cmd/guardiand/adminnodes.go b/node/cmd/guardiand/adminnodes.go index cb2e5a06e6..b0612873c9 100644 --- a/node/cmd/guardiand/adminnodes.go +++ b/node/cmd/guardiand/adminnodes.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "math" "os" "sort" "strings" @@ -166,6 +167,9 @@ func runListNodes(cmd *cobra.Command, args []string) { truncAddrs := make(map[vaa.ChainID]string) errors := map[vaa.ChainID]uint64{} for _, n := range h.RawHeartbeat.Networks { + if n.Id > math.MaxUint16 { + log.Fatalf("heartbeat chain id is greater than MaxUint16: %v", n.Id) + } heights[vaa.ChainID(n.Id)] = n.Height errors[vaa.ChainID(n.Id)] = n.ErrorCount if len(n.ContractAddress) >= 16 { diff --git a/node/cmd/guardiand/admintemplate.go b/node/cmd/guardiand/admintemplate.go index a985fbd27b..926b0d5f2a 100644 --- a/node/cmd/guardiand/admintemplate.go +++ b/node/cmd/guardiand/admintemplate.go @@ -359,7 +359,7 @@ func runGuardianSetTemplate(cmd *cobra.Command, args []string) { // Use deterministic devnet addresses as examples in the template, such that this doubles as a test fixture. guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *setUpdateNumGuardians) for i := 0; i < *setUpdateNumGuardians; i++ { - k := devnet.InsecureDeterministicEcdsaKeyByIndex(crypto.S256(), uint64(i)) + k := devnet.InsecureDeterministicEcdsaKeyByIndex(crypto.S256(), uint64(i)) // #nosec G115 -- Number of guardians will never overflow here guardians[i] = &nodev1.GuardianSetUpdate_Guardian{ Pubkey: crypto.PubkeyToAddress(k.PublicKey).Hex(), Name: fmt.Sprintf("Example validator %d", i), @@ -367,7 +367,7 @@ func runGuardianSetTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- Number of guardians will never overflow here Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -397,7 +397,7 @@ func runContractUpgradeTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -429,7 +429,7 @@ func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -463,7 +463,7 @@ func runTokenBridgeUpgradeContractTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -505,7 +505,7 @@ func runRecoverChainIdTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -596,7 +596,7 @@ func runAccountantModifyBalanceTemplate(cmd *cobra.Command, args []string) { log.Fatalf("reason is too long, can be at most %d bytes", vaa.AccountantModifyBalanceReasonLength) } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -642,7 +642,7 @@ func runCircleIntegrationUpdateWormholeFinalityTemplate(cmd *cobra.Command, args } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -695,7 +695,7 @@ func runCircleIntegrationRegisterEmitterAndDomainTemplate(cmd *cobra.Command, ar } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -736,7 +736,7 @@ func runCircleIntegrationUpgradeContractImplementationTemplate(cmd *cobra.Comman } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -775,7 +775,7 @@ func runWormchainStoreCodeTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -812,7 +812,7 @@ func runWormchainInstantiateContractTemplate(cmd *cobra.Command, args []string) } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -851,7 +851,7 @@ func runWormchainMigrateContractTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -895,7 +895,7 @@ func runWormchainWasmInstantiateAllowlistTemplate(action nodev1.WormchainWasmIns } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -933,7 +933,7 @@ func runGatewayScheduleUpgradeTemplate(cmd *cobra.Command, args []string) { } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -957,7 +957,7 @@ func runGatewayScheduleUpgradeTemplate(cmd *cobra.Command, args []string) { func runGatewayCancelUpgradeTemplate(cmd *cobra.Command, args []string) { m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -980,7 +980,7 @@ func runGatewayIbcComposabilityMwSetContractTemplate(cmd *cobra.Command, args [] } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -1034,7 +1034,7 @@ func runIbcUpdateChannelChainTemplate(module nodev1.IbcUpdateChannelChainModule) } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -1070,7 +1070,7 @@ func runWormholeRelayerSetDefaultDeliveryProviderTemplate(cmd *cobra.Command, ar } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -1119,7 +1119,7 @@ func runGeneralPurposeGovernanceEvmCallTemplate(cmd *cobra.Command, args []strin } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), @@ -1163,7 +1163,7 @@ func runGeneralPurposeGovernanceSolanaCallTemplate(cmd *cobra.Command, args []st } m := &nodev1.InjectGovernanceVAARequest{ - CurrentSetIndex: uint32(*templateGuardianIndex), + CurrentSetIndex: uint32(*templateGuardianIndex), // #nosec G115 -- This will never overflow Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), diff --git a/node/cmd/spy/spy.go b/node/cmd/spy/spy.go index 0cb6378ada..0795edf2dd 100644 --- a/node/cmd/spy/spy.go +++ b/node/cmd/spy/spy.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "net" "net/http" "os" @@ -187,8 +188,11 @@ func (s *spyServer) SubscribeSignedVAA(req *spyv1.SubscribeSignedVAARequest, res if err != nil { return status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode emitter address: %v", err)) } + if t.EmitterFilter.GetChainId() > math.MaxUint16 { + return status.Error(codes.InvalidArgument, fmt.Sprintf("emitter chain id must be a valid 16 bit unsigned integer: %v", t.EmitterFilter.ChainId.Number())) + } fi = append(fi, filterSignedVaa{ - chainId: vaa.ChainID(t.EmitterFilter.ChainId), + chainId: vaa.ChainID(t.EmitterFilter.ChainId), // #nosec G115 -- This is validated above emitterAddr: addr, }) default: diff --git a/node/hack/accountant/send_obs.go b/node/hack/accountant/send_obs.go index f64bdbf843..e211a5978f 100644 --- a/node/hack/accountant/send_obs.go +++ b/node/hack/accountant/send_obs.go @@ -51,7 +51,7 @@ func main() { logger.Fatal("failed to load guardian key", zap.Error(err)) } - sequence := uint64(time.Now().Unix()) + sequence := uint64(time.Now().Unix()) // #nosec G115 -- This is safe indefinitely timestamp := time.Now() if !testSubmit(ctx, logger, guardianSigner, wormchainConn, contract, "0000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16", timestamp, sequence, true, "Submit should succeed") { diff --git a/node/hack/parse_eth_tx/parse_eth_tx.go b/node/hack/parse_eth_tx/parse_eth_tx.go index 5f6fb0d9e1..bce5c514d1 100644 --- a/node/hack/parse_eth_tx/parse_eth_tx.go +++ b/node/hack/parse_eth_tx/parse_eth_tx.go @@ -9,6 +9,7 @@ import ( "context" "flag" "log" + "math" "github.com/certusone/wormhole/node/pkg/watchers/evm" "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors" @@ -31,7 +32,11 @@ func main() { log.Fatal("No transaction specified") } - chainID := vaa.ChainID(*flagChainID) + if *flagChainID > math.MaxUint16 { + log.Fatalf("chain id is not a valid uint16: %d", *flagChainID) + } + + chainID := vaa.ChainID(*flagChainID) // #nosec G115 -- This is validated above ctx := context.Background() diff --git a/node/hack/query/ccqlistener/ccqlistener.go b/node/hack/query/ccqlistener/ccqlistener.go index ae59a9ffe4..94d5ec1c4f 100644 --- a/node/hack/query/ccqlistener/ccqlistener.go +++ b/node/hack/query/ccqlistener/ccqlistener.go @@ -138,7 +138,7 @@ func main() { // Manual p2p setup components := p2p.DefaultComponents() - components.Port = uint(*p2pPort) + components.Port = uint(*p2pPort) // #nosec G115 -- TCP ports are 16 bit, so this conversion is safe bootstrapPeers := *p2pBootstrap networkID := *p2pNetworkID + "/ccq" diff --git a/node/hack/repair_eth/repair_eth.go b/node/hack/repair_eth/repair_eth.go index 7063aad5d3..34e356b3f8 100644 --- a/node/hack/repair_eth/repair_eth.go +++ b/node/hack/repair_eth/repair_eth.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "log" + "math" "net/http" "net/http/cookiejar" "strconv" @@ -397,12 +398,18 @@ func main() { var from, to string if lastHeight == 0 { - from = strconv.Itoa(int(currentHeight - step)) + if currentHeight-step > math.MaxInt { + log.Fatalf("from block overflowed: %v", currentHeight-step) + } + from = strconv.Itoa(int(currentHeight - step)) // #nosec G115 -- This is checked above to = "latest" lastHeight = currentHeight } else { - from = strconv.Itoa(int(lastHeight - step)) - to = strconv.Itoa(int(lastHeight)) + if lastHeight > math.MaxInt { + log.Fatalf("from block overflowed: %v", lastHeight) + } + from = strconv.Itoa(int(lastHeight - step)) // #nosec G115 -- If the above is safe, this is safe too + to = strconv.Itoa(int(lastHeight)) // #nosec G115 -- This is checked above } lastHeight -= step diff --git a/node/hack/repair_solana/repair.go b/node/hack/repair_solana/repair.go index 99ac2f53be..66c82e90bc 100644 --- a/node/hack/repair_solana/repair.go +++ b/node/hack/repair_solana/repair.go @@ -262,7 +262,7 @@ func fetchTxSeq(ctx context.Context, c *rpc.Client, sig solana.Signature) (*rpc. log.Printf("failed to parse seq %s: %v", seq, err) continue } - return out, uint64(seqInt), nil + return out, uint64(seqInt), nil // #nosec G115 -- The sequence number cannot exceed a uint64 } } return nil, 0, nil @@ -285,7 +285,7 @@ func process(out *rpc.GetTransactionResult) (*solana.PublicKey, error) { var programIndex uint16 for n, key := range tx.Message.AccountKeys { if key.Equals(program) { - programIndex = uint16(n) + programIndex = uint16(n) // #nosec G115 -- The solana runtime can only support 64 accounts per transaction max } } if programIndex == 0 { diff --git a/node/hack/repair_terra/repair.go b/node/hack/repair_terra/repair.go index 224a596ad4..0628d6520e 100644 --- a/node/hack/repair_terra/repair.go +++ b/node/hack/repair_terra/repair.go @@ -331,7 +331,7 @@ func main() { limiter := rate.NewLimiter(rate.Every(time.Duration(*sleepTime)*time.Second), 1) log.Printf("Starting search for missing sequence numbers (sleeping %ds between requests)...", *sleepTime) - offset := 0 + var offset uint64 = 0 var firstTime bool = true for (offset > 0) || firstTime { @@ -363,7 +363,7 @@ func main() { } next := gjson.Get(blockJSON, "next") log.Println("next block", next.Int()) - offset = int(next.Uint()) + offset = next.Uint() // Get the transactions. Should be 100 of them txs := gjson.Get(blockJSON, "txs") for _, tx := range txs.Array() { diff --git a/node/pkg/accountant/submit_obs.go b/node/pkg/accountant/submit_obs.go index 513e14a210..8860f4abc8 100644 --- a/node/pkg/accountant/submit_obs.go +++ b/node/pkg/accountant/submit_obs.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "strings" "time" @@ -88,7 +89,11 @@ func (acct *Accountant) handleBatch(ctx context.Context, subChan chan *common.Me return fmt.Errorf("failed to get guardian index for %s", tag) } - acct.submitObservationsToContract(msgs, gs.Index, uint32(guardianIndex), wormchainConn, contract, prefix, tag) + if guardianIndex > math.MaxUint32 { + return fmt.Errorf("guardian index greater than max uint32 %v", guardianIndex) + } + + acct.submitObservationsToContract(msgs, gs.Index, uint32(guardianIndex), wormchainConn, contract, prefix, tag) // #nosec G115 -- This is checked above transfersSubmitted.Add(float64(len(msgs))) return nil } @@ -309,7 +314,7 @@ func SubmitObservationsToContract( for idx, msg := range msgs { obs[idx] = Observation{ TxHash: msg.TxHash.Bytes(), - Timestamp: uint32(msg.Timestamp.Unix()), + Timestamp: uint32(msg.Timestamp.Unix()), // #nosec G115 -- This conversion is safe until year 2106 Nonce: msg.Nonce, EmitterChain: uint16(msg.EmitterChain), EmitterAddress: msg.EmitterAddress, diff --git a/node/pkg/adminrpc/adminserver.go b/node/pkg/adminrpc/adminserver.go index 2f345b9807..13d4391895 100644 --- a/node/pkg/adminrpc/adminserver.go +++ b/node/pkg/adminrpc/adminserver.go @@ -281,7 +281,7 @@ func accountantModifyBalance(req *nodev1.AccountantModifyBalance, timestamp time ChainId: vaa.ChainID(req.ChainId), TokenChain: vaa.ChainID(req.TokenChain), TokenAddress: tokenAdress, - Kind: uint8(req.Kind), + Kind: uint8(req.Kind), // #nosec G115 -- The `ModificationKind` enum only has 3 values Amount: amount, Reason: req.Reason, }.Serialize() @@ -667,6 +667,9 @@ func evmCallToVaa(evmCall *nodev1.EvmCall, timestamp time.Time, guardianSetIndex if err != nil { return nil, fmt.Errorf("failed to decode ABI encoded call: %w", err) } + if evmCall.ChainId > math.MaxUint16 { + return nil, fmt.Errorf("chain id exceeds max uint16: %v", evmCall.ChainId) + } body, err := vaa.BodyGeneralPurposeGovernanceEvm{ ChainID: vaa.ChainID(evmCall.ChainId), @@ -699,6 +702,9 @@ func solanaCallToVaa(solanaCall *nodev1.SolanaCall, timestamp time.Time, guardia if err != nil { return nil, fmt.Errorf("failed to decode instruction: %w", err) } + if solanaCall.ChainId > math.MaxUint16 { + return nil, fmt.Errorf("chain id exceeds max uint16: %v", solanaCall.ChainId) + } body, err := vaa.BodyGeneralPurposeGovernanceSolana{ ChainID: vaa.ChainID(solanaCall.ChainId), @@ -917,6 +923,9 @@ func (s *nodePrivilegedService) FindMissingMessages(ctx context.Context, req *no if err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid emitter address encoding: %v", err) } + if req.EmitterChain > math.MaxUint16 { + return nil, status.Errorf(codes.InvalidArgument, "chain id exceeds max uint16: %v", req.EmitterChain) + } emitterAddress := vaa.Address{} copy(emitterAddress[:], b) @@ -1050,7 +1059,7 @@ func (s *nodePrivilegedService) ChainGovernorResetReleaseTimer(ctx context.Conte func (s *nodePrivilegedService) PurgePythNetVaas(ctx context.Context, req *nodev1.PurgePythNetVaasRequest) (*nodev1.PurgePythNetVaasResponse, error) { prefix := db.VAAID{EmitterChain: vaa.ChainIDPythNet} - oldestTime := time.Now().Add(-time.Hour * 24 * time.Duration(req.DaysOld)) + oldestTime := time.Now().Add(-time.Hour * 24 * time.Duration(req.DaysOld)) // #nosec G115 -- This conversion is safe indefinitely resp, err := s.db.PurgeVaas(prefix, oldestTime, req.LogOnly) if err != nil { return nil, err @@ -1152,7 +1161,7 @@ func (s *nodePrivilegedService) SignExistingVAA(ctx context.Context, req *nodev1 continue } newVAA.Signatures = append(newVAA.Signatures, &vaa.Signature{ - Index: uint8(newIndex), + Index: uint8(newIndex), // #nosec G115 -- The length of newGS is constrained to a uint8 above Signature: sig.Signature, }) } @@ -1172,7 +1181,7 @@ func (s *nodePrivilegedService) SignExistingVAA(ctx context.Context, req *nodev1 copy(signature[:], sig) newVAA.Signatures = append(v.Signatures, &vaa.Signature{ - Index: uint8(localGuardianIndex), + Index: uint8(localGuardianIndex), // #nosec G115 -- The length of newGS is constrained to a uint8 above Signature: signature, }) @@ -1267,7 +1276,12 @@ func (s *nodePrivilegedService) GetAndObserveMissingVAAs(ctx context.Context, re splits := strings.Split(missingVAA.VaaKey, "/") chainID, err := strconv.Atoi(splits[0]) if err != nil { - errMsgs += fmt.Sprintf("\nerror converting chainID [%s] to int", missingVAA.VaaKey) + errMsgs += fmt.Sprintf("\nerror converting chainID [%d] to int", chainID) + errCounter++ + continue + } + if chainID > math.MaxUint16 { + errMsgs += fmt.Sprintf("\nchainID [%d] not a valid uint16", chainID) errCounter++ continue } @@ -1277,7 +1291,7 @@ func (s *nodePrivilegedService) GetAndObserveMissingVAAs(ctx context.Context, re errCounter++ continue } - vaaKey := db.VAAID{EmitterChain: vaa.ChainID(chainID), EmitterAddress: vaa.Address([]byte(splits[1])), Sequence: sequence} + vaaKey := db.VAAID{EmitterChain: vaa.ChainID(chainID), EmitterAddress: vaa.Address([]byte(splits[1])), Sequence: sequence} // #nosec G115 -- This chainId conversion is verified above hasVaa, err := s.db.HasVAA(vaaKey) if err != nil || hasVaa { errMsgs += fmt.Sprintf("\nerror checking for VAA %s", missingVAA.VaaKey) @@ -1285,7 +1299,12 @@ func (s *nodePrivilegedService) GetAndObserveMissingVAAs(ctx context.Context, re continue } var obsvReq gossipv1.ObservationRequest - obsvReq.ChainId = uint32(missingVAA.Chain) + if missingVAA.Chain > math.MaxUint16 { + errMsgs += fmt.Sprintf("\nmissing VAA chainID [%d] not a valid uint16", missingVAA.Chain) + errCounter++ + continue + } + obsvReq.ChainId = uint32(missingVAA.Chain) // #nosec G115 -- This conversion is checked above obsvReq.TxHash, err = hex.DecodeString(strings.TrimPrefix(missingVAA.Txhash, "0x")) if err != nil { obsvReq.TxHash, err = base58.Decode(missingVAA.Txhash) diff --git a/node/pkg/adminrpc/adminserver_test.go b/node/pkg/adminrpc/adminserver_test.go index 5f305fef6d..def71e701e 100644 --- a/node/pkg/adminrpc/adminserver_test.go +++ b/node/pkg/adminrpc/adminserver_test.go @@ -131,7 +131,7 @@ func generateMockVAA(gsIndex uint32, signers []guardiansigner.GuardianSigner, t copy(signature[:], sig) v.Signatures = append(v.Signatures, &vaa.Signature{ - Index: uint8(i), + Index: uint8(i), // #nosec G115 -- This conversion is safe based on the constants used Signature: signature, }) diff --git a/node/pkg/common/chainlock.go b/node/pkg/common/chainlock.go index 052d1bbd5b..1414f2872e 100644 --- a/node/pkg/common/chainlock.go +++ b/node/pkg/common/chainlock.go @@ -49,7 +49,7 @@ func (msg *MessagePublication) Marshal() ([]byte, error) { buf := new(bytes.Buffer) buf.Write(msg.TxHash[:]) - vaa.MustWrite(buf, binary.BigEndian, uint32(msg.Timestamp.Unix())) + vaa.MustWrite(buf, binary.BigEndian, uint32(msg.Timestamp.Unix())) // #nosec G115 -- This conversion is safe until year 2106 vaa.MustWrite(buf, binary.BigEndian, msg.Nonce) vaa.MustWrite(buf, binary.BigEndian, msg.Sequence) vaa.MustWrite(buf, binary.BigEndian, msg.ConsistencyLevel) diff --git a/node/pkg/db/db_test.go b/node/pkg/db/db_test.go index 543d1b6cd6..d4d8218237 100644 --- a/node/pkg/db/db_test.go +++ b/node/pkg/db/db_test.go @@ -127,7 +127,7 @@ func TestStoreSignedVAABatch(t *testing.T) { require.Less(t, int64(0), db.db.MaxBatchSize()) // In testing this was 10066329. // Make sure we exceed the max batch size. - numVAAs := uint64(db.db.MaxBatchCount() + 1) + numVAAs := uint64(db.db.MaxBatchCount() + 1) // #nosec G115 -- This is safe given the testing values noted above // Build the VAA batch. vaaBatch := make([]*vaa.VAA, 0, numVAAs) diff --git a/node/pkg/db/governor.go b/node/pkg/db/governor.go index 48bcc9d0e5..31f4de47cb 100644 --- a/node/pkg/db/governor.go +++ b/node/pkg/db/governor.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "fmt" + "math" "time" "github.com/certusone/wormhole/node/pkg/common" @@ -65,17 +66,23 @@ type Transfer struct { func (t *Transfer) Marshal() ([]byte, error) { buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(t.Timestamp.Unix())) + vaa.MustWrite(buf, binary.BigEndian, uint32(t.Timestamp.Unix())) // #nosec G115 -- This conversion is safe until year 2106 vaa.MustWrite(buf, binary.BigEndian, t.Value) vaa.MustWrite(buf, binary.BigEndian, t.OriginChain) buf.Write(t.OriginAddress[:]) vaa.MustWrite(buf, binary.BigEndian, t.EmitterChain) buf.Write(t.EmitterAddress[:]) - vaa.MustWrite(buf, binary.BigEndian, uint16(len(t.MsgID))) + if len(t.MsgID) > math.MaxUint16 { + return nil, fmt.Errorf("failed to marshal MsgID, length too long: %d", len(t.MsgID)) + } + vaa.MustWrite(buf, binary.BigEndian, uint16(len(t.MsgID))) // #nosec G115 -- This is checked above if len(t.MsgID) > 0 { buf.Write([]byte(t.MsgID)) } - vaa.MustWrite(buf, binary.BigEndian, uint16(len(t.Hash))) + if len(t.Hash) > math.MaxUint16 { + return nil, fmt.Errorf("failed to marshal Hash, length too long: %d", len(t.Hash)) + } + vaa.MustWrite(buf, binary.BigEndian, uint16(len(t.Hash))) // #nosec G115 -- This is checked above if len(t.Hash) > 0 { buf.Write([]byte(t.Hash)) } @@ -236,7 +243,7 @@ type PendingTransfer struct { func (p *PendingTransfer) Marshal() ([]byte, error) { buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(p.ReleaseTime.Unix())) + vaa.MustWrite(buf, binary.BigEndian, uint32(p.ReleaseTime.Unix())) // #nosec G115 -- This conversion is safe until year 2106 b, err := p.Msg.Marshal() if err != nil { @@ -429,9 +436,13 @@ func (d *Database) GetChainGovernorDataForTime(logger *zap.Logger, now time.Time // This is called by the chain governor to persist a pending transfer. func (d *Database) StoreTransfer(t *Transfer) error { - b, _ := t.Marshal() + b, err := t.Marshal() - err := d.db.Update(func(txn *badger.Txn) error { + if err != nil { + return err + } + + err = d.db.Update(func(txn *badger.Txn) error { if err := txn.Set(TransferMsgID(t), b); err != nil { return err } diff --git a/node/pkg/db/governor_test.go b/node/pkg/db/governor_test.go index 0b0b2d17b0..e48961cdcb 100644 --- a/node/pkg/db/governor_test.go +++ b/node/pkg/db/governor_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "fmt" + "math" "os" "sort" "testing" @@ -566,7 +567,7 @@ func TestUnmarshalPendingTransferFailures(t *testing.T) { func (d *Database) storeOldPendingMsg(t *testing.T, p *PendingTransfer) { buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(p.ReleaseTime.Unix())) + vaa.MustWrite(buf, binary.BigEndian, uint32(p.ReleaseTime.Unix())) // #nosec G115 -- This conversion is safe until year 2106 b := marshalOldMessagePublication(&p.Msg) @@ -586,7 +587,7 @@ func marshalOldMessagePublication(msg *common.MessagePublication) []byte { buf := new(bytes.Buffer) buf.Write(msg.TxHash[:]) - vaa.MustWrite(buf, binary.BigEndian, uint32(msg.Timestamp.Unix())) + vaa.MustWrite(buf, binary.BigEndian, uint32(msg.Timestamp.Unix())) // #nosec G115 -- This conversion is safe until year 2106 vaa.MustWrite(buf, binary.BigEndian, msg.Nonce) vaa.MustWrite(buf, binary.BigEndian, msg.Sequence) vaa.MustWrite(buf, binary.BigEndian, msg.ConsistencyLevel) @@ -761,29 +762,39 @@ func TestLoadingOldPendingTransfers(t *testing.T) { assert.Equal(t, pending2.Msg, pendings2[1].Msg) } -func marshalOldTransfer(xfer *Transfer) []byte { +func marshalOldTransfer(xfer *Transfer) ([]byte, error) { buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(xfer.Timestamp.Unix())) + vaa.MustWrite(buf, binary.BigEndian, uint32(xfer.Timestamp.Unix())) // #nosec G115 -- This conversion is safe until year 2106 vaa.MustWrite(buf, binary.BigEndian, xfer.Value) vaa.MustWrite(buf, binary.BigEndian, xfer.OriginChain) buf.Write(xfer.OriginAddress[:]) vaa.MustWrite(buf, binary.BigEndian, xfer.EmitterChain) buf.Write(xfer.EmitterAddress[:]) - vaa.MustWrite(buf, binary.BigEndian, uint16(len(xfer.MsgID))) + if len(xfer.MsgID) > math.MaxUint16 { + return nil, fmt.Errorf("failed to marshal MsgID, length too long: %d", len(xfer.MsgID)) + } + vaa.MustWrite(buf, binary.BigEndian, uint16(len(xfer.MsgID))) // #nosec G115 -- This conversion is checked above if len(xfer.MsgID) > 0 { buf.Write([]byte(xfer.MsgID)) } - vaa.MustWrite(buf, binary.BigEndian, uint16(len(xfer.Hash))) + if len(xfer.Hash) > math.MaxUint16 { + return nil, fmt.Errorf("failed to marshal Hash, length too long: %d", len(xfer.Hash)) + } + vaa.MustWrite(buf, binary.BigEndian, uint16(len(xfer.Hash))) // #nosec G115 -- This conversion is checked above if len(xfer.Hash) > 0 { buf.Write([]byte(xfer.Hash)) } - return buf.Bytes() + return buf.Bytes(), nil } func (d *Database) storeOldTransfer(xfer *Transfer) error { key := []byte(fmt.Sprintf("%v%v", oldTransfer, xfer.MsgID)) - b := marshalOldTransfer(xfer) + b, err := marshalOldTransfer(xfer) + + if err != nil { + return err + } return d.db.Update(func(txn *badger.Txn) error { if err := txn.Set(key, b); err != nil { @@ -812,7 +823,8 @@ func TestDeserializeOfOldTransfer(t *testing.T) { Hash: "Hash1", } - bytes := marshalOldTransfer(xfer1) + bytes, err := marshalOldTransfer(xfer1) + require.NoError(t, err) xfer2, err := unmarshalOldTransfer(bytes) require.NoError(t, err) diff --git a/node/pkg/devnet/guardiankey.go b/node/pkg/devnet/guardiankey.go index 7f54d608e8..52df1efc8c 100644 --- a/node/pkg/devnet/guardiankey.go +++ b/node/pkg/devnet/guardiankey.go @@ -17,7 +17,7 @@ func GenerateAndStoreDevnetGuardianKey(filename string) error { } // Generate the guardian key. - gk := InsecureDeterministicEcdsaKeyByIndex(ethcrypto.S256(), uint64(idx)) + gk := InsecureDeterministicEcdsaKeyByIndex(ethcrypto.S256(), uint64(idx)) // #nosec G115 -- Number of guardians will never overflow here // Store it to disk. if err := common.WriteArmoredKey(gk, "auto-generated deterministic devnet key", filename, common.GuardianKeyArmoredBlock, true); err != nil { diff --git a/node/pkg/governor/governor.go b/node/pkg/governor/governor.go index 8f6137b1c8..7465072021 100644 --- a/node/pkg/governor/governor.go +++ b/node/pkg/governor/governor.go @@ -145,7 +145,7 @@ func (ce *chainEntry) addFlowCancelTransfer(transfer transfer) error { return fmt.Errorf("value for transfer.dbTransfer exceeds MaxInt64: %d", transfer.dbTransfer.Value) } // Type conversion is safe here because of the MaxInt64 bounds check above - if value != -int64(transfer.dbTransfer.Value) { + if value != -int64(transfer.dbTransfer.Value) { // nolint:gosec return fmt.Errorf("transfer is invalid: transfer.value %d must equal the inverse of transfer.dbTransfer.Value %d", value, transfer.dbTransfer.Value) } if targetChain != ce.emitterChainId { diff --git a/node/pkg/governor/governor_monitoring.go b/node/pkg/governor/governor_monitoring.go index 378bc9ba4e..8cf56eb59d 100644 --- a/node/pkg/governor/governor_monitoring.go +++ b/node/pkg/governor/governor_monitoring.go @@ -77,6 +77,7 @@ package governor import ( "context" "fmt" + "math" "sort" "time" @@ -379,7 +380,7 @@ func (gov *ChainGovernor) GetEnqueuedVAAs() []*publicrpcv1.GovernorGetEnqueuedVA EmitterChain: uint32(pe.dbData.Msg.EmitterChain), EmitterAddress: pe.dbData.Msg.EmitterAddress.String(), Sequence: pe.dbData.Msg.Sequence, - ReleaseTime: uint32(pe.dbData.ReleaseTime.Unix()), + ReleaseTime: uint32(pe.dbData.ReleaseTime.Unix()), // #nosec G115 -- This conversion is safe until year 2106 NotionalValue: value, TxHash: pe.dbData.Msg.TxHash.String(), }) @@ -398,7 +399,11 @@ func (gov *ChainGovernor) IsVAAEnqueued(msgId *publicrpcv1.MessageID) (bool, err return false, fmt.Errorf("no message ID specified") } - emitterChain := vaa.ChainID(msgId.EmitterChain) + if msgId.GetEmitterChain() > math.MaxUint16 { + return false, fmt.Errorf("emitter chain id must be no greater than 16 bits") + } + + emitterChain := vaa.ChainID(msgId.GetEmitterChain()) // #nosec G115 -- This conversion is checked above emitterAddress, err := vaa.StringToAddress(msgId.EmitterAddress) if err != nil { @@ -499,6 +504,11 @@ func (gov *ChainGovernor) CollectMetrics(ctx context.Context, hb *gossipv1.Heart continue } + if n.Id > math.MaxUint16 { + gov.logger.Error("CollectMetrics: chain id is not a valid uint16", zap.Uint32("chain_id", n.Id)) + continue + } + chain := vaa.ChainID(n.Id) chainId := fmt.Sprint(n.Id) @@ -647,7 +657,7 @@ func (gov *ChainGovernor) publishStatus(ctx context.Context, hb *gossipv1.Heartb numEnqueued = numEnqueued + 1 enqueuedVaas = append(enqueuedVaas, &gossipv1.ChainGovernorStatus_EnqueuedVAA{ Sequence: pe.dbData.Msg.Sequence, - ReleaseTime: uint32(pe.dbData.ReleaseTime.Unix()), + ReleaseTime: uint32(pe.dbData.ReleaseTime.Unix()), // #nosec G115 -- This conversion is safe until year 2106 NotionalValue: value, TxHash: pe.dbData.Msg.TxHash.String(), }) diff --git a/node/pkg/governor/governor_test.go b/node/pkg/governor/governor_test.go index d44168c5d7..0af201ed6c 100644 --- a/node/pkg/governor/governor_test.go +++ b/node/pkg/governor/governor_test.go @@ -218,7 +218,7 @@ func TestSumAllFromToday(t *testing.T) { transfers = append(transfers, transfer) sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now.Add(-time.Hour*24)) require.NoError(t, err) - assert.Equal(t, uint64(125000), uint64(sum)) + assert.Equal(t, uint64(125000), uint64(sum)) // #nosec G115 -- If this overflowed the test would fail anyway assert.Equal(t, 1, len(updatedTransfers)) } @@ -470,7 +470,7 @@ func TestChainEntrySumExceedsDailyLimit(t *testing.T) { usage, err := gov.TrimAndSumValueForChain(emitter, now.Add(-time.Hour*24)) require.NoError(t, err) - assert.Equal(t, emitterTransferValue*uint64(expectedNumTransfers), usage) + assert.Equal(t, emitterTransferValue*uint64(expectedNumTransfers), usage) // #nosec G115 -- If this overflowed the test would fail anyway } func TestTrimAndSumValueOverflowErrors(t *testing.T) { @@ -564,7 +564,7 @@ func TestTrimOneOfTwoTransfers(t *testing.T) { sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now.Add(-time.Hour*24)) require.NoError(t, err) assert.Equal(t, 1, len(updatedTransfers)) - assert.Equal(t, uint64(225000), uint64(sum)) + assert.Equal(t, uint64(225000), uint64(sum)) // #nosec G115 -- If this overflowed the test would fail anyway } func TestTrimSeveralTransfers(t *testing.T) { @@ -620,7 +620,7 @@ func TestTrimSeveralTransfers(t *testing.T) { sum, updatedTransfers, err := gov.TrimAndSumValue(transfers, now.Add(-time.Hour*24)) require.NoError(t, err) assert.Equal(t, 3, len(updatedTransfers)) - assert.Equal(t, uint64(465000), uint64(sum)) + assert.Equal(t, uint64(465000), uint64(sum)) // #nosec G115 -- If this overflowed the test would fail anyway } func TestTrimmingAllTransfersShouldReturnZero(t *testing.T) { @@ -2246,7 +2246,7 @@ func TestNumDaysForReleaseTimerReset(t *testing.T) { msg.MessageIDString() // check that the enqueued vaa's release date is now + 1 day - expectedReleaseTime := uint32(now.Add(24 * time.Hour).Unix()) + expectedReleaseTime := uint32(now.Add(24 * time.Hour).Unix()) // #nosec G115 -- This conversion is safe until year 2106 enqueuedVaas := gov.GetEnqueuedVAAs() assert.Equal(t, len(enqueuedVaas), 1) assert.Equal(t, enqueuedVaas[0].ReleaseTime, expectedReleaseTime) @@ -2258,7 +2258,7 @@ func TestNumDaysForReleaseTimerReset(t *testing.T) { // check that the enqueued vaa's release date is now + 5 days enqueuedVaas = gov.GetEnqueuedVAAs() assert.Equal(t, len(enqueuedVaas), 1) - expectedReleaseTime = uint32(now.Add(5 * 24 * time.Hour).Unix()) + expectedReleaseTime = uint32(now.Add(5 * 24 * time.Hour).Unix()) // #nosec G115 -- This conversion is safe until year 2106 assert.Equal(t, enqueuedVaas[0].ReleaseTime, expectedReleaseTime) } @@ -2905,7 +2905,8 @@ func TestReloadTransfersNearCapacity(t *testing.T) { governorUsageEth, err = gov.TrimAndSumValueForChain(chainEntryEth, time.Unix(int64(transferTime.Unix()-1000), 0)) assert.Equal(t, uint64(8000), governorUsageEth) - assert.Equal(t, int(chainEntryEth.dailyLimit-governorUsageEth), 2000) // Remaining capacity + // Remaining capacity + assert.Equal(t, int(chainEntryEth.dailyLimit-governorUsageEth), 2000) // #nosec G115 -- If this overflowed the test would fail require.NoError(t, err) governorUsageSui, err = gov.TrimAndSumValueForChain(chainEntrySui, time.Unix(int64(transferTime.Unix()-1000), 0)) assert.Zero(t, governorUsageSui) @@ -2928,7 +2929,8 @@ func TestReloadTransfersNearCapacity(t *testing.T) { governorUsageEth, err = gov.TrimAndSumValueForChain(chainEntryEth, time.Unix(int64(transferTime.Unix()-1000), 0)) assert.Equal(t, uint64(8050), governorUsageEth) - assert.Equal(t, int(chainEntryEth.dailyLimit-governorUsageEth), 1950) // Remaining capacity + // Remaining capacity + assert.Equal(t, int(chainEntryEth.dailyLimit-governorUsageEth), 1950) // #nosec G115 -- If this overflowed the test would fail require.NoError(t, err) governorUsageSui, err = gov.TrimAndSumValueForChain(chainEntrySui, time.Unix(int64(transferTime.Unix()-1000), 0)) require.NoError(t, err) diff --git a/node/pkg/gwrelayer/gwrelayer_test.go b/node/pkg/gwrelayer/gwrelayer_test.go index 3c61a8e7da..203bd32df6 100644 --- a/node/pkg/gwrelayer/gwrelayer_test.go +++ b/node/pkg/gwrelayer/gwrelayer_test.go @@ -72,7 +72,7 @@ func Test_shouldPublishToIbcTranslator(t *testing.T) { Signatures: []*vaa.Signature{}, Timestamp: time.Unix(0, 0), Nonce: uint32(1), - Sequence: uint64(seqNum), + Sequence: uint64(seqNum), // #nosec G115 -- We're iterating over a fixed length array defined above ConsistencyLevel: uint8(32), EmitterChain: vaa.ChainIDSolana, EmitterAddress: solanaEmitter, diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index f7ad4c7171..4b5d8fdd3d 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -123,7 +123,7 @@ func newMockGuardianSet(t testing.TB, testId uint, n int) []*mockGuardian { MockSetC: make(chan *common.GuardianSet), guardianSigner: guardianSigner, guardianAddr: eth_crypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background())), - config: createGuardianConfig(t, testId, uint(i)), + config: createGuardianConfig(t, testId, uint(i)), // #nosec G115 -- Guardian set will never be that large } } @@ -428,7 +428,7 @@ func governedMsg(shouldBeDelayed bool) *common.MessagePublication { amount = 1_000_000_000_000 } - tokenAddrStr := "069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001" // nolint:gosec // wrapped-SOL + tokenAddrStr := "069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001" // #nosec G101 -- Address for testing toAddrStr := "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8" // whatever payloadBytes := buildMockTransferPayloadBytes( vaa.ChainIDSolana, @@ -658,7 +658,7 @@ func runConsensusTests(t *testing.T, testCases []testCase, numGuardians int) { // run the guardians for i := 0; i < numGuardians; i++ { - gRun := mockGuardianRunnable(t, gs, uint(i), obsDb) + gRun := mockGuardianRunnable(t, gs, uint(i), obsDb) // #nosec G115 -- Guardian set will never be that large err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) if i == 0 && numGuardians > 1 { time.Sleep(time.Second) // give the bootstrap guardian some time to start up @@ -762,7 +762,7 @@ func runConsensusTests(t *testing.T, testCases []testCase, numGuardians int) { _, err := adminCs[j].InjectGovernanceVAA(queryCtx, &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: guardianSetIndex, Messages: []*nodev1.GovernanceMessage{testCase.govMsg}, - Timestamp: uint32(testCase.msg.Timestamp.Unix()), + Timestamp: uint32(testCase.msg.Timestamp.Unix()), // #nosec G115 -- This conversion is safe until year 2106 }) queryCancel() assert.NoError(t, err) @@ -1157,7 +1157,7 @@ func BenchmarkConsensus(b *testing.B) { //runConsensusBenchmark(b, "1", 19, 1000, 1) // ~13s } -func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessages int, maxPendingObs int) { +func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessages uint64, maxPendingObs int) { const vaaCheckGuardianIndex = 1 // we will query this Guardian for VAAs. t.Run(name, func(t *testing.B) { @@ -1182,7 +1182,7 @@ func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessa // run the guardians for i := 0; i < numGuardians; i++ { - gRun := mockGuardianRunnable(t, gs, uint(i), obsDb) + gRun := mockGuardianRunnable(t, gs, uint(i), obsDb) // #nosec G115 -- This conversion is safe based on the constant used above err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) if i == 0 && numGuardians > 1 { time.Sleep(time.Second) // give the bootstrap guardian some time to start up @@ -1242,7 +1242,7 @@ func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessa go func() { // feed observations to nodes - for i := 0; i < numMessages; i++ { + for i := uint64(0); i < numMessages; i++ { select { case <-ctx.Done(): return @@ -1257,7 +1257,7 @@ func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessa }() // check that the VAAs were generated - for i := 0; i < numMessages; i++ { + for i := uint64(0); i < numMessages; i++ { msgId := &publicrpcv1.MessageID{ EmitterChain: publicrpcv1.ChainID(someMsgEmitterChain), EmitterAddress: someMsgEmitter.String(), @@ -1279,7 +1279,8 @@ func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessa logger.Info("Tests completed.") t.StopTimer() logsize := setupLogsCapture.Reset() - logsize = logsize / uint64(numMessages) / uint64(numGuardians) // normalize + // normalize + logsize = logsize / uint64(numMessages) / uint64(numGuardians) // #nosec G115 -- These conversions are safe based on the constants used above logger.Warn("benchmarkConsensus: logsize report", zap.Uint64("logbytes_per_msg", logsize)) supervisor.Signal(ctx, supervisor.SignalDone) rootCtxCancel() diff --git a/node/pkg/node/reobserve.go b/node/pkg/node/reobserve.go index 7e77eb2722..69ebfcf26b 100644 --- a/node/pkg/node/reobserve.go +++ b/node/pkg/node/reobserve.go @@ -3,6 +3,7 @@ package node import ( "context" "encoding/hex" + "math" "time" "github.com/benbjohnson/clock" @@ -42,6 +43,13 @@ func handleReobservationRequests( } } case req := <-obsvReqC: + if req.ChainId > math.MaxUint16 { + logger.Error("chain id is larger than MaxUint16", + zap.Uint32("chain_id", req.ChainId), + ) + continue + } + r := cachedRequest{ chainId: vaa.ChainID(req.ChainId), txHash: hex.EncodeToString(req.TxHash), diff --git a/node/pkg/node/reobserve_test.go b/node/pkg/node/reobserve_test.go index 1f84918f19..54efdaeebf 100644 --- a/node/pkg/node/reobserve_test.go +++ b/node/pkg/node/reobserve_test.go @@ -29,7 +29,7 @@ func setUpReobservationTest() (reobservationTestContext, func()) { chainObsvReqC := make(map[vaa.ChainID]chan *gossipv1.ObservationRequest) for i := 0; i < 10; i++ { - chainObsvReqC[vaa.ChainID(i)] = make(chan *gossipv1.ObservationRequest, 1) + chainObsvReqC[vaa.ChainID(i)] = make(chan *gossipv1.ObservationRequest, 1) // #nosec G115 -- This conversion is safe as we're only looping to 10 } go handleReobservationRequests(ctx, clock, zap.NewNop(), obsvReqC, chainObsvReqC) @@ -66,7 +66,7 @@ func TestReobservationRequest(t *testing.T) { ctx.obsvReqC <- req - actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above require.True(t, ok) assert.Equal(t, req, actual) @@ -83,14 +83,14 @@ func TestDuplicateReobservation(t *testing.T) { ctx.obsvReqC <- req - actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above require.True(t, ok) assert.Equal(t, req, actual) // Receiving the same request again should not trigger another re-observation. ctx.obsvReqC <- req - _, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + _, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above assert.False(t, ok) } @@ -105,7 +105,7 @@ func TestMultipleReobservations(t *testing.T) { ctx.obsvReqC <- req - actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above require.True(t, ok) assert.Equal(t, req, actual) @@ -114,7 +114,7 @@ func TestMultipleReobservations(t *testing.T) { ctx.obsvReqC <- req - actual, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above require.True(t, ok) assert.Equal(t, req, actual) @@ -122,7 +122,7 @@ func TestMultipleReobservations(t *testing.T) { req.ChainId = 3 ctx.obsvReqC <- req - actual, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above require.True(t, ok) assert.Equal(t, req, actual) } @@ -132,13 +132,13 @@ func TestReobserveUnknownChainId(t *testing.T) { defer cancel() req := &gossipv1.ObservationRequest{ - ChainId: uint32(len(ctx.chainObsvReqC)) + 1, + ChainId: uint32(len(ctx.chainObsvReqC)) + 1, // #nosec G115 -- This is safe as it's 10 + 1 TxHash: []byte{0xe5, 0x9c, 0x1b, 0xe5, 0x0b, 0xe7, 0xe4, 0x7e}, } ctx.obsvReqC <- req - _, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + _, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 10 + 1 above assert.False(t, ok) } @@ -153,7 +153,7 @@ func TestReobservationCacheEviction(t *testing.T) { ctx.obsvReqC <- req - actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above require.True(t, ok) assert.Equal(t, req, actual) @@ -163,7 +163,7 @@ func TestReobservationCacheEviction(t *testing.T) { // Receiving the same request again should not trigger another re-observation. ctx.obsvReqC <- req - _, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + _, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above assert.False(t, ok) // Advance the clock by another 7 minutes, which should evict the re-observation request @@ -173,7 +173,7 @@ func TestReobservationCacheEviction(t *testing.T) { // This time the request should be passed through. ctx.obsvReqC <- req - actual, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above require.True(t, ok) assert.Equal(t, req, actual) } @@ -203,10 +203,10 @@ func TestBlockingSend(t *testing.T) { // when the handler is done without adding unnecessary complexity. time.Sleep(50 * time.Millisecond) - actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) + actual, ok := readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req.ChainId)]) // #nosec G115 -- Chain id set to 1 above assert.True(t, ok) assert.Equal(t, req, actual) - _, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req2.ChainId)]) + _, ok = readFromChannel(ctx, ctx.chainObsvReqC[vaa.ChainID(req2.ChainId)]) // #nosec G115 -- Chain id set to 1 above assert.False(t, ok) } diff --git a/node/pkg/p2p/netmetrics.go b/node/pkg/p2p/netmetrics.go index d565f39b03..ae5b6849ef 100644 --- a/node/pkg/p2p/netmetrics.go +++ b/node/pkg/p2p/netmetrics.go @@ -38,7 +38,7 @@ func collectNodeMetrics(addr common.Address, peerId peer.ID, hb *gossipv1.Heartb continue } - chain := vaa.ChainID(n.Id) + chain := vaa.ChainID(n.Id) // #nosec G115 -- This is safe as chain id is constrained in SetNetworkStats wormholeNetworkNodeHeight.WithLabelValues( addr.Hex(), peerId.String(), hb.NodeName, chain.String()).Set(float64(n.Height)) diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index bde810819e..d8229d8e29 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -518,7 +518,7 @@ func Run(params *RunParams) func(ctx context.Context) error { defer DefaultRegistry.mu.Unlock() networks := make([]*gossipv1.Heartbeat_Network, 0, len(DefaultRegistry.networkStats)) for _, v := range DefaultRegistry.networkStats { - errCtr := DefaultRegistry.GetErrorCount(vaa.ChainID(v.Id)) + errCtr := DefaultRegistry.GetErrorCount(vaa.ChainID(v.Id)) // #nosec G115 -- This is safe as chain id is constrained in SetNetworkStats v.ErrorCount = errCtr networks = append(networks, v) } diff --git a/node/pkg/p2p/watermark_test.go b/node/pkg/p2p/watermark_test.go index 7c99ede7c9..73adfed45e 100644 --- a/node/pkg/p2p/watermark_test.go +++ b/node/pkg/p2p/watermark_test.go @@ -116,7 +116,7 @@ func TestWatermark(t *testing.T) { var gs [4]*G for i := range gs { gs[i] = NewG(t, fmt.Sprintf("n%d", i)) - gs[i].components.Port = uint(LOCAL_P2P_PORTRANGE_START + i) + gs[i].components.Port = uint(LOCAL_P2P_PORTRANGE_START + i) // #nosec G115 -- This is safe as the inputs are constants gs[i].networkID = "/wormhole/localdev" guardianset.Keys = append(guardianset.Keys, crypto.PubkeyToAddress(gs[i].guardianSigner.PublicKey(ctx))) diff --git a/node/pkg/processor/benchmark_test.go b/node/pkg/processor/benchmark_test.go index c85fe5031b..6823ca2636 100644 --- a/node/pkg/processor/benchmark_test.go +++ b/node/pkg/processor/benchmark_test.go @@ -51,7 +51,7 @@ func BenchmarkHandleObservation(b *testing.B) { var totalTime, underQuorumTime, quorumReachedTime, overQuorumTime, handleMsgTime time.Duration var totalCount, underQuorumCount, quorumReachedCount, overQuorumCount int for count := 0; count < NumObservations; count++ { - k := pd.createMessagePublication(b, uint64(count)) + k := pd.createMessagePublication(b, uint64(count)) // #nosec G115 -- Safe as NumObservations hard coded above start := time.Now() p.handleMessage(ctx, k) handleMsgTime += time.Since(start) @@ -107,7 +107,7 @@ func BenchmarkProfileHandleObservation(b *testing.B) { require.NotNil(b, pd) for count := 0; count < NumObservations; count++ { - k := pd.createMessagePublication(b, uint64(count)) + k := pd.createMessagePublication(b, uint64(count)) // #nosec G115 -- Safe as NumObservations hard coded above p.handleMessage(ctx, k) for guardianIdx := 1; guardianIdx < 19; guardianIdx++ { @@ -210,7 +210,7 @@ func (pd *ProcessorData) createObservation(b *testing.B, guardianIdx int, k *com v := &VAA{ VAA: vaa.VAA{ Version: vaa.SupportedVAAVersion, - GuardianSetIndex: uint32(guardianIdx), + GuardianSetIndex: uint32(guardianIdx), // #nosec G115 -- Safe as number of guardians constrained to 19 in these tests Signatures: nil, Timestamp: k.Timestamp, Nonce: k.Nonce, diff --git a/node/pkg/processor/observation.go b/node/pkg/processor/observation.go index 3fcea6318d..d25789f153 100644 --- a/node/pkg/processor/observation.go +++ b/node/pkg/processor/observation.go @@ -4,6 +4,7 @@ package processor import ( "encoding/hex" "fmt" + "math" "time" node_common "github.com/certusone/wormhole/node/pkg/common" @@ -49,6 +50,11 @@ var ( func signaturesToVaaFormat(signatures map[common.Address][]byte, gsKeys []common.Address) []*vaa.Signature { // Aggregate all valid signatures into a list of vaa.Signature and construct signed VAA. var sigs []*vaa.Signature + + if len(gsKeys) > math.MaxUint8 { + panic(fmt.Sprintf("guardian set too large: %d", len(gsKeys))) + } + for i, a := range gsKeys { sig, ok := signatures[a] @@ -59,7 +65,7 @@ func signaturesToVaaFormat(signatures map[common.Address][]byte, gsKeys []common } sigs = append(sigs, &vaa.Signature{ - Index: uint8(i), + Index: uint8(i), // #nosec G115 -- This is validated above Signature: bs, }) } diff --git a/node/pkg/publicrpc/publicrpcserver.go b/node/pkg/publicrpc/publicrpcserver.go index de238e8868..2aaf36fc98 100644 --- a/node/pkg/publicrpc/publicrpcserver.go +++ b/node/pkg/publicrpc/publicrpcserver.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "fmt" + "math" "github.com/certusone/wormhole/node/pkg/common" "github.com/certusone/wormhole/node/pkg/db" @@ -68,7 +69,11 @@ func (s *PublicrpcServer) GetSignedVAA(ctx context.Context, req *publicrpcv1.Get return nil, status.Error(codes.InvalidArgument, "no message ID specified") } - chainID := vaa.ChainID(req.MessageId.EmitterChain.Number()) + if req.MessageId.GetEmitterChain() > math.MaxUint16 { + return nil, status.Error(codes.InvalidArgument, "emitter chain id must be no greater than 16 bits") + } + + chainID := vaa.ChainID(req.MessageId.GetEmitterChain()) // #nosec G115 -- This conversion is checked above // This interface is not supported for PythNet messages because those VAAs are not stored in the database. if chainID == vaa.ChainIDPythNet { diff --git a/node/pkg/query/request.go b/node/pkg/query/request.go index 854707049c..8d59496f14 100644 --- a/node/pkg/query/request.go +++ b/node/pkg/query/request.go @@ -260,7 +260,7 @@ func (queryRequest *QueryRequest) Marshal() ([]byte, error) { vaa.MustWrite(buf, binary.BigEndian, MSG_VERSION) // version vaa.MustWrite(buf, binary.BigEndian, queryRequest.Nonce) // uint32 - vaa.MustWrite(buf, binary.BigEndian, uint8(len(queryRequest.PerChainQueries))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(queryRequest.PerChainQueries))) // #nosec G115 -- `PerChainQueries` length checked in `Validate` for _, perChainQuery := range queryRequest.PerChainQueries { pcqBuf, err := perChainQuery.Marshal() if err != nil { @@ -375,7 +375,7 @@ func (perChainQuery *PerChainQueryRequest) Marshal() ([]byte, error) { if len(queryBuf) > math.MaxUint32 { return nil, fmt.Errorf("query too long") } - vaa.MustWrite(buf, binary.BigEndian, uint32(len(queryBuf))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(queryBuf))) // #nosec G115 -- This conversion is safe as it is checked above buf.Write(queryBuf) return buf.Bytes(), nil @@ -552,13 +552,13 @@ func (ecd *EthCallQueryRequest) Marshal() ([]byte, error) { } buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.BlockId))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.BlockId))) // #nosec G115 -- This is validated in `Validate` buf.Write([]byte(ecd.BlockId)) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) // #nosec G115 -- This is validated in `Validate` for _, callData := range ecd.CallData { buf.Write(callData.To) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) // #nosec G115 -- This is validated in `Validate` buf.Write(callData.Data) } return buf.Bytes(), nil @@ -684,16 +684,16 @@ func (ecd *EthCallByTimestampQueryRequest) Marshal() ([]byte, error) { buf := new(bytes.Buffer) vaa.MustWrite(buf, binary.BigEndian, ecd.TargetTimestamp) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.TargetBlockIdHint))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.TargetBlockIdHint))) // #nosec G115 -- This is validated in `Validate` buf.Write([]byte(ecd.TargetBlockIdHint)) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.FollowingBlockIdHint))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.FollowingBlockIdHint))) // #nosec G115 -- This is validated in `Validate` buf.Write([]byte(ecd.FollowingBlockIdHint)) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) // #nosec G115 -- This is validated in `Validate` for _, callData := range ecd.CallData { buf.Write(callData.To) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) // #nosec G115 -- This is validated in `Validate` buf.Write(callData.Data) } return buf.Bytes(), nil @@ -850,16 +850,16 @@ func (ecd *EthCallWithFinalityQueryRequest) Marshal() ([]byte, error) { } buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.BlockId))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.BlockId))) // #nosec G115 -- This is validated in `Validate` buf.Write([]byte(ecd.BlockId)) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.Finality))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.Finality))) // #nosec G115 -- This is validated in `Validate` buf.Write([]byte(ecd.Finality)) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) // #nosec G115 -- This is validated in `Validate` for _, callData := range ecd.CallData { buf.Write(callData.To) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) // #nosec G115 -- This is validated in `Validate` buf.Write(callData.Data) } return buf.Bytes(), nil @@ -1010,14 +1010,14 @@ func (saq *SolanaAccountQueryRequest) Marshal() ([]byte, error) { buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(saq.Commitment))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(saq.Commitment))) // #nosec G115 -- This is validated in `Validate` buf.Write([]byte(saq.Commitment)) vaa.MustWrite(buf, binary.BigEndian, saq.MinContextSlot) vaa.MustWrite(buf, binary.BigEndian, saq.DataSliceOffset) vaa.MustWrite(buf, binary.BigEndian, saq.DataSliceLength) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(saq.Accounts))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(saq.Accounts))) // #nosec G115 -- This is validated in `Validate` for _, acct := range saq.Accounts { buf.Write(acct[:]) } @@ -1142,19 +1142,19 @@ func (spda *SolanaPdaQueryRequest) Marshal() ([]byte, error) { buf := new(bytes.Buffer) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(spda.Commitment))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(spda.Commitment))) // #nosec G115 -- This is validated in `Validate` buf.Write([]byte(spda.Commitment)) vaa.MustWrite(buf, binary.BigEndian, spda.MinContextSlot) vaa.MustWrite(buf, binary.BigEndian, spda.DataSliceOffset) vaa.MustWrite(buf, binary.BigEndian, spda.DataSliceLength) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(spda.PDAs))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(spda.PDAs))) // #nosec G115 -- This is validated in `Validate` for _, pda := range spda.PDAs { buf.Write(pda.ProgramAddress[:]) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(pda.Seeds))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(pda.Seeds))) // #nosec G115 -- This is validated in `Validate` for _, seed := range pda.Seeds { - vaa.MustWrite(buf, binary.BigEndian, uint32(len(seed))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(seed))) // #nosec G115 -- This is validated in `Validate` buf.Write(seed) } } diff --git a/node/pkg/query/response.go b/node/pkg/query/response.go index 7d4d4fa292..378628321f 100644 --- a/node/pkg/query/response.go +++ b/node/pkg/query/response.go @@ -201,12 +201,12 @@ func (msg *QueryResponsePublication) Marshal() ([]byte, error) { if len(msg.Request.QueryRequest) > math.MaxUint32 { return nil, fmt.Errorf("request too long") } - vaa.MustWrite(buf, binary.BigEndian, uint32(len(msg.Request.QueryRequest))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(msg.Request.QueryRequest))) // #nosec G115 -- This is validated above buf.Write(msg.Request.QueryRequest) // Per chain responses - vaa.MustWrite(buf, binary.BigEndian, uint8(len(msg.PerChainResponses))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(msg.PerChainResponses))) // #nosec G115 -- This is validated in `Validate` for idx := range msg.PerChainResponses { pcrBuf, err := msg.PerChainResponses[idx].Marshal() if err != nil { @@ -392,7 +392,7 @@ func (perChainResponse *PerChainQueryResponse) Marshal() ([]byte, error) { if len(respBuf) > math.MaxUint32 { return nil, fmt.Errorf("response is too long") } - vaa.MustWrite(buf, binary.BigEndian, uint32(len(respBuf))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(respBuf))) // #nosec G115 -- This is validated above buf.Write(respBuf) return buf.Bytes(), nil } @@ -564,9 +564,9 @@ func (ecr *EthCallQueryResponse) Marshal() ([]byte, error) { buf.Write(ecr.Hash[:]) vaa.MustWrite(buf, binary.BigEndian, ecr.Time.UnixMicro()) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecr.Results))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecr.Results))) // #nosec G115 -- This is validated in `Validate` for idx := range ecr.Results { - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecr.Results[idx]))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecr.Results[idx]))) // #nosec G115 -- This is validated in `Validate` buf.Write(ecr.Results[idx]) } @@ -690,9 +690,9 @@ func (ecr *EthCallByTimestampQueryResponse) Marshal() ([]byte, error) { buf.Write(ecr.FollowingBlockHash[:]) vaa.MustWrite(buf, binary.BigEndian, ecr.FollowingBlockTime.UnixMicro()) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecr.Results))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecr.Results))) // #nosec G115 -- This is validated in `Validate` for idx := range ecr.Results { - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecr.Results[idx]))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecr.Results[idx]))) // #nosec G115 -- This is validated in `Validate` buf.Write(ecr.Results[idx]) } @@ -844,9 +844,9 @@ func (ecr *EthCallWithFinalityQueryResponse) Marshal() ([]byte, error) { buf.Write(ecr.Hash[:]) vaa.MustWrite(buf, binary.BigEndian, ecr.Time.UnixMicro()) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecr.Results))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecr.Results))) // #nosec G115 -- This is validated in `Validate` for idx := range ecr.Results { - vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecr.Results[idx]))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecr.Results[idx]))) // #nosec G115 -- This is validated in `Validate` buf.Write(ecr.Results[idx]) } @@ -966,14 +966,14 @@ func (sar *SolanaAccountQueryResponse) Marshal() ([]byte, error) { vaa.MustWrite(buf, binary.BigEndian, sar.BlockTime.UnixMicro()) buf.Write(sar.BlockHash[:]) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(sar.Results))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(sar.Results))) // #nosec G115 -- This is validated in `Validate` for _, res := range sar.Results { vaa.MustWrite(buf, binary.BigEndian, res.Lamports) vaa.MustWrite(buf, binary.BigEndian, res.RentEpoch) vaa.MustWrite(buf, binary.BigEndian, res.Executable) buf.Write(res.Owner[:]) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(res.Data))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(res.Data))) // #nosec G115 -- This is validated in `Validate` buf.Write(res.Data) } @@ -1113,7 +1113,7 @@ func (sar *SolanaPdaQueryResponse) Marshal() ([]byte, error) { vaa.MustWrite(buf, binary.BigEndian, sar.BlockTime.UnixMicro()) buf.Write(sar.BlockHash[:]) - vaa.MustWrite(buf, binary.BigEndian, uint8(len(sar.Results))) + vaa.MustWrite(buf, binary.BigEndian, uint8(len(sar.Results))) // #nosec G115 -- This is validated in `Validate` for _, res := range sar.Results { buf.Write(res.Account[:]) vaa.MustWrite(buf, binary.BigEndian, res.Bump) @@ -1122,7 +1122,7 @@ func (sar *SolanaPdaQueryResponse) Marshal() ([]byte, error) { vaa.MustWrite(buf, binary.BigEndian, res.Executable) buf.Write(res.Owner[:]) - vaa.MustWrite(buf, binary.BigEndian, uint32(len(res.Data))) + vaa.MustWrite(buf, binary.BigEndian, uint32(len(res.Data))) // #nosec G115 -- This is validated in `Validate` buf.Write(res.Data) } diff --git a/node/pkg/query/response_test.go b/node/pkg/query/response_test.go index 9b39d2ea45..dc302018ce 100644 --- a/node/pkg/query/response_test.go +++ b/node/pkg/query/response_test.go @@ -39,7 +39,7 @@ func createQueryResponseFromRequestWithRequestBytes(t *testing.T, queryRequest * perChainResponses = append(perChainResponses, &PerChainQueryResponse{ ChainId: pcr.ChainId, Response: &EthCallQueryResponse{ - BlockNumber: uint64(1000 + idx), + BlockNumber: uint64(1000 + idx), // #nosec G115 -- This is safe in this test suite Hash: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e2"), Time: timeForTest(t, time.Now()), Results: results, @@ -54,10 +54,10 @@ func createQueryResponseFromRequestWithRequestBytes(t *testing.T, queryRequest * perChainResponses = append(perChainResponses, &PerChainQueryResponse{ ChainId: pcr.ChainId, Response: &EthCallByTimestampQueryResponse{ - TargetBlockNumber: uint64(1000 + idx), + TargetBlockNumber: uint64(1000 + idx), // #nosec G115 -- This is safe in this test suite TargetBlockHash: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e2"), TargetBlockTime: timeForTest(t, time.Now()), - FollowingBlockNumber: uint64(1000 + idx + 1), + FollowingBlockNumber: uint64(1000 + idx + 1), // #nosec G115 -- This is safe in this test suite FollowingBlockHash: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e3"), FollowingBlockTime: timeForTest(t, time.Now().Add(10*time.Second)), Results: results, @@ -72,7 +72,7 @@ func createQueryResponseFromRequestWithRequestBytes(t *testing.T, queryRequest * perChainResponses = append(perChainResponses, &PerChainQueryResponse{ ChainId: pcr.ChainId, Response: &EthCallWithFinalityQueryResponse{ - BlockNumber: uint64(1000 + idx), + BlockNumber: uint64(1000 + idx), // #nosec G115 -- This is safe in this test suite Hash: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e2"), Time: timeForTest(t, time.Now()), Results: results, @@ -282,8 +282,8 @@ func createSolanaAccountQueryResponseFromRequest(t *testing.T, queryRequest *Que results := []SolanaAccountResult{} for idx := range req.Accounts { results = append(results, SolanaAccountResult{ - Lamports: uint64(2000 + idx), - RentEpoch: uint64(3000 + idx), + Lamports: uint64(2000 + idx), // #nosec G115 -- This is safe in this test suite + RentEpoch: uint64(3000 + idx), // #nosec G115 -- This is safe in this test suite Executable: (idx%2 == 0), Owner: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e2"), Data: []byte([]byte(fmt.Sprintf("Result %d", idx))), @@ -292,7 +292,7 @@ func createSolanaAccountQueryResponseFromRequest(t *testing.T, queryRequest *Que perChainResponses = append(perChainResponses, &PerChainQueryResponse{ ChainId: pcr.ChainId, Response: &SolanaAccountQueryResponse{ - SlotNumber: uint64(1000 + idx), + SlotNumber: uint64(1000 + idx), // #nosec G115 -- This is safe in this test suite BlockTime: timeForTest(t, time.Now()), BlockHash: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e3"), Results: results, @@ -345,9 +345,9 @@ func createSolanaPdaQueryResponseFromRequest(t *testing.T, queryRequest *QueryRe for idx := range req.PDAs { results = append(results, SolanaPdaResult{ Account: ethCommon.HexToHash("4fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773e"), - Bump: uint8(255 - idx), - Lamports: uint64(2000 + idx), - RentEpoch: uint64(3000 + idx), + Bump: uint8(255 - idx), // #nosec G115 -- This is safe in this test suite + Lamports: uint64(2000 + idx), // #nosec G115 -- This is safe in this test suite + RentEpoch: uint64(3000 + idx), // #nosec G115 -- This is safe in this test suite Executable: (idx%2 == 0), Owner: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e2"), Data: []byte([]byte(fmt.Sprintf("Result %d", idx))), @@ -356,7 +356,7 @@ func createSolanaPdaQueryResponseFromRequest(t *testing.T, queryRequest *QueryRe perChainResponses = append(perChainResponses, &PerChainQueryResponse{ ChainId: pcr.ChainId, Response: &SolanaPdaQueryResponse{ - SlotNumber: uint64(1000 + idx), + SlotNumber: uint64(1000 + idx), // #nosec G115 -- This is safe in this test suite BlockTime: timeForTest(t, time.Now()), BlockHash: ethCommon.HexToHash("0x9999bac44d09a7f69ee7941819b0a19c59ccb1969640cc513be09ef95ed2d8e3"), Results: results, diff --git a/node/pkg/watchers/algorand/watcher.go b/node/pkg/watchers/algorand/watcher.go index 5928bed401..12afc96a4e 100644 --- a/node/pkg/watchers/algorand/watcher.go +++ b/node/pkg/watchers/algorand/watcher.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "math" "time" "github.com/algorand/go-algorand-sdk/client/v2/algod" @@ -237,7 +238,7 @@ func (e *Watcher) Run(ctx context.Context) error { case <-ctx.Done(): return nil case r := <-e.obsvReqC: - if vaa.ChainID(r.ChainId) != vaa.ChainIDAlgorand { + if r.ChainId > math.MaxUint16 || vaa.ChainID(r.ChainId) != vaa.ChainIDAlgorand { panic("invalid chain ID") } @@ -298,9 +299,15 @@ func (e *Watcher) Run(ctx context.Context) error { } } + if status.LastRound > math.MaxInt64 { + logger.Error("Last round not a valid uint64: ", zap.Uint64("lastRound", status.LastRound)) + p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDAlgorand, 1) + continue + } + currentAlgorandHeight.Set(float64(status.LastRound)) p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDAlgorand, &gossipv1.Heartbeat_Network{ - Height: int64(status.LastRound), + Height: int64(status.LastRound), // #nosec G115 -- This is validated above ContractAddress: fmt.Sprintf("%d", e.appid), }) diff --git a/node/pkg/watchers/aptos/watcher.go b/node/pkg/watchers/aptos/watcher.go index 9ea36ac4bb..c8819b0aa0 100644 --- a/node/pkg/watchers/aptos/watcher.go +++ b/node/pkg/watchers/aptos/watcher.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "io" + "math" "net/http" "time" @@ -103,7 +104,7 @@ func (e *Watcher) Run(ctx context.Context) error { case <-ctx.Done(): return ctx.Err() case r := <-e.obsvReqC: - if vaa.ChainID(r.ChainId) != vaa.ChainIDAptos { + if r.ChainId > math.MaxUint16 || vaa.ChainID(r.ChainId) != vaa.ChainIDAptos { panic("invalid chain ID") } @@ -225,10 +226,16 @@ func (e *Watcher) Run(ctx context.Context) error { blockHeight := pHealth.Get("block_height") + if blockHeight.Uint() > math.MaxInt64 { + logger.Error("Block height not a valid uint64: ", zap.Uint64("blockHeight", blockHeight.Uint())) + p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDAptos, 1) + continue + } + if blockHeight.Exists() { currentAptosHeight.Set(float64(blockHeight.Uint())) p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDAptos, &gossipv1.Heartbeat_Network{ - Height: int64(blockHeight.Uint()), + Height: int64(blockHeight.Uint()), // #nosec G115 -- This is validated above ContractAddress: e.aptosAccount, }) @@ -304,15 +311,25 @@ func (e *Watcher) observeData(logger *zap.Logger, data gjson.Result, nativeSeq u return } + if nonce.Uint() > math.MaxUint32 { + logger.Error("nonce is larger than expected MaxUint32") + return + } + + if consistencyLevel.Uint() > math.MaxUint8 { + logger.Error("consistency level is larger than expected MaxUint8") + return + } + observation := &common.MessagePublication{ TxHash: txHash, - Timestamp: time.Unix(int64(ts.Uint()), 0), - Nonce: uint32(nonce.Uint()), // uint32 + Timestamp: time.Unix(int64(ts.Uint()), 0), // #nosec G115 -- This conversion is safe indefinitely + Nonce: uint32(nonce.Uint()), // #nosec G115 -- This is validated above Sequence: sequence.Uint(), EmitterChain: vaa.ChainIDAptos, EmitterAddress: a, Payload: pl, - ConsistencyLevel: uint8(consistencyLevel.Uint()), + ConsistencyLevel: uint8(consistencyLevel.Uint()), // #nosec G115 -- This is validated above IsReobservation: isReobservation, } diff --git a/node/pkg/watchers/cosmwasm/watcher.go b/node/pkg/watchers/cosmwasm/watcher.go index 0cf856518b..9c95764a18 100644 --- a/node/pkg/watchers/cosmwasm/watcher.go +++ b/node/pkg/watchers/cosmwasm/watcher.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "io" + "math" "net/http" "strconv" "time" @@ -242,7 +243,7 @@ func (e *Watcher) Run(ctx context.Context) error { case <-ctx.Done(): return nil case r := <-e.obsvReqC: - if vaa.ChainID(r.ChainId) != e.chainID { + if r.ChainId > math.MaxUint16 || vaa.ChainID(r.ChainId) != e.chainID { panic("invalid chain ID") } diff --git a/node/pkg/watchers/evm/by_transaction.go b/node/pkg/watchers/evm/by_transaction.go index 61c0ec0d00..31f080e042 100644 --- a/node/pkg/watchers/evm/by_transaction.go +++ b/node/pkg/watchers/evm/by_transaction.go @@ -76,7 +76,7 @@ func MessageEventsForTransaction( message := &common.MessagePublication{ TxHash: ev.Raw.TxHash, - Timestamp: time.Unix(int64(blockTime), 0), + Timestamp: time.Unix(int64(blockTime), 0), // #nosec G115 -- This conversion is safe indefinitely Nonce: ev.Nonce, Sequence: ev.Sequence, EmitterChain: chainId, diff --git a/node/pkg/watchers/evm/ccq.go b/node/pkg/watchers/evm/ccq.go index 274608179a..760802a208 100644 --- a/node/pkg/watchers/evm/ccq.go +++ b/node/pkg/watchers/evm/ccq.go @@ -179,7 +179,7 @@ func (w *Watcher) ccqHandleEthCallQueryRequest(ctx context.Context, queryRequest resp := query.EthCallQueryResponse{ BlockNumber: blockResult.Number.ToInt().Uint64(), Hash: blockResult.Hash, - Time: time.Unix(int64(blockResult.Time), 0), + Time: time.Unix(int64(blockResult.Time), 0), // #nosec G115 -- This conversion is safe indefinitely Results: results, } @@ -478,10 +478,10 @@ func (w *Watcher) ccqHandleEthCallByTimestampQueryRequest(ctx context.Context, q resp := query.EthCallByTimestampQueryResponse{ TargetBlockNumber: targetBlockNum, TargetBlockHash: blockResult.Hash, - TargetBlockTime: time.Unix(int64(blockResult.Time), 0), + TargetBlockTime: time.Unix(int64(blockResult.Time), 0), // #nosec G115 -- This conversion is safe indefinitely FollowingBlockNumber: followingBlockNum, FollowingBlockHash: nextBlockResult.Hash, - FollowingBlockTime: time.Unix(int64(nextBlockResult.Time), 0), + FollowingBlockTime: time.Unix(int64(nextBlockResult.Time), 0), // #nosec G115 -- This conversion is safe indefinitely Results: results, } @@ -620,7 +620,7 @@ func (w *Watcher) ccqHandleEthCallWithFinalityQueryRequest(ctx context.Context, resp := query.EthCallWithFinalityQueryResponse{ BlockNumber: blockNumber, Hash: blockResult.Hash, - Time: time.Unix(int64(blockResult.Time), 0), + Time: time.Unix(int64(blockResult.Time), 0), // #nosec G115 -- This conversion is safe indefinitely Results: results, } diff --git a/node/pkg/watchers/evm/ccq_backfill.go b/node/pkg/watchers/evm/ccq_backfill.go index 4f19e3d36c..63d8c3d7ac 100644 --- a/node/pkg/watchers/evm/ccq_backfill.go +++ b/node/pkg/watchers/evm/ccq_backfill.go @@ -108,7 +108,7 @@ func (w *Watcher) ccqBackfillInit(ctx context.Context) error { // Query for more blocks until we go back the desired length of time. The last block in the array will be the oldest, so query starting one before that. for blocks[len(blocks)-1].Timestamp > cutOffTime { - newBlocks, err := w.ccqBackfillGetBlocks(ctx, int64(blocks[len(blocks)-1].BlockNum-1), w.ccqBatchSize) + newBlocks, err := w.ccqBackfillGetBlocks(ctx, blocks[len(blocks)-1].BlockNum-1, w.ccqBatchSize) if err != nil { return fmt.Errorf("failed to get batch starting at %d: %w", blocks[len(blocks)-1].BlockNum-1, err) } @@ -124,8 +124,8 @@ func (w *Watcher) ccqBackfillInit(ctx context.Context) error { zap.Uint64("latestBlockNum", newBlocks[0].BlockNum), zap.Uint64("oldestBlockTimestamp", newBlocks[len(newBlocks)-1].Timestamp), zap.Uint64("latestBlockTimestamp", newBlocks[0].Timestamp), - zap.Stringer("oldestTime", time.Unix(int64(newBlocks[len(newBlocks)-1].Timestamp), 0)), - zap.Stringer("latestTime", time.Unix(int64(newBlocks[0].Timestamp), 0)), + zap.Stringer("oldestTime", time.Unix(int64(newBlocks[len(newBlocks)-1].Timestamp), 0)), // #nosec G115 -- This conversion is safe indefinitely + zap.Stringer("latestTime", time.Unix(int64(newBlocks[0].Timestamp), 0)), // #nosec G115 -- This conversion is safe indefinitely ) } @@ -136,8 +136,8 @@ func (w *Watcher) ccqBackfillInit(ctx context.Context) error { zap.Uint64("latestBlockNum", blocks[0].BlockNum), zap.Uint64("oldestBlockTimestamp", blocks[len(blocks)-1].Timestamp), zap.Uint64("latestBlockTimestamp", blocks[0].Timestamp), - zap.Stringer("oldestTime", time.Unix(int64(blocks[len(blocks)-1].Timestamp), 0)), - zap.Stringer("latestTime", time.Unix(int64(blocks[0].Timestamp), 0)), + zap.Stringer("oldestTime", time.Unix(int64(blocks[len(blocks)-1].Timestamp), 0)), // #nosec G115 -- This conversion is safe indefinitely + zap.Stringer("latestTime", time.Unix(int64(blocks[0].Timestamp), 0)), // #nosec G115 -- This conversion is safe indefinitely ) w.ccqTimestampCache.AddBatch(blocks) @@ -227,8 +227,8 @@ func ccqBackFillDetermineMaxBatchSize(ctx context.Context, logger *zap.Logger, c } // ccqBackfillGetBlocks gets a range of blocks from the RPC, starting from initialBlockNum and going downward for numBlocks. -func (w *Watcher) ccqBackfillGetBlocks(ctx context.Context, initialBlockNum int64, numBlocks int64) (Blocks, error) { - w.ccqLogger.Info("getting batch", zap.Int64("initialBlockNum", initialBlockNum), zap.Int64("numBlocks", numBlocks)) +func (w *Watcher) ccqBackfillGetBlocks(ctx context.Context, initialBlockNum uint64, numBlocks int64) (Blocks, error) { + w.ccqLogger.Info("getting batch", zap.Uint64("initialBlockNum", initialBlockNum), zap.Int64("numBlocks", numBlocks)) batch := make([]ethRpc.BatchElem, numBlocks) results := make([]ccqBatchResult, numBlocks) blockNum := initialBlockNum @@ -251,9 +251,9 @@ func (w *Watcher) ccqBackfillGetBlocks(ctx context.Context, initialBlockNum int6 cancel() if err != nil { w.ccqLogger.Error("failed to get batch of blocks", - zap.Int64("initialBlockNum", initialBlockNum), + zap.Uint64("initialBlockNum", initialBlockNum), zap.Int64("numBlocks", numBlocks), - zap.Int64("finalBlockNum", blockNum), + zap.Uint64("finalBlockNum", blockNum), zap.Error(err), ) @@ -290,14 +290,14 @@ func (w *Watcher) ccqPerformBackfill(ctx context.Context, evt *ccqBackfillReques return } - numBlocks := int64(lastBlock - firstBlock - 1) + numBlocks := int64(lastBlock - firstBlock - 1) // #nosec G115 -- Realistically impossible to overflow if numBlocks > w.ccqBatchSize { numBlocks = w.ccqBatchSize } w.ccqLogger.Info("received a backfill request", zap.Uint64("timestamp", evt.timestamp), zap.Uint64("firstBlock", firstBlock), zap.Uint64("lastBlock", lastBlock), zap.Int64("numBlocks", numBlocks)) - blocks, err := w.ccqBackfillGetBlocks(ctx, int64(lastBlock-1), numBlocks) + blocks, err := w.ccqBackfillGetBlocks(ctx, lastBlock-1, numBlocks) if err != nil { - w.ccqLogger.Error("failed to get backfill batch", zap.Int64("startingBlock", int64(lastBlock-1)), zap.Int64("numBlocks", numBlocks)) + w.ccqLogger.Error("failed to get backfill batch", zap.Uint64("startingBlock", lastBlock-1), zap.Int64("numBlocks", numBlocks)) return } @@ -308,8 +308,8 @@ func (w *Watcher) ccqPerformBackfill(ctx context.Context, evt *ccqBackfillReques zap.Uint64("latestBlockNum", blocks[0].BlockNum), zap.Uint64("oldestBlockTimestamp", blocks[len(blocks)-1].Timestamp), zap.Uint64("latestBlockTimestamp", blocks[0].Timestamp), - zap.Stringer("oldestTime", time.Unix(int64(blocks[len(blocks)-1].Timestamp), 0)), - zap.Stringer("latestTime", time.Unix(int64(blocks[0].Timestamp), 0)), + zap.Stringer("oldestTime", time.Unix(int64(blocks[len(blocks)-1].Timestamp), 0)), // #nosec G115 -- This conversion is safe indefinitely + zap.Stringer("latestTime", time.Unix(int64(blocks[0].Timestamp), 0)), // #nosec G115 -- This conversion is safe indefinitely ) w.ccqTimestampCache.AddBatch(blocks) diff --git a/node/pkg/watchers/evm/ccq_backfill_test.go b/node/pkg/watchers/evm/ccq_backfill_test.go index d3f44c8f8b..9154763a72 100644 --- a/node/pkg/watchers/evm/ccq_backfill_test.go +++ b/node/pkg/watchers/evm/ccq_backfill_test.go @@ -66,7 +66,7 @@ func TestCcqBackFillDetermineMaxBatchSize(t *testing.T) { } require.Equal(t, batchSize, int64(len(blocks))) - blockNum := uint64(latestBlockNum) + blockNum := uint64(latestBlockNum) // #nosec G115 -- This value is set above so the conversion is safe for _, block := range blocks { assert.Equal(t, blockNum, block.BlockNum) assert.Equal(t, blockNum*10, block.Timestamp) diff --git a/node/pkg/watchers/evm/connectors/batch_poller.go b/node/pkg/watchers/evm/connectors/batch_poller.go index 1262350cac..de6770ed3f 100644 --- a/node/pkg/watchers/evm/connectors/batch_poller.go +++ b/node/pkg/watchers/evm/connectors/batch_poller.go @@ -266,7 +266,7 @@ func (b *BatchPollConnector) getBlockRange(ctx context.Context, logger *zap.Logg batch := make([]rpc.BatchElem, numBlocks) results := make([]BatchResult, numBlocks) - for idx := 0; idx < int(numBlocks); idx++ { + for idx := uint64(0); idx < numBlocks; idx++ { batch[idx] = rpc.BatchElem{ Method: "eth_getBlockByNumber", Args: []interface{}{ diff --git a/node/pkg/watchers/evm/connectors/batch_poller_test.go b/node/pkg/watchers/evm/connectors/batch_poller_test.go index 8cf7052290..142baba5e6 100644 --- a/node/pkg/watchers/evm/connectors/batch_poller_test.go +++ b/node/pkg/watchers/evm/connectors/batch_poller_test.go @@ -144,7 +144,7 @@ func (e *mockConnectorForBatchPoller) setBlockNumbers(finalized, safe, latest ui e.blockNumbers = []uint64{finalized, safe} if latest != 0 { e.headSink <- ðTypes.Header{ - Number: big.NewInt(int64(latest)), + Number: big.NewInt(int64(latest)), // #nosec G115 -- Hardcoded in tests so no risk of overflow Time: latest, } } @@ -156,13 +156,13 @@ func (e *mockConnectorForBatchPoller) setBlockNumbersTwice(finalized1, safe1, la e.blockNumbers = []uint64{finalized1, safe1, finalized2, safe2} if latest1 != 0 { e.headSink <- ðTypes.Header{ - Number: big.NewInt(int64(latest1)), + Number: big.NewInt(int64(latest1)), // #nosec G115 -- Hardcoded in tests so no risk of overflow Time: latest1, } } if latest2 != 0 { e.headSink <- ðTypes.Header{ - Number: big.NewInt(int64(latest2)), + Number: big.NewInt(int64(latest2)), // #nosec G115 -- Hardcoded in tests so no risk of overflow Time: latest2, } } diff --git a/node/pkg/watchers/evm/watcher.go b/node/pkg/watchers/evm/watcher.go index 70b8d379c2..9266beff79 100644 --- a/node/pkg/watchers/evm/watcher.go +++ b/node/pkg/watchers/evm/watcher.go @@ -296,7 +296,7 @@ func (w *Watcher) Run(parentCtx context.Context) error { case r := <-w.obsvReqC: // This can't happen unless there is a programming error - the caller // is expected to send us only requests for our chainID. - if vaa.ChainID(r.ChainId) != w.chainID { + if r.ChainId > math.MaxUint16 || vaa.ChainID(r.ChainId) != w.chainID { panic("invalid chain ID") } @@ -481,7 +481,7 @@ func (w *Watcher) Run(parentCtx context.Context) error { if ev.Finality == connectors.Latest { atomic.StoreUint64(&w.latestBlockNumber, blockNumberU) currentEthHeight.WithLabelValues(w.networkName).Set(float64(blockNumberU)) - stats.Height = int64(blockNumberU) + stats.Height = int64(blockNumberU) // #nosec G115 -- This conversion is safe indefinitely w.updateNetworkStats(&stats) w.ccqAddLatestBlock(ev) continue @@ -492,11 +492,11 @@ func (w *Watcher) Run(parentCtx context.Context) error { if ev.Finality == connectors.Safe { atomic.StoreUint64(&w.latestSafeBlockNumber, blockNumberU) currentEthSafeHeight.WithLabelValues(w.networkName).Set(float64(blockNumberU)) - stats.SafeHeight = int64(blockNumberU) + stats.SafeHeight = int64(blockNumberU) // #nosec G115 -- This conversion is safe indefinitely } else { atomic.StoreUint64(&w.latestFinalizedBlockNumber, blockNumberU) currentEthFinalizedHeight.WithLabelValues(w.networkName).Set(float64(blockNumberU)) - stats.FinalizedHeight = int64(blockNumberU) + stats.FinalizedHeight = int64(blockNumberU) // #nosec G115 -- This conversion is safe indefinitely } w.updateNetworkStats(&stats) @@ -833,7 +833,7 @@ func (w *Watcher) getBlockTime(ctx context.Context, blockHash eth_common.Hash) ( func (w *Watcher) postMessage(logger *zap.Logger, ev *ethabi.AbiLogMessagePublished, blockTime uint64) { message := &common.MessagePublication{ TxHash: ev.Raw.TxHash, - Timestamp: time.Unix(int64(blockTime), 0), + Timestamp: time.Unix(int64(blockTime), 0), // #nosec G115 -- This conversion is safe indefinitely Nonce: ev.Nonce, Sequence: ev.Sequence, EmitterChain: w.chainID, diff --git a/node/pkg/watchers/ibc/watcher.go b/node/pkg/watchers/ibc/watcher.go index 74ad36b8db..3d512dc21a 100644 --- a/node/pkg/watchers/ibc/watcher.go +++ b/node/pkg/watchers/ibc/watcher.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "math" "net/http" "net/url" "strconv" @@ -439,7 +440,7 @@ func (w *Watcher) handleObservationRequests(ctx context.Context, ce *chainEntry) case <-ctx.Done(): return nil case r := <-ce.obsvReqC: - if vaa.ChainID(r.ChainId) != ce.chainID { + if r.ChainId > math.MaxUint16 || vaa.ChainID(r.ChainId) != ce.chainID { panic("invalid chain ID") } @@ -548,7 +549,7 @@ func parseIbcReceivePublishEvent(logger *zap.Logger, desiredContract string, eve if err != nil { return evt, err } - evt.Msg.EmitterChain = vaa.ChainID(unumber) + evt.Msg.EmitterChain = vaa.ChainID(unumber) // #nosec G115 -- Already range checked by `GetAsUint` str, err = attributes.GetAsString("message.sender") if err != nil { @@ -563,7 +564,7 @@ func parseIbcReceivePublishEvent(logger *zap.Logger, desiredContract string, eve if err != nil { return evt, err } - evt.Msg.Nonce = uint32(unumber) + evt.Msg.Nonce = uint32(unumber) // #nosec G115 -- This conversion is safe based on GetAsUint usage above unumber, err = attributes.GetAsUint("message.sequence", 64) if err != nil { diff --git a/node/pkg/watchers/near/finalizer.go b/node/pkg/watchers/near/finalizer.go index 54aa77cb21..9a0297d343 100644 --- a/node/pkg/watchers/near/finalizer.go +++ b/node/pkg/watchers/near/finalizer.go @@ -72,7 +72,8 @@ func (f Finalizer) isFinalized(logger *zap.Logger, ctx context.Context, queriedB startingBlockHeight := queriedBlock.Header.Height for i := 0; i < nearBlockchainMaxGaps; i++ { - blockHeightToQuery := startingBlockHeight + uint64(2+i) // we start at height+2 because NEAR consensus takes at least two blocks to reach finality. + // we start at height+2 because NEAR consensus takes at least two blocks to reach finality. + blockHeightToQuery := startingBlockHeight + uint64(2+i) // #nosec G115 -- nearBlockchainMaxGaps is 5 block, err := f.nearAPI.GetBlockByHeight(ctx, blockHeightToQuery) if err != nil { break diff --git a/node/pkg/watchers/near/tx_processing.go b/node/pkg/watchers/near/tx_processing.go index afcf17c79e..46ae003e2c 100644 --- a/node/pkg/watchers/near/tx_processing.go +++ b/node/pkg/watchers/near/tx_processing.go @@ -149,16 +149,16 @@ func (e *Watcher) processWormholeLog(logger *zap.Logger, _ context.Context, job return errors.New("Wormhole publish event malformed") } - successValueInt, err := successValueToInt(successValue) + successValueUint64, err := successValueToUint64(successValue) // SECURITY defense-in-depth: check that outcome.status.SuccessValue should equal to the base64 encoded sequence number - if err != nil || successValueInt == 0 || uint64(successValueInt) != pubEvent.Seq { + if err != nil || successValueUint64 == 0 || successValueUint64 != pubEvent.Seq { logger.Error( "SuccessValue does not match sequence number", zap.String("error_type", "malformed_wormhole_event"), zap.String("log_msg_type", "tx_processing_error"), zap.String("SuccessValue", successValue), - zap.Int("int(SuccessValue)", successValueInt), + zap.Uint64("int(SuccessValue)", successValueUint64), zap.Uint64("log.seq", pubEvent.Seq), ) return errors.New("Wormhole publish event.seq does not match SuccessValue") @@ -236,7 +236,7 @@ func (e *Watcher) processWormholeLog(logger *zap.Logger, _ context.Context, job observation := &common.MessagePublication{ TxHash: txHashEthFormat, - Timestamp: time.Unix(int64(ts), 0), + Timestamp: time.Unix(int64(ts), 0), // #nosec G115 -- This conversion is safe indefinitely Nonce: pubEvent.Nonce, Sequence: pubEvent.Seq, EmitterChain: vaa.ChainIDNear, @@ -269,16 +269,16 @@ func (e *Watcher) processWormholeLog(logger *zap.Logger, _ context.Context, job } // TODO test this code -func successValueToInt(successValue string) (int, error) { +func successValueToUint64(successValue string) (uint64, error) { successValueBytes, err := base64.StdEncoding.DecodeString(successValue) if err != nil { return 0, err } - successValueInt, err := strconv.Atoi(string(successValueBytes)) + successValueUint64, err := strconv.ParseUint(string(successValueBytes), 10, 64) if err != nil { return 0, err } - return successValueInt, nil + return successValueUint64, nil } func isWormholePublishEvent(logger *zap.Logger, eventJsonStr string) bool { diff --git a/node/pkg/watchers/near/watcher.go b/node/pkg/watchers/near/watcher.go index 3da2f52662..5603e7fd2a 100644 --- a/node/pkg/watchers/near/watcher.go +++ b/node/pkg/watchers/near/watcher.go @@ -12,6 +12,7 @@ import ( "github.com/certusone/wormhole/node/pkg/readiness" "github.com/certusone/wormhole/node/pkg/supervisor" "github.com/certusone/wormhole/node/pkg/watchers/near/nearapi" + "github.com/ethereum/go-ethereum/common/math" "github.com/mr-tron/base58" "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" @@ -149,6 +150,11 @@ func (e *Watcher) runBlockPoll(ctx context.Context) error { logger.Warn("NEAR poll error", zap.String("log_msg_type", "block_poll_error"), zap.String("error", err.Error())) } + if highestFinalBlockHeightObserved > math.MaxInt64 { + logger.Error("failed to start NEAR block poll", zap.String("error_type", "startup_fail"), zap.String("log_msg_type", "startup_error")) + return fmt.Errorf("the latest finalised NEAR block heigh is not a valid int64: %d", highestFinalBlockHeightObserved) + } + p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDNear, &gossipv1.Heartbeat_Network{ Height: int64(highestFinalBlockHeightObserved), ContractAddress: e.wormholeAccount, @@ -194,7 +200,7 @@ func (e *Watcher) runObsvReqProcessor(ctx context.Context) error { case <-ctx.Done(): return ctx.Err() case r := <-e.obsvReqC: - if vaa.ChainID(r.ChainId) != vaa.ChainIDNear { + if r.ChainId > math.MaxUint16 || vaa.ChainID(r.ChainId) != vaa.ChainIDNear { panic("invalid chain ID") } diff --git a/node/pkg/watchers/near/watcher_test.go b/node/pkg/watchers/near/watcher_test.go index 0389c96251..7a9c6b0de5 100644 --- a/node/pkg/watchers/near/watcher_test.go +++ b/node/pkg/watchers/near/watcher_test.go @@ -608,7 +608,7 @@ func TestSuccessValueToInt(t *testing.T) { for _, tc := range testsPositive { t.Run(tc.input, func(t *testing.T) { - i, err := successValueToInt(tc.input) + i, err := successValueToUint64(tc.input) assert.Equal(t, tc.output, i) assert.NoError(t, err) }) @@ -616,7 +616,7 @@ func TestSuccessValueToInt(t *testing.T) { for _, tc := range testsNegative { t.Run(tc.input, func(t *testing.T) { - i, err := successValueToInt(tc.input) + i, err := successValueToUint64(tc.input) assert.Equal(t, tc.output, i) assert.NotNil(t, err) }) diff --git a/node/pkg/watchers/solana/ccq.go b/node/pkg/watchers/solana/ccq.go index f951df884c..b4ec8cb2db 100644 --- a/node/pkg/watchers/solana/ccq.go +++ b/node/pkg/watchers/solana/ccq.go @@ -243,7 +243,7 @@ func (w *SolanaWatcher) ccqBaseHandleSolanaAccountQueryRequest( w.ccqLogger.Info(fmt.Sprintf("account read for %s query succeeded", tag), zap.String("requestId", requestId), zap.Uint64("slotNumber", info.Context.Slot), - zap.Uint64("blockTime", uint64(*block.BlockTime)), + zap.Uint64("blockTime", uint64(*block.BlockTime)), // #nosec G115 -- This conversion is safe indefinitely zap.String("blockHash", hex.EncodeToString(block.Blockhash[:])), zap.Uint64("blockHeight", *block.BlockHeight), zap.Int("numFastRetries", numFastRetries), @@ -291,7 +291,7 @@ func (w *SolanaWatcher) ccqCheckForMinSlotContext( } // Estimate how far in the future the requested slot is, using our estimated slot time. - futureSlotEstimate := time.Duration(req.MinContextSlot-currentSlot) * CCQ_ESTIMATED_SLOT_TIME + futureSlotEstimate := time.Duration(req.MinContextSlot-currentSlot) * CCQ_ESTIMATED_SLOT_TIME // #nosec G115 -- This conversion is safe indefinitely // If the requested slot is definitively more than the retry interval, use the regular retry mechanism. if futureSlotEstimate > query.RetryInterval*2 { diff --git a/node/pkg/watchers/solana/client.go b/node/pkg/watchers/solana/client.go index 4848fe2912..e235af1ba8 100644 --- a/node/pkg/watchers/solana/client.go +++ b/node/pkg/watchers/solana/client.go @@ -435,7 +435,7 @@ func (s *SolanaWatcher) Run(ctx context.Context) error { currentSolanaHeight.WithLabelValues(s.networkName, string(s.commitment)).Set(float64(slot)) readiness.SetReady(s.readinessSync) p2p.DefaultRegistry.SetNetworkStats(s.chainID, &gossipv1.Heartbeat_Network{ - Height: int64(slot), + Height: int64(slot), // #nosec G115 -- This conversion is safe indefinitely ContractAddress: contractAddr, }) @@ -646,7 +646,7 @@ func (s *SolanaWatcher) processTransaction(ctx context.Context, logger *zap.Logg var programIndex uint16 for n, key := range tx.Message.AccountKeys { if key.Equals(s.contract) { - programIndex = uint16(n) + programIndex = uint16(n) // #nosec G115 -- The solana runtime can only support 64 accounts per transaction max } } if programIndex == 0 { diff --git a/node/pkg/watchers/sui/watcher.go b/node/pkg/watchers/sui/watcher.go index fae0635d52..519baf0e97 100644 --- a/node/pkg/watchers/sui/watcher.go +++ b/node/pkg/watchers/sui/watcher.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "math" "net/http" "strconv" "strings" @@ -387,7 +388,7 @@ func (e *Watcher) Run(ctx context.Context) error { logger.Error("sui_fetch_obvs_req context done") return ctx.Err() case r := <-e.obsvReqC: - if vaa.ChainID(r.ChainId) != vaa.ChainIDSui { + if r.ChainId > math.MaxUint16 || vaa.ChainID(r.ChainId) != vaa.ChainIDSui { panic("invalid chain ID") } diff --git a/scripts/lint.sh b/scripts/lint.sh index ee329b01b7..2d15745893 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -89,7 +89,7 @@ while getopts 'cwdlgh' opt; do SELF_ARGS_WITHOUT_DOCKER+="-l " ;; g) - GOLANGCI_LINT_ARGS+="--out-format=github-actions " + GOLANGCI_LINT_ARGS+="" GITHUB_ACTION="true" SELF_ARGS_WITHOUT_DOCKER+="-g " ;;