Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap errors from external libraries to prevent leaking sensitive information #185

Merged
merged 2 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions encoding/ascii.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package encoding

import (
"fmt"

"github.com/moov-io/iso8583/utils"
)

var ASCII Encoder = &asciiEncoder{}
Expand All @@ -12,7 +14,7 @@ func (e asciiEncoder) Encode(data []byte) ([]byte, error) {
var out []byte
for _, r := range data {
if r > 127 {
return nil, fmt.Errorf("invalid ASCII char: '%s'", string(r))
return nil, utils.NewSafeError(fmt.Errorf("invalid ASCII char: '%s'", string(r)), "failed to perform ASCII encoding")
}
out = append(out, r)
}
Expand All @@ -29,7 +31,7 @@ func (e asciiEncoder) Decode(data []byte, length int) ([]byte, int, error) {
var out []byte
for _, r := range data {
if r > 127 {
return nil, 0, fmt.Errorf("invalid ASCII char: '%s'", string(r))
return nil, 0, utils.NewSafeError(fmt.Errorf("invalid ASCII char: '%s'", string(r)), "failed to perform ASCII decoding")
}
out = append(out, r)
}
Expand Down
2 changes: 2 additions & 0 deletions encoding/ascii_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestASCII(t *testing.T) {

_, _, err = enc.Decode([]byte("hello, 世界!"), 10)
require.Error(t, err)
require.EqualError(t, err, "failed to perform ASCII decoding")

_, _, err = enc.Decode([]byte("hello"), 6)
require.Error(t, err)
Expand All @@ -36,5 +37,6 @@ func TestASCII(t *testing.T) {

_, err = enc.Encode([]byte("hello, 世界!"))
require.Error(t, err)
require.EqualError(t, err, "failed to perform ASCII encoding")
})
}
5 changes: 3 additions & 2 deletions encoding/bcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package encoding
import (
"fmt"

"github.com/moov-io/iso8583/utils"
"github.com/yerden/go-util/bcd"
)

Expand All @@ -19,7 +20,7 @@ func (e *bcdEncoder) Encode(src []byte) ([]byte, error) {
dst := make([]byte, bcd.EncodedLen(len(src)))
n, err := enc.Encode(dst, src)
if err != nil {
return nil, err
return nil, utils.NewSafeError(err, "failed to perform BCD encoding")
}

return dst[:n], nil
Expand All @@ -43,7 +44,7 @@ func (e *bcdEncoder) Decode(src []byte, length int) ([]byte, int, error) {
dst := make([]byte, decodedLen)
_, err := dec.Decode(dst, src[:read])
if err != nil {
return nil, 0, err
return nil, 0, utils.NewSafeError(err, "failed to perform BCD decoding")
}

// becase BCD is right aligned, we skip first bytes and
Expand Down
13 changes: 11 additions & 2 deletions encoding/bcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"github.com/yerden/go-util/bcd"
)

func TestBCD(t *testing.T) {
Expand Down Expand Up @@ -45,18 +46,26 @@ func TestBCD(t *testing.T) {
_, _, err = BCD.Decode(nil, 6)
require.Error(t, err)
require.EqualError(t, err, "not enough data to decode. expected len 3, got 0")

_, _, err = BCD.Decode([]byte{0xAB, 0xCD}, 4)
require.Error(t, err)
require.EqualError(t, err, "failed to perform BCD decoding")
require.ErrorIs(t, err, bcd.ErrBadBCD)
})

t.Run("Encode", func(t *testing.T) {
res, err := BCD.Encode([]byte("0110"))

require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x10}, res)

// right justified by default
res, err = BCD.Encode([]byte("123"))

require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x23}, res)

_, err = BCD.Encode([]byte("abc"))
require.Error(t, err)
require.EqualError(t, err, "failed to perform BCD encoding")
require.ErrorIs(t, err, bcd.ErrBadInput)
})
}
7 changes: 4 additions & 3 deletions encoding/bertlv.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package encoding

import (
"bytes"
"fmt"
"math/bits"

"github.com/moov-io/iso8583/utils"
)

