Skip to content

Commit

Permalink
cmd/multinode: handle JSON input with BOM to add command
Browse files Browse the repository at this point in the history
Decode the JSON input string to its corresponding unicode
decoding if the special byte order mark is present.

Fixes #4950

Change-Id: If91bac22590c89b35c58bf54f6d3bdb8a67d7a4f
  • Loading branch information
profclems committed Aug 25, 2022
1 parent d605421 commit ea0124a
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 2 deletions.
20 changes: 18 additions & 2 deletions cmd/multinode/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down
72 changes: 72 additions & 0 deletions cmd/multinode/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
})
}

1 comment on commit ea0124a

@storjrobot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit has been mentioned on Storj Community Forum (official). There might be relevant details there:

https://forum.storj.io/t/release-preparation-v1-63/19558/1

Please sign in to comment.