Skip to content

Commit

Permalink
Add new integration and e2e test for invalid client api version.
Browse files Browse the repository at this point in the history
Signed-off-by: James Blair <mail@jamesblair.net>
  • Loading branch information
jmhbnz committed Mar 27, 2023
1 parent 30abf17 commit 1562124
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
32 changes: 32 additions & 0 deletions tests/e2e/v3_curl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,35 @@ func CURLWithExpected(cx ctlCtx, tests []v3cURLTest) error {
}
return nil
}

func TestV3CurlInvalidUTF8VersionHandling(t *testing.T) {
for _, p := range apiPrefix {
testCtl(t, testV3CurlInvalidUTF8VersionHandling, withApiPrefix(p), withCfg(*e2e.NewConfigNoTLS()))
}
}

func testV3CurlInvalidUTF8VersionHandling(cx ctlCtx) {
p := cx.apiPrefix

// Create an ordinary put payload
putData, err := json.Marshal(&pb.PutRequest{
Key: []byte("foo"),
Value: []byte("bar"), // this will be automatically base64-encoded by Go
})
if err != nil {
cx.t.Fatal(err)
}

// Create a curl request with a client api version header that contains invalid utf8 string
req := e2e.CURLReq{
Endpoint: path.Join(p, "/kv/put"),
Header: "client-api-version: hello\xff\xfe\xfdworld",
Value: string(putData),
Expected: "revision",
}

// Post request and expect no errors as etcd should not panic due to this
if err := e2e.CURLPost(cx.epc, req); err != nil {
cx.t.Fatalf("failed (%s) (%v)", p, err)
}
}
53 changes: 53 additions & 0 deletions tests/integration/clientv3/kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"

"go.etcd.io/etcd/api/v3/mvccpb"
Expand Down Expand Up @@ -896,3 +897,55 @@ func TestBalancerSupportLearner(t *testing.T) {
t.Errorf("expect no error (balancer should retry when request to learner fails), got error: %v", err)
}
}

func TestCtxClientVersionOverwrite(t *testing.T) {

// Initialize integration test framework
integration2.BeforeTest(t)

// Create a new single node cluster
clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
defer clus.Terminate(t)

// Create a client request with an invalid client api version
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = metadata.NewIncomingContext(ctx, metadata.New(map[string]string{rpctypes.MetadataClientAPIVersionKey: "hello\xff\xfe\xfdworld"}))
_, err := clus.Members[0].Client.Put(ctx, "/foo", "bar")

// We should see no error because downstream call stack for clientv3
// should overwrite the invalid version created in the ctx here as part of
// `client/v3/ctx.go:func withVersion`
if err != nil {
t.Fatalf("Expected no error (clientv3 should be overwriting clientapiversion): %v", err)
}
}

func TestInvalidUTF8ClientVersionRejection(t *testing.T) {

// Initialize integration test framework
integration2.BeforeTest(t)

// Create a new single node cluster
clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
defer clus.Terminate(t)

// Set an invalid API version for the client to rely on
invalidVersion := []byte{0x01, 0x02, 0x03}
version.APIVersion = string(invalidVersion)

// Create a client request with an invalid client api version
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
_, err := clus.Members[0].Client.Put(ctx, "/foo", "bar")
defer cancel()

// We should see a grpc internal error because downstream call stack for clientv3
// should reject any headers that contain non printable characters.
// Refer grpc stream.go call to imetadata.Validate(md)
if err == nil {
t.Fatalf("A client with non printable characters %v in api version should create an error", invalidVersion)
}
if err != nil {
t.Logf("Error returned is: %v", err)
}
}

0 comments on commit 1562124

Please sign in to comment.