Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9fc8c0e

Browse files
Review comments 3 - Describe path traversal and add tests
Change continue token API value to v1alpha1
1 parent d844464 commit 9fc8c0e

2 files changed

Lines changed: 62 additions & 9 deletions

File tree

staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ func (s *store) GetToList(ctx context.Context, key string, resourceVersion strin
397397
// continueToken is a simple structured object for encoding the state of a continue token.
398398
// TODO: if we change the version of the encoded from, we can't start encoding the new version
399399
// until all other servers are upgraded (i.e. we need to support rolling schema)
400-
// TODO: be very careful about changing this
400+
// This is a public API struct and cannot change.
401401
type continueToken struct {
402402
APIVersion string `json:"v"`
403403
ResourceVersion int64 `json:"rv"`
@@ -416,13 +416,17 @@ func decodeContinue(continueValue, keyPrefix string) (fromKey string, rv int64,
416416
return "", 0, fmt.Errorf("continue key is not valid: %v", err)
417417
}
418418
switch c.APIVersion {
419-
case "v1":
419+
case "v1alpha1":
420420
if c.ResourceVersion == 0 {
421-
return "", 0, fmt.Errorf("continue key is not valid: incorrect encoded start resourceVersion (version v1)")
421+
return "", 0, fmt.Errorf("continue key is not valid: incorrect encoded start resourceVersion (version v1alpha1)")
422422
}
423423
if len(c.StartKey) == 0 {
424-
return "", 0, fmt.Errorf("continue key is not valid: encoded start key empty (version v1)")
424+
return "", 0, fmt.Errorf("continue key is not valid: encoded start key empty (version v1alpha1)")
425425
}
426+
// defend against path traversal attacks by clients - path.Clean will ensure that startKey cannot
427+
// be at a higher level of the hierarchy, and so when we append the key prefix we will end up with
428+
// continue start key that is fully qualified and cannot range over anything less specific than
429+
// keyPrefix.
426430
cleaned := path.Clean(c.StartKey)
427431
if cleaned != c.StartKey || cleaned == "." || cleaned == "/" {
428432
return "", 0, fmt.Errorf("continue key is not valid: %s", cleaned)
@@ -442,7 +446,7 @@ func encodeContinue(key, keyPrefix string, resourceVersion int64) (string, error
442446
if nextKey == key {
443447
return "", fmt.Errorf("unable to encode next field: the key and key prefix do not match")
444448
}
445-
out, err := json.Marshal(&continueToken{APIVersion: "v1", ResourceVersion: resourceVersion, StartKey: nextKey})
449+
out, err := json.Marshal(&continueToken{APIVersion: "v1alpha1", ResourceVersion: resourceVersion, StartKey: nextKey})
446450
if err != nil {
447451
return "", err
448452
}

staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ package etcd3
1818

1919
import (
2020
"bytes"
21+
"encoding/base64"
22+
"encoding/json"
2123
"fmt"
2224
"reflect"
2325
"strconv"
2426
"sync"
2527
"testing"
2628

29+
"github.com/coreos/etcd/clientv3"
30+
"github.com/coreos/etcd/integration"
31+
"golang.org/x/net/context"
2732
apitesting "k8s.io/apimachinery/pkg/api/testing"
2833
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2934
"k8s.io/apimachinery/pkg/fields"
@@ -37,10 +42,6 @@ import (
3742
"k8s.io/apiserver/pkg/storage"
3843
storagetests "k8s.io/apiserver/pkg/storage/tests"
3944
"k8s.io/apiserver/pkg/storage/value"
40-
41-
"github.com/coreos/etcd/clientv3"
42-
"github.com/coreos/etcd/integration"
43-
"golang.org/x/net/context"
4445
)
4546

4647
var scheme = runtime.NewScheme()
@@ -923,3 +924,51 @@ func TestPrefix(t *testing.T) {
923924
}
924925
}
925926
}
927+
928+
func encodeContinueOrDie(apiVersion string, resourceVersion int64, nextKey string) string {
929+
out, err := json.Marshal(&continueToken{APIVersion: apiVersion, ResourceVersion: resourceVersion, StartKey: nextKey})
930+
if err != nil {
931+
panic(err)
932+
}
933+
return base64.RawURLEncoding.EncodeToString(out)
934+
}
935+
936+
func Test_decodeContinue(t *testing.T) {
937+
type args struct {
938+
continueValue string
939+
keyPrefix string
940+
}
941+
tests := []struct {
942+
name string
943+
args args
944+
wantFromKey string
945+
wantRv int64
946+
wantErr bool
947+
}{
948+
{name: "valid", args: args{continueValue: encodeContinueOrDie("v1alpha1", 1, "key"), keyPrefix: "/test/"}, wantRv: 1, wantFromKey: "/test/key"},
949+
950+
{name: "empty version", args: args{continueValue: encodeContinueOrDie("", 1, "key"), keyPrefix: "/test/"}, wantErr: true},
951+
{name: "invalid version", args: args{continueValue: encodeContinueOrDie("v1", 1, "key"), keyPrefix: "/test/"}, wantErr: true},
952+
953+
{name: "path traversal - parent", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "../key"), keyPrefix: "/test/"}, wantErr: true},
954+
{name: "path traversal - local", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "./key"), keyPrefix: "/test/"}, wantErr: true},
955+
{name: "path traversal - double parent", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "./../key"), keyPrefix: "/test/"}, wantErr: true},
956+
{name: "path traversal - after parent", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "key/../.."), keyPrefix: "/test/"}, wantErr: true},
957+
{name: "path traversal - separator", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "/"), keyPrefix: "/test/"}, wantErr: true},
958+
}
959+
for _, tt := range tests {
960+
t.Run(tt.name, func(t *testing.T) {
961+
gotFromKey, gotRv, err := decodeContinue(tt.args.continueValue, tt.args.keyPrefix)
962+
if (err != nil) != tt.wantErr {
963+
t.Errorf("decodeContinue() error = %v, wantErr %v", err, tt.wantErr)
964+
return
965+
}
966+
if gotFromKey != tt.wantFromKey {
967+
t.Errorf("decodeContinue() gotFromKey = %v, want %v", gotFromKey, tt.wantFromKey)
968+
}
969+
if gotRv != tt.wantRv {
970+
t.Errorf("decodeContinue() gotRv = %v, want %v", gotRv, tt.wantRv)
971+
}
972+
})
973+
}
974+
}

0 commit comments

Comments
 (0)