diff --git a/x/wasm/client/cli/tx.go b/x/wasm/client/cli/tx.go index 9d025e1715..a51890d244 100644 --- a/x/wasm/client/cli/tx.go +++ b/x/wasm/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "bufio" + "fmt" "io/ioutil" "strconv" @@ -15,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + wasmUtils "github.com/cosmwasm/wasmd/x/wasm/client/utils" "github.com/cosmwasm/wasmd/x/wasm/internal/types" ) @@ -23,6 +25,9 @@ const ( flagAmount = "amount" ) +// limit max bytes read to prevent gzip bombs +const maxSize = 400 * 1024 + // GetTxCmd returns the transaction commands for this module func GetTxCmd(cdc *codec.Codec) *cobra.Command { txCmd := &cobra.Command{ @@ -57,6 +62,23 @@ func StoreCodeCmd(cdc *codec.Codec) *cobra.Command { return err } + // limit the input size + if len(wasm) > maxSize { + return fmt.Errorf("input size exceeds the max size allowed (allowed:%d, actual: %d)", + maxSize, len(wasm)) + } + + // gzip the wasm file + if wasmUtils.IsWasm(wasm) { + wasm, err = wasmUtils.GzipIt(wasm) + + if err != nil { + return err + } + } else if !wasmUtils.IsGzip(wasm) { + return fmt.Errorf("invalid input file. Use wasm binary or gzip") + } + // build and sign the transaction, then broadcast to Tendermint msg := types.MsgStoreCode{ Sender: cliCtx.GetFromAddress(), diff --git a/x/wasm/client/utils/utils.go b/x/wasm/client/utils/utils.go new file mode 100644 index 0000000000..bbe9adc7f9 --- /dev/null +++ b/x/wasm/client/utils/utils.go @@ -0,0 +1,38 @@ +package utils + +import ( + "bytes" + "compress/gzip" +) + +var ( + gzipIdent = []byte("\x1F\x8B\x08") + wasmIdent = []byte("\x00\x61\x73\x6D") +) + +// IsGzip returns checks if the file contents are gzip compressed +func IsGzip(input []byte) bool { + return bytes.Equal(input[:3], gzipIdent) +} + +// IsWasm checks if the file contents are of wasm binary +func IsWasm(input []byte) bool { + return bytes.Equal(input[:4], wasmIdent) +} + +// GzipIt compresses the input ([]byte) +func GzipIt(input []byte) ([]byte, error) { + // Create gzip writer. + var b bytes.Buffer + w := gzip.NewWriter(&b) + _, err := w.Write(input) + if err != nil { + return nil, err + } + err = w.Close() // You must close this first to flush the bytes to the buffer. + if err != nil { + return nil, err + } + + return b.Bytes(), nil +} diff --git a/x/wasm/client/utils/utils_test.go b/x/wasm/client/utils/utils_test.go new file mode 100644 index 0000000000..f389043840 --- /dev/null +++ b/x/wasm/client/utils/utils_test.go @@ -0,0 +1,64 @@ +package utils + +import ( + "github.com/stretchr/testify/require" + "io/ioutil" + "testing" +) + +func GetTestData() ([]byte, []byte, []byte, error){ + wasmCode, err := ioutil.ReadFile("../../internal/keeper/testdata/contract.wasm") + + if err != nil { + return nil, nil, nil, err + } + + gzipData, err := GzipIt(wasmCode) + if err != nil { + return nil, nil, nil, err + } + + someRandomStr := []byte("hello world") + + return wasmCode, someRandomStr, gzipData, nil +} + +func TestIsWasm (t *testing.T) { + wasmCode, someRandomStr, gzipData, err := GetTestData() + require.NoError(t, err) + + t.Log("should return false for some random string data") + require.False(t, IsWasm(someRandomStr)) + t.Log("should return false for gzip data") + require.False(t, IsWasm(gzipData)) + t.Log("should return true for exact wasm") + require.True(t, IsWasm(wasmCode)) +} + +func TestIsGzip (t *testing.T) { + wasmCode, someRandomStr, gzipData, err := GetTestData() + require.NoError(t, err) + + require.False(t, IsGzip(wasmCode)) + require.False(t, IsGzip(someRandomStr)) + require.True(t, IsGzip(gzipData)) +} + +func TestGzipIt (t *testing.T) { + wasmCode, someRandomStr, _, err := GetTestData() + originalGzipData := []byte{31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 202, 72, 205, 201, 201, 87, 40, 207, 47, 202, 73, 1, + 4, 0, 0, 255, 255, 133, 17, 74, 13, 11, 0, 0, 0} + + require.NoError(t, err) + + t.Log("gzip wasm with no error") + _, err = GzipIt(wasmCode) + require.NoError(t, err) + + t.Log("gzip of a string should return exact gzip data") + strToGzip, err := GzipIt(someRandomStr) + + require.True(t, IsGzip(strToGzip)) + require.NoError(t, err) + require.Equal(t, originalGzipData, strToGzip) +} \ No newline at end of file