cmd/multinode: handle JSON input with BOM to add command

Decode the JSON input string to its corresponding unicode
decoding if the special byte order mark is present.

Fixes https://github.com/storj/storj/issues/4950

Change-Id: If91bac22590c89b35c58bf54f6d3bdb8a67d7a4f
This commit is contained in:
Clement Sam 2022-08-22 22:54:16 +00:00
parent d6054214c3
commit ea0124a183
2 changed files with 90 additions and 2 deletions

View File

@ -7,13 +7,15 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"os"
"path/filepath"
"github.com/spf13/cobra"
"github.com/zeebo/errs"
"go.uber.org/zap"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
"storj.io/common/fpath"
"storj.io/common/peertls/tlsopts"
@ -210,7 +212,7 @@ func cmdAdd(cmd *cobra.Command, args []string) (err error) {
var nodesJSONData []byte
if path == "-" {
stdin := cmd.InOrStdin()
data, err := ioutil.ReadAll(stdin)
data, err := io.ReadAll(stdin)
if err != nil {
return err
}
@ -243,8 +245,22 @@ func cmdAdd(cmd *cobra.Command, args []string) (err error) {
return nil
}
// decodeUTF16or8 decodes the b as UTF-16 if the special byte order mark is present.
func decodeUTF16or8(b []byte) ([]byte, error) {
r := bytes.NewReader(b)
// fallback to r if no BOM sequence is located in the source text.
t := unicode.BOMOverride(transform.Nop)
return io.ReadAll(transform.NewReader(r, t))
}
func unmarshalJSONNodes(nodesJSONData []byte) ([]nodes.Node, error) {
var nodesInfo []nodes.Node
var err error
nodesJSONData, err = decodeUTF16or8(nodesJSONData)
if err != nil {
return nil, err
}
nodesJSONData = bytes.TrimLeft(nodesJSONData, " \t\r\n")
switch {

View File

@ -8,6 +8,7 @@ import (
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/text/encoding/unicode"
"storj.io/common/storj"
"storj.io/storj/multinode/nodes"
@ -100,4 +101,75 @@ func Test_unmarshalJSONNodes(t *testing.T) {
require.Equal(t, "invalid secret", err.Error())
require.Nil(t, got)
})
t.Run("JSON with UTF8 byte order mark sequence", func(t *testing.T) {
nodesJSONData := "\xef\xbb\xbf{\"name\": \"Storagenode 1\",\"id\":\"1MJ7R1cqGrFnELPY3YKd62TBJ6vE8x9yPKPwUFHUx6G8oypezR\",\"publicAddress\": \"awn7k09ts6mxbgau.myfritz.net:13010\",\"apiSecret\": \"b_yeI0OBKBusBVN4_dHxpxlwdTyoFPwtEuHv9ACl9jI=\"}"
got, err := unmarshalJSONNodes([]byte(nodesJSONData))
expectedNodeInfo := []nodes.Node{
{
ID: nodeID,
PublicAddress: "awn7k09ts6mxbgau.myfritz.net:13010",
APISecret: apiSecret,
Name: "Storagenode 1",
},
}
require.NoError(t, err)
require.Equal(t, expectedNodeInfo, got)
})
t.Run("UTF-16LE byte order mark sequence", func(t *testing.T) {
nodesJSONData := `{
"name": "Storagenode 1",
"id":"1MJ7R1cqGrFnELPY3YKd62TBJ6vE8x9yPKPwUFHUx6G8oypezR",
"publicAddress": "awn7k09ts6mxbgau.myfritz.net:13010",
"apiSecret": "b_yeI0OBKBusBVN4_dHxpxlwdTyoFPwtEuHv9ACl9jI="
}`
// encode to UTF-16LE
enc := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewEncoder()
utf16LEStr, err := enc.String(nodesJSONData)
require.NoError(t, err)
expectedNodeInfo := []nodes.Node{
{
ID: nodeID,
PublicAddress: "awn7k09ts6mxbgau.myfritz.net:13010",
APISecret: apiSecret,
Name: "Storagenode 1",
},
}
got, err := unmarshalJSONNodes([]byte(utf16LEStr))
require.NoError(t, err)
require.Equal(t, expectedNodeInfo, got)
})
t.Run("UTF-16BE byte order mark sequence", func(t *testing.T) {
nodesJSONData := `{
"name": "Storagenode 1",
"id":"1MJ7R1cqGrFnELPY3YKd62TBJ6vE8x9yPKPwUFHUx6G8oypezR",
"publicAddress": "awn7k09ts6mxbgau.myfritz.net:13010",
"apiSecret": "b_yeI0OBKBusBVN4_dHxpxlwdTyoFPwtEuHv9ACl9jI="
}`
// encode to UTF-16BE
enc := unicode.UTF16(unicode.BigEndian, unicode.UseBOM).NewEncoder()
utf16BEStr, err := enc.String(nodesJSONData)
require.NoError(t, err)
expectedNodeInfo := []nodes.Node{
{
ID: nodeID,
PublicAddress: "awn7k09ts6mxbgau.myfritz.net:13010",
APISecret: apiSecret,
Name: "Storagenode 1",
},
}
got, err := unmarshalJSONNodes([]byte(utf16BEStr))
require.NoError(t, err)
require.Equal(t, expectedNodeInfo, got)
})
}