PR Size Review Check / pr-size-review-gate (pull_request) Successful in 46s
sizelint / sizelint (pull_request) Successful in 1m0s
buildbot/nix-build gitea:clan/data-mesher#checks.aarch64-linux.nixos-data-mesher-basic Build done.
buildbot/nix-eval Build done.
buildbot/nix-build Build done.
gitea-mq/buildbot/nix-eval Build done.
gitea-mq/buildbot/nix-build Build done.
gitea-mq Merge queue passed
Three msgpack decoders read directly from unbounded libp2p streams: file-transfer server (`FileRequest`), file-transfer client (`FileResponse`), and the outbound state-exchange response. A malicious peer can stream arbitrarily large strings into decoded fields, causing the victim to allocate memory until OOM. Wrap all peer-facing decoders in `io.LimitReader`. The inbound state-exchange handler (`handleStream`) already had this protection; the other three paths were missed.
165 lines
3.0 KiB
Go
165 lines
3.0 KiB
Go
package msgpack_test
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.clan.lol/clan/data-mesher/pkg/msgpack"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNetIPExtension(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
ip net.IP
|
|
}{
|
|
{
|
|
name: "IPv4",
|
|
ip: net.ParseIP("192.168.1.1").To4(),
|
|
},
|
|
{
|
|
name: "IPv6",
|
|
ip: net.ParseIP("2001:db8::1"),
|
|
},
|
|
{
|
|
name: "IPv4-mapped IPv6",
|
|
ip: net.ParseIP("::ffff:192.168.1.1"),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Test direct marshal/unmarshal
|
|
data, err := msgpack.Marshal(tt.ip)
|
|
if err != nil {
|
|
t.Fatalf("Marshal failed: %v", err)
|
|
}
|
|
|
|
var result net.IP
|
|
if err := msgpack.Unmarshal(data, &result); err != nil {
|
|
t.Fatalf("Unmarshal failed: %v", err)
|
|
}
|
|
|
|
if !result.Equal(tt.ip) {
|
|
t.Errorf("IP mismatch: got %v, want %v", result, tt.ip)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNetIPInStruct(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type testStruct struct {
|
|
Name string
|
|
IP net.IP
|
|
Port int
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
data testStruct
|
|
}{
|
|
{
|
|
name: "struct with IPv4",
|
|
data: testStruct{
|
|
Name: "server1",
|
|
IP: net.ParseIP("10.0.0.1").To4(),
|
|
Port: 8080,
|
|
},
|
|
},
|
|
{
|
|
name: "struct with IPv6",
|
|
data: testStruct{
|
|
Name: "server2",
|
|
IP: net.ParseIP("fe80::1"),
|
|
Port: 443,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data, err := msgpack.Marshal(tt.data)
|
|
if err != nil {
|
|
t.Fatalf("Marshal failed: %v", err)
|
|
}
|
|
|
|
var result testStruct
|
|
if err := msgpack.Unmarshal(data, &result); err != nil {
|
|
t.Fatalf("Unmarshal failed: %v", err)
|
|
}
|
|
|
|
if result.Name != tt.data.Name {
|
|
t.Errorf("Name mismatch: got %q, want %q", result.Name, tt.data.Name)
|
|
}
|
|
|
|
if !result.IP.Equal(tt.data.IP) {
|
|
t.Errorf("IP mismatch: got %v, want %v", result.IP, tt.data.IP)
|
|
}
|
|
|
|
if result.Port != tt.data.Port {
|
|
t.Errorf("Port mismatch: got %d, want %d", result.Port, tt.data.Port)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLimitedDecoder_RejectsOversizedPayload(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
|
|
type msg struct {
|
|
Data string `codec:"data"`
|
|
}
|
|
|
|
const limit = 1024
|
|
|
|
// encode a message larger than the limit
|
|
payload := msg{Data: strings.Repeat("A", limit*2)}
|
|
|
|
encoded, err := msgpack.Marshal(payload)
|
|
as.NoError(err)
|
|
as.Greater(len(encoded), limit)
|
|
|
|
// decoding through a LimitReader must fail
|
|
decoder := msgpack.NewDecoder(io.LimitReader(bytes.NewReader(encoded), limit))
|
|
|
|
var result msg
|
|
as.Error(decoder.Decode(&result))
|
|
}
|
|
|
|
func TestLimitedDecoder_AcceptsPayloadWithinLimit(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
|
|
type msg struct {
|
|
Data string `codec:"data"`
|
|
}
|
|
|
|
const limit = 1024
|
|
|
|
payload := msg{Data: "hello"}
|
|
|
|
encoded, err := msgpack.Marshal(payload)
|
|
as.NoError(err)
|
|
as.Less(len(encoded), limit)
|
|
|
|
decoder := msgpack.NewDecoder(io.LimitReader(bytes.NewReader(encoded), limit))
|
|
|
|
var result msg
|
|
as.NoError(decoder.Decode(&result))
|
|
as.Equal(payload.Data, result.Data)
|
|
}
|