Browse Source

make gosec linter pass (#3294)

* not related to linter: remove obsolete constants:
 - `Insecure` and `Secure` and type `Security` are not used anywhere

* not related to linter: update example

 - NewInsecure was deleted; change example to NewRemoteDB

* address: Binds to all network interfaces (gosec):

 - bind to localhost instead of 0.0.0.0
 - regenerate test key and cert for this purpose (was valid for ::) and
 otherwise we would see:
 transport: authentication handshake failed: x509: certificate is
 valid for ::, not 127.0.0.1\"

(used https://github.com/google/keytransparency/blob/master/scripts/gen_server_keys.sh
to regenerate certs)

* use sha256 in tests instead of md5; time difference is negligible

* nolint usage of math/rand in test and add comment on its import

 - crypto/rand is slower and we do not need sth more secure in tests

* enable linter in circle-ci

* another nolint math/rand in test

* replace another occurrence of md5

* consistent comment about importing math/rand
pull/3305/head
Ismail Khoffi 6 years ago
committed by Anton Kaliaev
parent
commit
b089587b42
11 changed files with 64 additions and 72 deletions
  1. +0
    -1
      .golangci.yml
  2. +3
    -1
      crypto/merkle/proof_key_path_test.go
  3. +1
    -1
      libs/db/remotedb/doc.go
  4. +0
    -8
      libs/db/remotedb/grpcdb/client.go
  5. +1
    -1
      libs/db/remotedb/remotedb_test.go
  6. +17
    -23
      libs/db/remotedb/test.crt
  7. +25
    -25
      libs/db/remotedb/test.key
  8. +2
    -2
      mempool/mempool_test.go
  9. +8
    -5
      tools/tm-bench/transacter.go
  10. +3
    -3
      tools/tm-bench/transacter_test.go
  11. +4
    -2
      types/block_test.go

+ 0
- 1
.golangci.yml View File

@ -19,7 +19,6 @@ linters:
- lll - lll
- gochecknoglobals - gochecknoglobals
- gocritic - gocritic
- gosec
- gochecknoinits - gochecknoinits
- scopelint - scopelint
- stylecheck - stylecheck


+ 3
- 1
crypto/merkle/proof_key_path_test.go View File

@ -1,6 +1,8 @@
package merkle package merkle
import ( import (
// it is ok to use math/rand here: we do not need a cryptographically secure random
// number generator here and we can run the tests a bit faster
"math/rand" "math/rand"
"testing" "testing"
@ -24,7 +26,7 @@ func TestKeyPath(t *testing.T) {
keys[i][j] = alphanum[rand.Intn(len(alphanum))] keys[i][j] = alphanum[rand.Intn(len(alphanum))]
} }
case KeyEncodingHex: case KeyEncodingHex:
rand.Read(keys[i])
rand.Read(keys[i]) //nolint: gosec
default: default:
panic("Unexpected encoding") panic("Unexpected encoding")
} }


+ 1
- 1
libs/db/remotedb/doc.go View File

