113 lines
4.1 KiB
Go
113 lines
4.1 KiB
Go
package control
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
|
|
"github.com/rvbox/rvbox/internal/server/store"
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
)
|
|
|
|
func TestJSONRPCUnaryMatchesControlService_HP_CTL_15(t *testing.T) {
|
|
persistence, err := store.Open(context.Background(), store.Options{DataDir: filepath.Join(t.TempDir(), "state"), BusyTimeout: time.Second})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer persistence.Close()
|
|
registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_POWERSHELL, 9)
|
|
service, err := NewService(Options{Store: persistence, CursorKey: bytesKey()})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
handler := httptest.NewServer(NewJSONRPCHandler(service, 1<<20))
|
|
defer handler.Close()
|
|
requestID := fixedIssue(0xaf).String()
|
|
params, err := protojson.Marshal(&rvboxv1.RunCommandRequest{TargetClientId: "win-a", RequestId: requestID, Spec: &rvboxv1.ExecutionSpec{Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo rpc"}}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body, _ := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": 7, "method": "runCommand", "params": json.RawMessage(params)})
|
|
response, err := http.Post(handler.URL, "application/json", bytes.NewReader(body))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer response.Body.Close()
|
|
encoded, _ := io.ReadAll(response.Body)
|
|
var envelope struct {
|
|
Result json.RawMessage `json:"result"`
|
|
Error json.RawMessage `json:"error"`
|
|
}
|
|
if err := json.Unmarshal(encoded, &envelope); err != nil || len(envelope.Error) != 0 {
|
|
t.Fatalf("JSON-RPC response = %s, %v", encoded, err)
|
|
}
|
|
result := &rvboxv1.RunCommandResponse{}
|
|
if err := protojson.Unmarshal(envelope.Result, result); err != nil || result.GetIssueUuid() != requestID {
|
|
t.Fatalf("JSON-RPC result = %s, %v", envelope.Result, err)
|
|
}
|
|
unknown, _ := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": "x", "method": "notARealMethod", "params": map[string]any{}})
|
|
unknownResponse, err := http.Post(handler.URL, "application/json", bytes.NewReader(unknown))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer unknownResponse.Body.Close()
|
|
unknownBody, _ := io.ReadAll(unknownResponse.Body)
|
|
if !bytes.Contains(unknownBody, []byte(`-32601`)) {
|
|
t.Fatalf("unknown method response = %s", unknownBody)
|
|
}
|
|
}
|
|
|
|
func TestJSONRPCRejectsOversizeAndMalformedRequests_BH_CTL_16(t *testing.T) {
|
|
persistence, err := store.Open(context.Background(), store.Options{DataDir: filepath.Join(t.TempDir(), "state"), BusyTimeout: time.Second})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer persistence.Close()
|
|
service, err := NewService(Options{Store: persistence, CursorKey: bytesKey()})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
handler := httptest.NewServer(NewJSONRPCHandler(service, 16))
|
|
defer handler.Close()
|
|
response, err := http.Post(handler.URL, "application/json", bytes.NewReader([]byte(`{"jsonrpc":"2.0","id":1,"method":"listClients","params":{}}`)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer response.Body.Close()
|
|
body, _ := io.ReadAll(response.Body)
|
|
if !bytes.Contains(body, []byte(`-32600`)) {
|
|
t.Fatalf("oversize response = %s", body)
|
|
}
|
|
malformed, err := http.Post(handler.URL, "application/json", bytes.NewReader([]byte(`{"jsonrpc":"2.0","id":1,"method":"listClients","params":{}}`[:16])))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer malformed.Body.Close()
|
|
malformedBody, _ := io.ReadAll(malformed.Body)
|
|
if !bytes.Contains(malformedBody, []byte(`-32700`)) {
|
|
t.Fatalf("malformed response = %s", malformedBody)
|
|
}
|
|
}
|
|
|
|
func FuzzDecodeJSONRPCRequestBounded_SEC_CTL_01(f *testing.F) {
|
|
f.Add([]byte(`{"jsonrpc":"2.0","id":1,"method":"listClients","params":{}}`))
|
|
f.Add([]byte(`{"jsonrpc":"2.0","id":null,"method":"listClients"}`))
|
|
f.Add([]byte(`{"jsonrpc":"2.0","id":1,"method":"listClients"}{}`))
|
|
f.Fuzz(func(t *testing.T, body []byte) {
|
|
// Keep fuzzing at the same independently enforced boundary as the
|
|
// production handler rather than allowing a corpus entry to allocate
|
|
// unbounded JSON decoder state.
|
|
if len(body) > 64<<10 {
|
|
return
|
|
}
|
|
_, _, _ = decodeJSONRPCRequest(body, 64<<10)
|
|
})
|
|
}
|