Skip to content

Commit

Permalink
Add request length check to json rpcs (#221)
Browse files Browse the repository at this point in the history
* Add request length check to avoid ddos

* Add tests
  • Loading branch information
philipsu522 authored Apr 2, 2024
1 parent 66ac407 commit 338bb7d
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
7 changes: 7 additions & 0 deletions rpc/jsonrpc/server/http_json_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (

// HTTP + JSON handler

const REQUEST_BATCH_SIZE_LIMIT = 10

// jsonrpc calls grab the given method's function info and runs reflect.Call
func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, hreq *http.Request) {
Expand All @@ -41,6 +43,11 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
}

requests, err := parseRequests(b)
if len(requests) > REQUEST_BATCH_SIZE_LIMIT {
writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf(
rpctypes.CodeParseError, "Batch size limit exceeded."))
return
}
if err != nil {
writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf(
rpctypes.CodeParseError, "decoding request: %v", err))
Expand Down
62 changes: 62 additions & 0 deletions rpc/jsonrpc/server/http_json_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package server
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -221,6 +222,67 @@ func TestRPCNotificationInBatch(t *testing.T) {
}
}

func TestRPCBatchLimit(t *testing.T) {
mux := testMux()
tests := []struct {
payload string
success bool
}{
{
`[
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]}
]`,
true,
},
{
`[
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]},
{"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]}
]`,
false,
},
}
for i, tt := range tests {
req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload))
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
res := rec.Result()
// Always expecting back a JSONRPCResponse
assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i)
blob, err := io.ReadAll(res.Body)

fmt.Printf("responses: %s\n", blob)
if err != nil {
t.Errorf("#%d: err reading body: %v", i, err)
continue
}
res.Body.Close()

var responses []rpctypes.RPCResponse
err = json.Unmarshal(blob, &responses)
if err != nil {
if tt.success {
t.Errorf("#%d: expected successful parsing of an RPCResponse\nblob: %s", i, blob)
continue
} else {
fmt.Printf("blob: %s, %d\n", responses, len(responses))
assert.Contains(t, string(blob), "Batch size limit exceeded.")
}
}

}
}

func TestUnknownRPCPath(t *testing.T) {
mux := testMux()
req, _ := http.NewRequest("GET", "http://localhost/unknownrpcpath", strings.NewReader(""))
Expand Down

0 comments on commit 338bb7d

Please sign in to comment.