Skip to content

Commit 078717a

Browse files
committed
fix(vuln): fixed vulnerability in returned error, not escaping single quotes
Signed-off-by: Frederic BIDON <[email protected]>
1 parent 97d4db8 commit 078717a

File tree

2 files changed

+66
-20
lines changed

2 files changed

+66
-20
lines changed

client_response.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"encoding/json"
88
"fmt"
99
"io"
10+
"strings"
1011
)
1112

1213
// A ClientResponse represents a client response.
@@ -50,13 +51,17 @@ func NewAPIError(opName string, payload any, code int) *APIError {
5051
}
5152
}
5253

54+
// sanitizer ensures that single quotes are escaped
55+
var sanitizer = strings.NewReplacer(`\`, `\\`, `'`, `\'`)
56+
5357
func (o *APIError) Error() string {
5458
var resp []byte
5559
if err, ok := o.Response.(error); ok {
56-
resp = []byte("'" + err.Error() + "'")
60+
resp = []byte("'" + sanitizer.Replace(err.Error()) + "'")
5761
} else {
5862
resp, _ = json.Marshal(o.Response)
5963
}
64+
6065
return fmt.Sprintf("%s (status %d): %s", o.OperationName, o.Code, resp)
6166
}
6267

client_response_test.go

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,26 +53,67 @@ func TestResponseReaderFunc(t *testing.T) {
5353
assert.Equal(t, 490, actual.Code)
5454
}
5555

56+
type errResponse struct {
57+
A int `json:"a"`
58+
B string `json:"b"`
59+
}
60+
5661
func TestResponseReaderFuncError(t *testing.T) {
57-
reader := ClientResponseReaderFunc(func(r ClientResponse, _ Consumer) (any, error) {
58-
_, _ = io.ReadAll(r.Body())
59-
return nil, NewAPIError("fake", errors.New("writer closed"), 490)
62+
t.Parallel()
63+
64+
t.Run("with API error as string", func(t *testing.T) {
65+
reader := ClientResponseReaderFunc(func(r ClientResponse, _ Consumer) (any, error) {
66+
_, _ = io.ReadAll(r.Body())
67+
68+
return nil, NewAPIError("fake", errors.New("writer closed"), 490)
69+
})
70+
71+
_, err := reader.ReadResponse(response{}, nil)
72+
require.Error(t, err)
73+
require.ErrorContains(t, err, "'writer closed'")
6074
})
61-
_, err := reader.ReadResponse(response{}, nil)
62-
require.Error(t, err)
63-
require.ErrorContains(t, err, "writer closed")
64-
65-
reader = func(r ClientResponse, _ Consumer) (any, error) {
66-
_, _ = io.ReadAll(r.Body())
67-
err := &fs.PathError{
68-
Op: "write",
69-
Path: "path/to/fake",
70-
Err: fs.ErrClosed,
71-
}
72-
return nil, NewAPIError("fake", err, 200)
73-
}
74-
_, err = reader.ReadResponse(response{}, nil)
75-
require.Error(t, err)
76-
assert.Contains(t, err.Error(), "file already closed")
7775

76+
t.Run("with API error as complex error", func(t *testing.T) {
77+
reader := ClientResponseReaderFunc(func(r ClientResponse, _ Consumer) (any, error) {
78+
_, _ = io.ReadAll(r.Body())
79+
err := &fs.PathError{
80+
Op: "write",
81+
Path: "path/to/fake",
82+
Err: fs.ErrClosed,
83+
}
84+
85+
return nil, NewAPIError("fake", err, 200)
86+
})
87+
88+
_, err := reader.ReadResponse(response{}, nil)
89+
require.Error(t, err)
90+
assert.Contains(t, err.Error(), "file already closed")
91+
})
92+
93+
t.Run("with API error requiring escaping", func(t *testing.T) {
94+
reader := ClientResponseReaderFunc(func(r ClientResponse, _ Consumer) (any, error) {
95+
_, _ = io.ReadAll(r.Body())
96+
return nil, NewAPIError("fake", errors.New(`writer is \"terminated\" and 'closed'`), 490)
97+
})
98+
99+
_, err := reader.ReadResponse(response{}, nil)
100+
require.Error(t, err)
101+
require.ErrorContains(t, err, `'writer is \\"terminated\\" and \'closed\''`)
102+
})
103+
104+
t.Run("with API error as JSON", func(t *testing.T) {
105+
reader := ClientResponseReaderFunc(func(r ClientResponse, _ Consumer) (any, error) {
106+
_, _ = io.ReadAll(r.Body())
107+
obj := &errResponse{ // does not implement error
108+
A: 555,
109+
B: "closed",
110+
}
111+
112+
return nil, NewAPIError("fake", obj, 200)
113+
})
114+
115+
_, err := reader.ReadResponse(response{}, nil)
116+
require.Error(t, err)
117+
assert.Contains(t, err.Error(), `{"a":555,"b":"closed"}`)
118+
})
78119
}

0 commit comments

Comments
 (0)