Skip to content

Commit

Permalink
Merge pull request #1186 from cyli/limit-readall-size
Browse files Browse the repository at this point in the history
Do not ioutil.ReadAll anything without limiting the size.
  • Loading branch information
endophage authored Jul 10, 2017
2 parents c251b85 + 2c18c0e commit 87d6d6c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 3 deletions.
16 changes: 13 additions & 3 deletions storage/httpstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ import (
"github.com/docker/notary/tuf/validation"
)

const (
// MaxErrorResponseSize is the maximum size for an error message - 1KiB
MaxErrorResponseSize int64 = 1 << 10
// MaxKeySize is the maximum size for a stored TUF key - 256KiB
MaxKeySize = 256 << 10
)

// ErrServerUnavailable indicates an error from the server. code allows us to
// populate the http error we received
type ErrServerUnavailable struct {
Expand Down Expand Up @@ -128,7 +135,8 @@ func NewHTTPStore(baseURL, metaPrefix, metaExtension, keyExtension string, round
}

func tryUnmarshalError(resp *http.Response, defaultError error) error {
bodyBytes, err := ioutil.ReadAll(resp.Body)
b := io.LimitReader(resp.Body, MaxErrorResponseSize)
bodyBytes, err := ioutil.ReadAll(b)
if err != nil {
return defaultError
}
Expand Down Expand Up @@ -319,7 +327,8 @@ func (s HTTPStore) GetKey(role data.RoleName) ([]byte, error) {
if err := translateStatusToError(resp, role.String()+" key"); err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
b := io.LimitReader(resp.Body, MaxKeySize)
body, err := ioutil.ReadAll(b)
if err != nil {
return nil, err
}
Expand All @@ -344,7 +353,8 @@ func (s HTTPStore) RotateKey(role data.RoleName) ([]byte, error) {
if err := translateStatusToError(resp, role.String()+" key"); err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
b := io.LimitReader(resp.Body, MaxKeySize)
body, err := ioutil.ReadAll(b)
if err != nil {
return nil, err
}
Expand Down
46 changes: 46 additions & 0 deletions storage/httpstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,31 @@ func TestTranslateErrorsWhenCannotParse400(t *testing.T) {
}
}

// Cut off error reading after a certain size
func TestTranslateErrorsLimitsErrorSize(t *testing.T) {
// if the error message itself is the max error size, then extra JSON surrounding it will put it over
// the top
msg := make([]byte, MaxErrorResponseSize)
for i := range msg {
msg[i] = 'a'
}

serialObj, err := validation.NewSerializableError(validation.ErrBadRoot{Msg: string(msg)})
require.NoError(t, err)
serialization, err := json.Marshal(serialObj)
require.NoError(t, err)
errorBody := bytes.NewBuffer([]byte(fmt.Sprintf(
`{"errors": [{"otherstuff": "what", "detail": %s}]}`,
string(serialization))))
errorResp := http.Response{
StatusCode: http.StatusBadRequest,
Body: ioutil.NopCloser(errorBody),
}

err = translateStatusToError(&errorResp, "")
require.IsType(t, ErrInvalidOperation{}, err)
}

func TestHTTPStoreRemoveAll(t *testing.T) {
// Set up a simple handler and server for our store, just check that a non-error response back is fine
handler := func(w http.ResponseWriter, r *http.Request) {}
Expand Down Expand Up @@ -323,6 +348,27 @@ func TestHTTPStoreGetKey(t *testing.T) {
require.Equal(t, "FAIL", err.Error())
}

func TestHTTPStoreGetRotateKeySizeLimited(t *testing.T) {
tooLarge := make([]byte, MaxKeySize+10)
for i := range tooLarge {
tooLarge[i] = 'a'
}
handler := func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/metadata/snapshot.key", r.URL.Path)
w.Write(tooLarge)
}
server := httptest.NewServer(http.HandlerFunc(handler))
defer server.Close()
store, err := NewHTTPStore(server.URL, "metadata", "json", "key", http.DefaultTransport)
require.NoError(t, err)

for _, downloadFunc := range []func(data.RoleName) ([]byte, error){store.RotateKey, store.GetKey} {
gotten, err := downloadFunc(data.CanonicalSnapshotRole)
require.NoError(t, err)
require.Equal(t, tooLarge[:MaxKeySize], gotten)
}
}

func TestHTTPOffline(t *testing.T) {
s, err := NewHTTPStore("https://localhost/", "", "", "", nil)
require.NoError(t, err)
Expand Down

0 comments on commit 87d6d6c

Please sign in to comment.