Skip to content

Commit

Permalink
Merge pull request #24 from stellar/limit-responses
Browse files Browse the repository at this point in the history
Limit bytes read from stellar.toml and federation responses
  • Loading branch information
bartekn authored Dec 7, 2016
2 parents 91a8df7 + f2d7d1b commit d8b0f52
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 2 deletions.
9 changes: 8 additions & 1 deletion clients/federation/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package federation
import (
"encoding/json"
"fmt"
"io"
"net/url"
"strings"

Expand Down Expand Up @@ -102,7 +103,13 @@ func (c *Client) getJSON(url string, dest interface{}) error {
return errors.Errorf("http get failed with (%d) status code", hresp.StatusCode)
}

err = json.NewDecoder(hresp.Body).Decode(dest)
limitReader := io.LimitReader(hresp.Body, FederationResponseMaxSize)

err = json.NewDecoder(limitReader).Decode(dest)
if err == io.ErrUnexpectedEOF && limitReader.(*io.LimitedReader).N == 0 {
return errors.Errorf("federation response exceeds %d bytes limit", FederationResponseMaxSize)
}

if err != nil {
return errors.Wrap(err, "json decode errored")
}
Expand Down
17 changes: 17 additions & 0 deletions clients/federation/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package federation
import (
"errors"
"net/http"
"strings"
"testing"

"github.com/stellar/go/clients/stellartoml"
Expand Down Expand Up @@ -34,6 +35,22 @@ func TestLookupByAddress(t *testing.T) {
assert.Equal(t, "123", resp.Memo)
}

// response exceeds limit
tomlmock.On("GetStellarToml", "toobig.org").Return(&stellartoml.Response{
FederationServer: "https://toobig.org/federation",
}, nil)
hmock.On("GET", "https://toobig.org/federation").
ReturnJSON(http.StatusOK, map[string]string{
"stellar_address": strings.Repeat("0", FederationResponseMaxSize) + "*stellar.org",
"account_id": "GASTNVNLHVR3NFO3QACMHCJT3JUSIV4NBXDHDO4VTPDTNN65W3B2766C",
"memo_type": "id",
"memo": "123",
})
_, err = c.LookupByAddress("response*toobig.org")
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "federation response exceeds")
}

// failed toml resolution
tomlmock.On("GetStellarToml", "missing.org").Return(
(*stellartoml.Response)(nil),
Expand Down
3 changes: 3 additions & 0 deletions clients/federation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"github.com/stellar/go/clients/stellartoml"
)

// FederationResponseMaxSize is the maximum size of response from a federation server
const FederationResponseMaxSize = 100 * 1024

// DefaultTestNetClient is a default federation client for testnet
var DefaultTestNetClient = &Client{
HTTP: http.DefaultClient,
Expand Down
12 changes: 11 additions & 1 deletion clients/stellartoml/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package stellartoml

import (
"fmt"
"io"
"net/http"

"github.com/BurntSushi/toml"
Expand All @@ -24,7 +25,16 @@ func (c *Client) GetStellarToml(domain string) (resp *Response, err error) {
return
}

_, err = toml.DecodeReader(hresp.Body, &resp)
limitReader := io.LimitReader(hresp.Body, StellarTomlMaxSize)
_, err = toml.DecodeReader(limitReader, &resp)

// There is one corner case not handled here: response is exactly StellarTomlMaxSize long and is incorrect toml.
// Check discussion: https://github.com/stellar/go/pull/24#discussion_r89909696
if err != nil && limitReader.(*io.LimitedReader).N == 0 {
err = errors.Errorf("stellar.toml response exceeds %d bytes limit", StellarTomlMaxSize)
return
}

if err != nil {
err = errors.Wrap(err, "toml decode failed")
return
Expand Down
12 changes: 12 additions & 0 deletions clients/stellartoml/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package stellartoml

import (
"net/http"
"strings"
"testing"

"github.com/stellar/go/support/http/httptest"
Expand Down Expand Up @@ -34,6 +35,17 @@ func TestClient(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "https://localhost/federation", stoml.FederationServer)

// stellar.toml exceeds limit
h.
On("GET", "https://toobig.org/.well-known/stellar.toml").
ReturnString(http.StatusOK,
`FEDERATION_SERVER="https://localhost/federation`+strings.Repeat("0", StellarTomlMaxSize)+`"`,
)
stoml, err = c.GetStellarToml("toobig.org")
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "stellar.toml response exceeds")
}

// not found
h.
On("GET", "https://missing.org/.well-known/stellar.toml").
Expand Down
3 changes: 3 additions & 0 deletions clients/stellartoml/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package stellartoml

import "net/http"

// StellarTomlMaxSize is the maximum size of stellar.toml file
const StellarTomlMaxSize = 5 * 1024

// WellKnownPath represents the url path at which the stellar.toml file should
// exist to conform to the federation protocol.
const WellKnownPath = "/.well-known/stellar.toml"
Expand Down

0 comments on commit d8b0f52

Please sign in to comment.