// BER-TLV Tag encoder
Expand Down Expand Up @@ -34,7 +35,7 @@ func (berTLVEncoderTag) Decode(data []byte, length int) ([]byte, int, error) {

firstByte, err := r.ReadByte()
if err != nil {
return nil, 0, err
return nil, 0, utils.NewSafeError(err, "failed to read byte")
}
tagLenBytes := 1

Expand All @@ -46,7 +47,7 @@ func (berTLVEncoderTag) Decode(data []byte, length int) ([]byte, int, error) {
for shouldReadSubsequentByte {
b, err := r.ReadByte()
if err != nil {
return nil, tagLenBytes, fmt.Errorf("failed to decode TLV tag: %w", err)
return nil, tagLenBytes, utils.NewSafeError(err, "failed to decode TLV tag")
}
tagLenBytes++
// We read subsequent bytes to extract the tag by checking if
Expand Down
19 changes: 17 additions & 2 deletions encoding/bertlv_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package encoding

import (
"io"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -41,13 +42,27 @@ func TestBerTLVTag(t *testing.T) {
}

func TestBerTLVTag_DecodeOnInvalidInput(t *testing.T) {
t.Run("when bytes are nil", func(t *testing.T) {
_, _, err := BerTLVTag.Decode(nil, 0)
require.EqualError(t, err, "failed to read byte")
require.ErrorIs(t, err, io.EOF)
})

t.Run("when bytes are empty", func(t *testing.T) {
_, _, err := BerTLVTag.Decode([]byte{}, 0)
require.EqualError(t, err, "failed to read byte")
require.ErrorIs(t, err, io.EOF)
})

t.Run("when bits 5-1 of first byte set but 2nd byte does not exist", func(t *testing.T) {
_, _, err := BerTLVTag.Decode([]byte{0x5F}, 0)
require.EqualError(t, err, "failed to decode TLV tag: EOF")
require.EqualError(t, err, "failed to decode TLV tag")
require.ErrorIs(t, err, io.EOF)
})

t.Run("when MSB of 2nd byte set but 3nd byte does not exist", func(t *testing.T) {
_, _, err := BerTLVTag.Decode([]byte{0x5F, 0xA8}, 0)
require.EqualError(t, err, "failed to decode TLV tag: EOF")
require.EqualError(t, err, "failed to decode TLV tag")
require.ErrorIs(t, err, io.EOF)
})
}
12 changes: 7 additions & 5 deletions encoding/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package encoding

import (
"encoding/hex"
"fmt"
"errors"
"strings"

"github.com/moov-io/iso8583/utils"
)

// HEX to ASCII encoder
Expand Down Expand Up @@ -31,14 +33,14 @@ func (e hexToASCIIEncoder) Decode(data []byte, length int) ([]byte, int, error)
// to read 8 HEX digits we have to read 16 ASCII chars (bytes)
read := hex.EncodedLen(length)
if read > len(data) {
return nil, 0, fmt.Errorf("not enough data to read")
return nil, 0, errors.New("not enough data to read")
}

out := make([]byte, length)

_, err := hex.Decode(out, data[:read])
if err != nil {
return nil, 0, err
return nil, 0, utils.NewSafeError(err, "failed to perform hex decoding")
}

return out, read, nil
Expand All @@ -56,7 +58,7 @@ func (e asciiToHexEncoder) Encode(data []byte) ([]byte, error) {

_, err := hex.Decode(out, data)
if err != nil {
return nil, err
return nil, utils.NewSafeError(err, "failed to perform hex decoding")
}

return out, nil
Expand All @@ -68,7 +70,7 @@ func (e asciiToHexEncoder) Encode(data []byte) ([]byte, error) {
// 0x2A} would be converted to []byte("5F2A")
func (e asciiToHexEncoder) Decode(data []byte, length int) ([]byte, int, error) {
if length > len(data) {
return nil, 0, fmt.Errorf("not enough data to read")
return nil, 0, errors.New("not enough data to read")
}

out := make([]byte, hex.EncodedLen(length))
Expand Down
8 changes: 8 additions & 0 deletions encoding/hex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ func TestHexToASCIIEncoder(t *testing.T) {
require.Error(t, err)
require.EqualError(t, err, "not enough data to read")

_, _, err = enc.Decode([]byte("nothex"), 3)
require.Error(t, err)
require.EqualError(t, err, "failed to perform hex decoding")

got, err = enc.Encode([]byte{0xAA, 0xBB, 0xCC})
require.NoError(t, err)
require.Equal(t, []byte("AABBCC"), got)
Expand All @@ -34,4 +38,8 @@ func TestASCIIToHexEncoder(t *testing.T) {
got, err = enc.Encode([]byte("aabbcc"))
require.NoError(t, err)
require.Equal(t, []byte{0xAA, 0xBB, 0xCC}, got)

_, err = enc.Encode([]byte("nothex"))
require.Error(t, err)
require.EqualError(t, err, "failed to perform hex decoding")
}
5 changes: 3 additions & 2 deletions encoding/lbcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package encoding
import (
"fmt"

"github.com/moov-io/iso8583/utils"
"github.com/yerden/go-util/bcd"
)

Expand All @@ -19,7 +20,7 @@ func (e *lBCDEncoder) Encode(src []byte) ([]byte, error) {
dst := make([]byte, bcd.EncodedLen(len(src)))
n, err := enc.Encode(dst, src)
if err != nil {
return nil, err
return nil, utils.NewSafeError(err, "failed to perform BCD encoding")
}

return dst[:n], nil
Expand All @@ -42,7 +43,7 @@ func (e *lBCDEncoder) Decode(src []byte, length int) ([]byte, int, error) {

_, err := dec.Decode(dst, src)
if err != nil {
return nil, 0, err
return nil, 0, utils.NewSafeError(err, "failed to perform BCD decoding")
}

// because it's left aligned, we return data from
Expand Down
12 changes: 11 additions & 1 deletion encoding/lbcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"github.com/yerden/go-util/bcd"
)

func TestLBCD(t *testing.T) {
Expand All @@ -27,12 +28,21 @@ func TestLBCD(t *testing.T) {
_, _, err = LBCD.Decode(nil, 5)
require.Error(t, err)
require.EqualError(t, err, "not enough data to decode. expected len 3, got 0")

_, _, err = LBCD.Decode([]byte{0xAB, 0xCD}, 4)
require.Error(t, err)
require.EqualError(t, err, "failed to perform BCD decoding")
require.ErrorIs(t, err, bcd.ErrBadBCD)
})

t.Run("Encode", func(t *testing.T) {
res, err := LBCD.Encode([]byte("123"))

require.NoError(t, err)
require.Equal(t, []byte{0x12, 0x30}, res)

_, err = LBCD.Encode([]byte("abc"))
require.Error(t, err)
require.EqualError(t, err, "failed to perform BCD encoding")
require.ErrorIs(t, err, bcd.ErrBadInput)
})
}
13 changes: 9 additions & 4 deletions field/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"

"github.com/moov-io/iso8583/encoding"
"github.com/moov-io/iso8583/utils"
)

var _ Field = (*Binary)(nil)
Expand Down Expand Up @@ -118,7 +119,7 @@ func (f *Binary) SetData(data interface{}) error {

bin, ok := data.(*Binary)
if !ok {
return fmt.Errorf("data does not match required *Binary type")
return errors.New("data does not match required *Binary type")
}

f.data = bin
Expand All @@ -137,19 +138,23 @@ func (f *Binary) MarshalJSON() ([]byte, error) {
if err != nil {
return nil, err
}
return json.Marshal(str)
bytes, err := json.Marshal(str)
if err != nil {
return nil, utils.NewSafeError(err, "failed to JSON marshal string to bytes")
}
return bytes, nil
}

func (f *Binary) UnmarshalJSON(b []byte) error {
var v string
err := json.Unmarshal(b, &v)
if err != nil {
return fmt.Errorf("failed to JSON unmarshal bytes to string: %w", err)
return utils.NewSafeError(err, "failed to JSON unmarshal bytes to string")
}

hex, err := encoding.ASCIIHexToBytes.Encode([]byte(v))
if err != nil {
return fmt.Errorf("failed to convert ASCII Hex string to bytes")
return utils.NewSafeError(err, "failed to convert ASCII Hex string to bytes")
}
return f.SetBytes(hex)
}
11 changes: 8 additions & 3 deletions field/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/moov-io/iso8583/padding"
"github.com/moov-io/iso8583/sort"
"github.com/moov-io/iso8583/utils"
)

var _ Field = (*Composite)(nil)
Expand Down Expand Up @@ -304,7 +305,11 @@ func (f *Composite) String() (string, error) {
// MarshalJSON implements the encoding/json.Marshaler interface.
func (f *Composite) MarshalJSON() ([]byte, error) {
jsonData := OrderedMap(f.getSubfields())
return json.Marshal(jsonData)
bytes, err := json.Marshal(jsonData)
if err != nil {
return nil, utils.NewSafeError(err, "failed to JSON marshal map to bytes")
}
return bytes, nil
}

// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
Expand All @@ -314,7 +319,7 @@ func (f *Composite) UnmarshalJSON(b []byte) error {
var data map[string]json.RawMessage
err := json.Unmarshal(b, &data)
if err != nil {
return err
return utils.NewSafeError(err, "failed to JSON unmarshal bytes to map")
}

for tag, rawMsg := range data {
Expand All @@ -328,7 +333,7 @@ func (f *Composite) UnmarshalJSON(b []byte) error {
}

if err := json.Unmarshal(rawMsg, subfield); err != nil {
return fmt.Errorf("failed to unmarshal subfield %v: %w", tag, err)
return utils.NewSafeErrorf(err, "failed to unmarshal subfield %v", tag)
}

f.setSubfields[tag] = struct{}{}
Expand Down
4 changes: 3 additions & 1 deletion field/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package field
import (
"fmt"
"reflect"
"strconv"
"testing"

"github.com/moov-io/iso8583/encoding"
Expand Down Expand Up @@ -382,7 +383,8 @@ func TestCompositePacking(t *testing.T) {
read, err := composite.Unpack([]byte("ABCDEF"))
require.Equal(t, 0, read)
require.Error(t, err)
require.EqualError(t, err, "failed to unpack subfield 3: failed to set bytes: failed to convert into number: strconv.Atoi: parsing \"EF\": invalid syntax")
require.EqualError(t, err, "failed to unpack subfield 3: failed to set bytes: failed to convert into number")
require.ErrorIs(t, err, strconv.ErrSyntax)
})

t.Run("Unpack returns an error on length of data exceeding max length", func(t *testing.T) {
Expand Down
Loading