Skip to content

Commit 8409e00

Browse files
authored
Validate Minecraft resource keys (minekube#892)
1 parent cbb918b commit 8409e00

4 files changed

Lines changed: 116 additions & 13 deletions

File tree

pkg/edition/java/cookie/internal.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"strings"
87
"sync"
98

109
"github.com/robinbraemer/event"
1110
"go.minekube.com/common/minecraft/key"
1211
"go.minekube.com/gate/pkg/edition/java/internal/methods"
1312
pkt "go.minekube.com/gate/pkg/edition/java/proto/packet/cookie"
1413
"go.minekube.com/gate/pkg/edition/java/proto/state/states"
14+
protoutils "go.minekube.com/gate/pkg/edition/java/proto/util"
1515

1616
"go.minekube.com/gate/pkg/edition/java/proto/version"
1717
"go.minekube.com/gate/pkg/edition/java/proxy"
@@ -25,13 +25,7 @@ func validate(c *Cookie, cli Client) error {
2525
}
2626

2727
func validateKey(key key.Key) error {
28-
if key == nil {
29-
return errors.New("key is nil")
30-
}
31-
if strings.TrimSpace(key.String()) == "" {
32-
return errors.New("empty key")
33-
}
34-
return nil
28+
return protoutils.ValidateKey(key)
3529
}
3630

3731
func validateCookie(c *Cookie) error {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package cookie
2+
3+
import (
4+
"testing"
5+
6+
"go.minekube.com/common/minecraft/key"
7+
)
8+
9+
func TestValidateKeyRejectsInvalidResourceLocations(t *testing.T) {
10+
for _, invalid := range []key.Key{
11+
key.New("MineKube", "cookie"),
12+
key.New("minecraft", "BadCookie"),
13+
key.New("mine kube", "cookie"),
14+
key.New("..", "cookie"),
15+
} {
16+
t.Run(invalid.String(), func(t *testing.T) {
17+
if err := validateKey(invalid); err == nil {
18+
t.Fatalf("validateKey(%q) succeeded, want error", invalid)
19+
}
20+
})
21+
}
22+
}

pkg/edition/java/proto/util/reader.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -437,23 +437,50 @@ func ReadUnixMilli(rd io.Reader) (time.Time, error) {
437437
//
438438
//
439439

440-
const defaultKeySeparator = ":"
440+
// ValidateKey verifies that a key is a valid Minecraft resource location.
441+
func ValidateKey(k key.Key) error {
442+
if k == nil {
443+
return errors.New("key is nil")
444+
}
445+
if k.Namespace() == ".." {
446+
return fmt.Errorf("invalid key %q: namespace must not be ..", k.String())
447+
}
448+
if !key.NamespaceValid(k.Namespace()) || !key.ValueValid(k.Value()) {
449+
return fmt.Errorf("invalid key %q", k.String())
450+
}
451+
return nil
452+
}
441453

442454
// ReadKey reads a standard Mojang Text namespaced:key from the reader.
443455
func ReadKey(rd io.Reader) (key.Key, error) {
444456
str, err := ReadString(rd)
445457
if err != nil {
446458
return nil, err
447459
}
448-
parts := strings.SplitN(str, defaultKeySeparator, 2)
449-
if len(parts) != 2 {
450-
return nil, errors.New("invalid key format")
460+
k := parseIdentifierKey(str)
461+
if err := ValidateKey(k); err != nil {
462+
return nil, err
463+
}
464+
return k, nil
465+
}
466+
467+
func parseIdentifierKey(str string) key.Key {
468+
namespace := key.MinecraftNamespace
469+
value := str
470+
if separatorIndex := strings.IndexByte(str, ':'); separatorIndex >= 0 {
471+
value = str[separatorIndex+1:]
472+
if separatorIndex != 0 {
473+
namespace = str[:separatorIndex]
474+
}
451475
}
452-
return key.New(parts[0], parts[1]), nil
476+
return key.New(namespace, value)
453477
}
454478

455479
// WriteKey writes a standard Mojang Text namespaced:key to the writer.
456480
func WriteKey(wr io.Writer, k key.Key) error {
481+
if err := ValidateKey(k); err != nil {
482+
return err
483+
}
457484
return WriteString(wr, k.String())
458485
}
459486

pkg/edition/java/proto/util/writer_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,68 @@ import (
44
"bytes"
55
"math"
66
"testing"
7+
8+
"go.minekube.com/common/minecraft/key"
79
)
810

11+
func TestReadKeyRejectsInvalidResourceLocations(t *testing.T) {
12+
for _, raw := range []string{
13+
"MineKube:cookie",
14+
"minecraft:BadCookie",
15+
"mine kube:cookie",
16+
"..:cookie",
17+
} {
18+
t.Run(raw, func(t *testing.T) {
19+
var buf bytes.Buffer
20+
if err := WriteString(&buf, raw); err != nil {
21+
t.Fatalf("failed to write raw key string: %v", err)
22+
}
23+
24+
if got, err := ReadKey(&buf); err == nil {
25+
t.Fatalf("ReadKey(%q) = %q, want error", raw, got)
26+
}
27+
})
28+
}
29+
}
30+
31+
func TestReadKeyDefaultsMissingNamespaceToMinecraft(t *testing.T) {
32+
for raw, want := range map[string]key.Key{
33+
"cookie": key.New(key.MinecraftNamespace, "cookie"),
34+
":cookie": key.New(key.MinecraftNamespace, "cookie"),
35+
} {
36+
t.Run(raw, func(t *testing.T) {
37+
var buf bytes.Buffer
38+
if err := WriteString(&buf, raw); err != nil {
39+
t.Fatalf("failed to write raw key string: %v", err)
40+
}
41+
42+
got, err := ReadKey(&buf)
43+
if err != nil {
44+
t.Fatalf("ReadKey(%q) returned error: %v", raw, err)
45+
}
46+
if got.String() != want.String() {
47+
t.Fatalf("ReadKey(%q) = %q, want %q", raw, got, want)
48+
}
49+
})
50+
}
51+
}
52+
53+
func TestWriteKeyRejectsInvalidResourceLocations(t *testing.T) {
54+
for _, invalid := range []key.Key{
55+
key.New("MineKube", "cookie"),
56+
key.New("minecraft", "BadCookie"),
57+
key.New("mine kube", "cookie"),
58+
key.New("..", "cookie"),
59+
} {
60+
t.Run(invalid.String(), func(t *testing.T) {
61+
var buf bytes.Buffer
62+
if err := WriteKey(&buf, invalid); err == nil {
63+
t.Fatalf("WriteKey(%q) succeeded, want error", invalid)
64+
}
65+
})
66+
}
67+
}
68+
969
// TestWriteBytes17_NonExtended tests WriteBytes17 with allowExtended=false
1070
// This is the case that was failing for 1.7.x clients with encryption requests
1171
func TestWriteBytes17_NonExtended(t *testing.T) {

0 commit comments

Comments
 (0)