@ -11,7 +11,7 @@ remotedb's RemoteDB implements db.DB so can be used normally
like other databases. One just has to explicitly connect to the like other databases. One just has to explicitly connect to the
remote database with a client setup such as: remote database with a client setup such as:
client, err := remotedb.NewInsecure(addr)
client, err := remotedb.NewRemoteDB(addr, cert)
// Make sure to invoke InitRemote! // Make sure to invoke InitRemote!
if err := client.InitRemote(&remotedb.Init{Name: "test-remote-db", Type: "leveldb"}); err != nil { if err := client.InitRemote(&remotedb.Init{Name: "test-remote-db", Type: "leveldb"}); err != nil {
log.Fatalf("Failed to initialize the remote db") log.Fatalf("Failed to initialize the remote db")


+ 0
- 8
libs/db/remotedb/grpcdb/client.go View File

@ -7,14 +7,6 @@ import (
protodb "github.com/tendermint/tendermint/libs/db/remotedb/proto" protodb "github.com/tendermint/tendermint/libs/db/remotedb/proto"
) )
// Security defines how the client will talk to the gRPC server.
type Security uint
const (
Insecure Security = iota
Secure
)
// NewClient creates a gRPC client connected to the bound gRPC server at serverAddr. // NewClient creates a gRPC client connected to the bound gRPC server at serverAddr.
// Use kind to set the level of security to either Secure or Insecure. // Use kind to set the level of security to either Secure or Insecure.
func NewClient(serverAddr, serverCert string) (protodb.DBClient, error) { func NewClient(serverAddr, serverCert string) (protodb.DBClient, error) {


+ 1
- 1
libs/db/remotedb/remotedb_test.go View File

@ -14,7 +14,7 @@ import (
func TestRemoteDB(t *testing.T) { func TestRemoteDB(t *testing.T) {
cert := "test.crt" cert := "test.crt"
key := "test.key" key := "test.key"
ln, err := net.Listen("tcp", "0.0.0.0:0")
ln, err := net.Listen("tcp", "localhost:0")
require.Nil(t, err, "expecting a port to have been assigned on which we can listen") require.Nil(t, err, "expecting a port to have been assigned on which we can listen")
srv, err := grpcdb.NewServer(cert, key) srv, err := grpcdb.NewServer(cert, key)
require.Nil(t, err) require.Nil(t, err)


+ 17
- 23
libs/db/remotedb/test.crt View File

@ -1,25 +1,19 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
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
M5OXGXdw
-----END CERTIFICATE----- -----END CERTIFICATE-----

+ 25
- 25
libs/db/remotedb/test.key View File

@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----

+ 2
- 2
mempool/mempool_test.go View File

@ -1,8 +1,8 @@
package mempool package mempool
import ( import (
"crypto/md5"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -451,7 +451,7 @@ func TestMempoolMaxMsgSize(t *testing.T) {
} }
func checksumIt(data []byte) string { func checksumIt(data []byte) string {
h := md5.New()
h := sha256.New()
h.Write(data) h.Write(data)
return fmt.Sprintf("%x", h.Sum(nil)) return fmt.Sprintf("%x", h.Sum(nil))
} }


+ 8
- 5
tools/tm-bench/transacter.go View File

@ -1,11 +1,14 @@
package main package main
import ( import (
"crypto/md5"
"crypto/sha256"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
// it is ok to use math/rand here: we do not need a cryptographically secure random
// number generator here and we can run the tests a bit faster
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
@ -154,12 +157,12 @@ func (t *transacter) sendLoop(connIndex int) {
}() }()
// hash of the host name is a part of each tx // hash of the host name is a part of each tx
var hostnameHash [md5.Size]byte
var hostnameHash [sha256.Size]byte
hostname, err := os.Hostname() hostname, err := os.Hostname()
if err != nil { if err != nil {
hostname = "127.0.0.1" hostname = "127.0.0.1"
} }
hostnameHash = md5.Sum([]byte(hostname))
hostnameHash = sha256.Sum256([]byte(hostname))
// each transaction embeds connection index, tx number and hash of the hostname // each transaction embeds connection index, tx number and hash of the hostname
// we update the tx number between successive txs // we update the tx number between successive txs
tx := generateTx(connIndex, txNumber, t.Size, hostnameHash) tx := generateTx(connIndex, txNumber, t.Size, hostnameHash)
@ -257,7 +260,7 @@ func connect(host string) (*websocket.Conn, *http.Response, error) {
return websocket.DefaultDialer.Dial(u.String(), nil) return websocket.DefaultDialer.Dial(u.String(), nil)
} }
func generateTx(connIndex int, txNumber int, txSize int, hostnameHash [md5.Size]byte) []byte {
func generateTx(connIndex int, txNumber int, txSize int, hostnameHash [sha256.Size]byte) []byte {
tx := make([]byte, txSize) tx := make([]byte, txSize)
binary.PutUvarint(tx[:8], uint64(connIndex)) binary.PutUvarint(tx[:8], uint64(connIndex))
@ -266,7 +269,7 @@ func generateTx(connIndex int, txNumber int, txSize int, hostnameHash [md5.Size]
binary.PutUvarint(tx[32:40], uint64(time.Now().Unix())) binary.PutUvarint(tx[32:40], uint64(time.Now().Unix()))
// 40-* random data // 40-* random data
if _, err := rand.Read(tx[40:]); err != nil {
if _, err := rand.Read(tx[40:]); err != nil { //nolint: gosec
panic(errors.Wrap(err, "failed to read random bytes")) panic(errors.Wrap(err, "failed to read random bytes"))
} }


+ 3
- 3
tools/tm-bench/transacter_test.go View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"crypto/md5"
"crypto/sha256"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -28,7 +28,7 @@ func TestGenerateTxUpdateTxConsistentency(t *testing.T) {
} }
for tcIndex, tc := range cases { for tcIndex, tc := range cases {
hostnameHash := md5.Sum([]byte(tc.hostname))
hostnameHash := sha256.Sum256([]byte(tc.hostname))
// Tx generated from update tx. This is defined outside of the loop, since we have // Tx generated from update tx. This is defined outside of the loop, since we have
// to a have something initially to update // to a have something initially to update
updatedTx := generateTx(tc.connIndex, tc.startingTxNumber, tc.txSize, hostnameHash) updatedTx := generateTx(tc.connIndex, tc.startingTxNumber, tc.txSize, hostnameHash)
@ -69,7 +69,7 @@ func BenchmarkIterationOfSendLoop(b *testing.B) {
// something too far away to matter // something too far away to matter
endTime := now.Add(time.Hour) endTime := now.Add(time.Hour)
txNumber := 0 txNumber := 0
hostnameHash := md5.Sum([]byte{0})
hostnameHash := sha256.Sum256([]byte{0})
tx := generateTx(connIndex, txNumber, txSize, hostnameHash) tx := generateTx(connIndex, txNumber, txSize, hostnameHash)
txHex := make([]byte, len(tx)*2) txHex := make([]byte, len(tx)*2)
hex.Encode(txHex, tx) hex.Encode(txHex, tx)


+ 4
- 2
types/block_test.go View File

@ -1,6 +1,8 @@
package types package types
import ( import (
// it is ok to use math/rand here: we do not need a cryptographically secure random
// number generator here and we can run the tests a bit faster
"crypto/rand" "crypto/rand"
"math" "math"
"os" "os"
@ -162,8 +164,8 @@ func TestBlockString(t *testing.T) {
func makeBlockIDRandom() BlockID { func makeBlockIDRandom() BlockID {
blockHash := make([]byte, tmhash.Size) blockHash := make([]byte, tmhash.Size)
partSetHash := make([]byte, tmhash.Size) partSetHash := make([]byte, tmhash.Size)
rand.Read(blockHash)
rand.Read(partSetHash)
rand.Read(blockHash) //nolint: gosec
rand.Read(partSetHash) //nolint: gosec
blockPartsHeader := PartSetHeader{123, partSetHash} blockPartsHeader := PartSetHeader{123, partSetHash}
return BlockID{blockHash, blockPartsHeader} return BlockID{blockHash, blockPartsHeader}
} }


Loading…
Cancel
Save