package grpcapi import ( "context" "crypto/sha256" "testing" "galaxy/gateway/internal/session" "connectrpc.com/connect" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestExecuteCommandRejectsPayloadHashWithInvalidLength(t *testing.T) { t.Parallel() delegate := &recordingGatewayService{} server, runGateway := newTestGateway(t, ServerDependencies{ Service: delegate, SessionCache: staticSessionCache{lookupFunc: func(context.Context, string) (session.Record, error) { return newActiveSessionRecord(), nil }}, }) defer runGateway.stop(t) addr := waitForListenAddr(t, server) client := newEdgeClient(t, addr) req := newValidExecuteCommandRequest() req.PayloadHash = []byte("short") // Signature verification now precedes the payload-hash gate, so re-sign // over the tampered hash to keep the signature valid and exercise the gate. req.Signature = signRequest(req.GetProtocolVersion(), req.GetDeviceSessionId(), req.GetMessageType(), req.GetTimestampMs(), req.GetRequestId(), req.GetPayloadHash()) _, err := client.ExecuteCommand(context.Background(), connect.NewRequest(req)) require.Error(t, err) assert.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err)) assert.Equal(t, "payload_hash must be a 32-byte SHA-256 digest", connectErrorMessage(t, err)) assert.Zero(t, delegate.executeCalls) } func TestExecuteCommandRejectsPayloadHashMismatch(t *testing.T) { t.Parallel() delegate := &recordingGatewayService{} server, runGateway := newTestGateway(t, ServerDependencies{ Service: delegate, SessionCache: staticSessionCache{lookupFunc: func(context.Context, string) (session.Record, error) { return newActiveSessionRecord(), nil }}, }) defer runGateway.stop(t) addr := waitForListenAddr(t, server) client := newEdgeClient(t, addr) req := newValidExecuteCommandRequest() sum := sha256.Sum256([]byte("other")) req.PayloadHash = sum[:] // Signature verification now precedes the payload-hash gate, so re-sign // over the tampered hash to keep the signature valid and exercise the gate. req.Signature = signRequest(req.GetProtocolVersion(), req.GetDeviceSessionId(), req.GetMessageType(), req.GetTimestampMs(), req.GetRequestId(), req.GetPayloadHash()) _, err := client.ExecuteCommand(context.Background(), connect.NewRequest(req)) require.Error(t, err) assert.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err)) assert.Equal(t, "payload_hash does not match payload_bytes", connectErrorMessage(t, err)) assert.Zero(t, delegate.executeCalls) } func TestSubscribeEventsRejectsPayloadHashWithInvalidLength(t *testing.T) { t.Parallel() delegate := &recordingGatewayService{} server, runGateway := newTestGateway(t, ServerDependencies{ Service: delegate, SessionCache: staticSessionCache{lookupFunc: func(context.Context, string) (session.Record, error) { return newActiveSessionRecord(), nil }}, }) defer runGateway.stop(t) addr := waitForListenAddr(t, server) client := newEdgeClient(t, addr) req := newValidSubscribeEventsRequest() req.PayloadHash = []byte("short") // Signature verification now precedes the payload-hash gate, so re-sign // over the tampered hash to keep the signature valid and exercise the gate. req.Signature = signRequest(req.GetProtocolVersion(), req.GetDeviceSessionId(), req.GetMessageType(), req.GetTimestampMs(), req.GetRequestId(), req.GetPayloadHash()) err := subscribeEventsError(t, context.Background(), client, req) require.Error(t, err) assert.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err)) assert.Equal(t, "payload_hash must be a 32-byte SHA-256 digest", connectErrorMessage(t, err)) assert.Zero(t, delegate.subscribeCalls) } func TestSubscribeEventsRejectsPayloadHashMismatch(t *testing.T) { t.Parallel() delegate := &recordingGatewayService{} server, runGateway := newTestGateway(t, ServerDependencies{ Service: delegate, SessionCache: staticSessionCache{lookupFunc: func(context.Context, string) (session.Record, error) { return newActiveSessionRecord(), nil }}, }) defer runGateway.stop(t) addr := waitForListenAddr(t, server) client := newEdgeClient(t, addr) req := newValidSubscribeEventsRequest() sum := sha256.Sum256([]byte("other")) req.PayloadHash = sum[:] // Signature verification now precedes the payload-hash gate, so re-sign // over the tampered hash to keep the signature valid and exercise the gate. req.Signature = signRequest(req.GetProtocolVersion(), req.GetDeviceSessionId(), req.GetMessageType(), req.GetTimestampMs(), req.GetRequestId(), req.GetPayloadHash()) err := subscribeEventsError(t, context.Background(), client, req) require.Error(t, err) assert.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err)) assert.Equal(t, "payload_hash does not match payload_bytes", connectErrorMessage(t, err)) assert.Zero(t, delegate.subscribeCalls) }