From 248fdd8451708cb11cea6fb2a9833fb710e0ab51 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Sat, 30 May 2026 19:59:16 -0700 Subject: [PATCH 1/2] fix(transcoders): return error on short onion/onion3 input instead of panicking Signed-off-by: Sai Asish Y --- transcoders.go | 6 ++++++ transcoders_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 transcoders_test.go diff --git a/transcoders.go b/transcoders.go index 03b39ae..f7489ad 100644 --- a/transcoders.go +++ b/transcoders.go @@ -191,6 +191,9 @@ func onionStB(s string) ([]byte, error) { } func onionBtS(b []byte) (string, error) { + if len(b) != 12 { + return "", fmt.Errorf("invalid len for onion addr: got %d expected 12", len(b)) + } addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10])) port := binary.BigEndian.Uint16(b[10:12]) if port == 0 { @@ -245,6 +248,9 @@ func onion3StB(s string) ([]byte, error) { } func onion3BtS(b []byte) (string, error) { + if len(b) != 37 { + return "", fmt.Errorf("invalid len for onion addr: got %d expected 37", len(b)) + } addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:35])) port := binary.BigEndian.Uint16(b[35:37]) if port < 1 { diff --git a/transcoders_test.go b/transcoders_test.go new file mode 100644 index 0000000..9a80560 --- /dev/null +++ b/transcoders_test.go @@ -0,0 +1,27 @@ +package multiaddr + +import "testing" + +// Regression test for https://github.com/multiformats/go-multiaddr/issues/288. +// onionBtS and onion3BtS used to index their input at fixed offsets without +// a length check, panicking with `slice bounds out of range` when called with +// short input (e.g. directly via the exported TranscoderOnion / TranscoderOnion3). +func TestOnionTranscodersRejectShortInput(t *testing.T) { + cases := []struct { + name string + tr Transcoder + b []byte + }{ + {"onion-short", TranscoderOnion, []byte{0x00, 0x01}}, + {"onion-empty", TranscoderOnion, nil}, + {"onion3-short", TranscoderOnion3, []byte{0x00, 0x01}}, + {"onion3-empty", TranscoderOnion3, nil}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if _, err := tc.tr.BytesToString(tc.b); err == nil { + t.Fatalf("expected error for short input, got nil") + } + }) + } +} From 3aac990ff34e6686b8df08e8fe1a9d1276f4fa66 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Tue, 2 Jun 2026 11:03:04 -0700 Subject: [PATCH 2/2] guard portBtS against short input and add portValidate Signed-off-by: Sai Asish Y --- transcoders.go | 12 +++++++++++- transcoders_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/transcoders.go b/transcoders.go index f7489ad..1152693 100644 --- a/transcoders.go +++ b/transcoders.go @@ -138,7 +138,7 @@ func ip4BtS(b []byte) (string, error) { return net.IP(b).String(), nil } -var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, nil) +var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, portValidate) func portStB(s string) ([]byte, error) { i, err := strconv.ParseUint(s, 10, 16) @@ -151,10 +151,20 @@ func portStB(s string) ([]byte, error) { } func portBtS(b []byte) (string, error) { + if len(b) < 2 { + return "", fmt.Errorf("port: byte slice too short: %d bytes, want 2", len(b)) + } i := binary.BigEndian.Uint16(b) return strconv.FormatUint(uint64(i), 10), nil } +func portValidate(b []byte) error { + if len(b) != 2 { + return fmt.Errorf("port: invalid length: %d bytes, want 2", len(b)) + } + return nil +} + var TranscoderOnion = NewTranscoderFromFunctions(onionStB, onionBtS, onionValidate) func onionStB(s string) ([]byte, error) { diff --git a/transcoders_test.go b/transcoders_test.go index 9a80560..1666b91 100644 --- a/transcoders_test.go +++ b/transcoders_test.go @@ -25,3 +25,31 @@ func TestOnionTranscodersRejectShortInput(t *testing.T) { }) } } + +// portBtS used to call binary.BigEndian.Uint16 without checking len(b) >= 2. +// Direct callers of TranscoderPort (outside the codec.go length checks) could +// panic with `index out of range`. The internal guard and the new validator +// must turn both into normal errors. +func TestTranscoderPortRejectsShortInput(t *testing.T) { + t.Run("BytesToString-short", func(t *testing.T) { + if _, err := TranscoderPort.BytesToString(nil); err == nil { + t.Fatalf("expected error on nil input") + } + if _, err := TranscoderPort.BytesToString([]byte{0x00}); err == nil { + t.Fatalf("expected error on 1-byte input") + } + }) + t.Run("Validate-rejects-wrong-length", func(t *testing.T) { + if err := TranscoderPort.ValidateBytes(nil); err == nil { + t.Fatalf("expected validate error on nil input") + } + if err := TranscoderPort.ValidateBytes([]byte{0x00, 0x01, 0x02}); err == nil { + t.Fatalf("expected validate error on 3-byte input") + } + }) + t.Run("Validate-accepts-2-byte", func(t *testing.T) { + if err := TranscoderPort.ValidateBytes([]byte{0x00, 0x50}); err != nil { + t.Fatalf("expected no error on 2-byte input, got %v", err) + } + }) +}