Skip to content

Commit be3ac71

Browse files
committed
fix(router): check persistent query hashes to be well-formed
The OperationKit.unmarshalOperation method checks if non-empty sha256 hash in GraphQLRequestExtensions.PersistedQuery.Sha256Hash is valid. It rejects hex strings that could not be sha256 hash and raises en error that it cannot parse the body. Tests now deal only with valid hash values.
1 parent 9bbdfbb commit be3ac71

12 files changed

+97
-27
lines changed

router-tests/automatic_persisted_queries_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func TestAutomaticPersistedQueries(t *testing.T) {
3535
},
3636
}, func(t *testing.T, xEnv *testenv.Environment) {
3737
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{
38-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "does-not-exist"}}`),
38+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + cacheHashNotStored + `"}}`),
3939
})
4040
require.Equal(t, `{"errors":[{"message":"PersistedQueryNotFound","extensions":{"code":"PERSISTED_QUERY_NOT_FOUND"}}]}`, res.Body)
4141
})
@@ -198,7 +198,7 @@ func TestAutomaticPersistedQueries(t *testing.T) {
198198
},
199199
}, func(t *testing.T, xEnv *testenv.Environment) {
200200
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{
201-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "does-not-exist"}}`),
201+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + cacheHashNotStored + `"}}`),
202202
})
203203
require.Equal(t, `{"errors":[{"message":"PersistedQueryNotFound","extensions":{"code":"PERSISTED_QUERY_NOT_FOUND"}}]}`, res.Body)
204204
})

router-tests/header_propagation_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func TestCacheControl(t *testing.T) {
4848
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{
4949
Query: "", // Empty query
5050
Variables: json.RawMessage(`{}`),
51-
Extensions: json.RawMessage(`{"persistedQuery": {"version": 1, "sha256Hash": "invalid-hash"}}`),
51+
Extensions: json.RawMessage(`{"persistedQuery": {"version": 1, "sha256Hash": "` + cacheHashNotStored + `"}}`),
5252
})
5353

5454
require.Contains(t, res.Body, "PERSISTED_QUERY_NOT_FOUND")

router-tests/persisted_operations_over_get_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestPersistedOperationOverGET(t *testing.T) {
2323
header := make(http.Header)
2424
header.Add("graphql-client-name", "my-client")
2525
res, err := xEnv.MakeGraphQLRequestOverGET(testenv.GraphQLRequest{
26-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "does-not-exist"}}`),
26+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + cacheHashNotStored + `"}}`),
2727
Header: header,
2828
})
2929
require.NoError(t, err)
@@ -99,7 +99,7 @@ func TestAutomatedPersistedQueriesOverGET(t *testing.T) {
9999
header := make(http.Header)
100100
header.Add("graphql-client-name", "my-client")
101101
res, err := xEnv.MakeGraphQLRequestOverGET(testenv.GraphQLRequest{
102-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "does-not-exist"}}`),
102+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + cacheHashNotStored + `"}}`),
103103
Header: header,
104104
})
105105
require.NoError(t, err)

router-tests/persisted_operations_test.go

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,56 @@ import (
1616
"github.com/wundergraph/cosmo/router/pkg/config"
1717
)
1818

19+
// cacheHashNotStored is a constant representing a non-existent entry in Persisted Queries Cache
20+
// of the testenv. All the pre-stored persistent entries used in testing are located here:
21+
// ./testenv/testdata/cdn/organization/graph/operations/my-client
22+
const cacheHashNotStored = "0000000000000000000000000000000000000000000000000000000000000000"
23+
1924
func TestPersistedOperationNotFound(t *testing.T) {
2025
t.Parallel()
2126

2227
testenv.Run(t, &testenv.Config{}, func(t *testing.T, xEnv *testenv.Environment) {
2328
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{
24-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "does-not-exist"}}`),
29+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + cacheHashNotStored + `"}}`),
2530
})
2631
require.Equal(t, res.Response.Header.Get("Content-Type"), "application/json; charset=utf-8")
2732
require.Equal(t, `{"errors":[{"message":"PersistedQueryNotFound","extensions":{"code":"PERSISTED_QUERY_NOT_FOUND"}}]}`, res.Body)
2833
})
2934
}
3035

36+
func TestPersistedOperationInvalidHash(t *testing.T) {
37+
t.Parallel()
38+
39+
malformed := []string{
40+
"malformed",
41+
"some-words-free-style-not-allowed",
42+
"../../../path/not/ok",
43+
// too short
44+
"000000000000000000000000000000000000000000000000000000000000000",
45+
"00000000000000000000000000000000000000000000000000000000000000",
46+
"0000",
47+
"0",
48+
// too long
49+
"0000000000000000000000000000000000000000000000000000000000000000000000000000000",
50+
"0000000000000000000000000000000000000000000000000000000000000001z",
51+
// 1 bad character spoils everything
52+
"0000000000000z00000000000000000000000000000000000000000000000000",
53+
"0000000000000000000000000000000000000z00000000000000000000000000",
54+
"000000000000000000000000000000000000000000000000000000000000000z",
55+
}
56+
for _, hash := range malformed {
57+
testenv.Run(t, &testenv.Config{}, func(t *testing.T, xEnv *testenv.Environment) {
58+
res, err := xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
59+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + hash + `"}}`),
60+
})
61+
require.NoError(t, err)
62+
require.Equal(t, http.StatusBadRequest, res.Response.StatusCode)
63+
require.Equal(t, res.Response.Header.Get("Content-Type"), "application/json; charset=utf-8")
64+
require.Equal(t, `{"errors":[{"message":"error parsing request body"}]}`, res.Body)
65+
})
66+
}
67+
}
68+
3169
func TestPersistedOperation(t *testing.T) {
3270
t.Parallel()
3371

@@ -440,11 +478,12 @@ func TestPersistedOperationsWithNestedVariablesExtraction(t *testing.T) {
440478
t.Parallel()
441479

442480
testenv.Run(t, &testenv.Config{}, func(t *testing.T, xEnv *testenv.Environment) {
481+
nestedVariableExtraction := "5000000000000000000000000000000000000000000000000000000000000000"
443482
header := make(http.Header)
444483
header.Add("graphql-client-name", "my-client")
445484
res, err := xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
446485
OperationName: []byte(`"NormalizationQuery"`),
447-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "nestedVariableExtraction"}}`),
486+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + nestedVariableExtraction + `"}}`),
448487
Header: header,
449488
Variables: []byte(`{"arg":"a"}`),
450489
})
@@ -453,7 +492,7 @@ func TestPersistedOperationsWithNestedVariablesExtraction(t *testing.T) {
453492
require.Equal(t, `{"data":{"rootFieldWithListOfInputArg":[{"arg":"a"}]}}`, res.Body)
454493
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
455494
OperationName: []byte(`"NormalizationQuery"`),
456-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "nestedVariableExtraction"}}`),
495+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + nestedVariableExtraction + `"}}`),
457496
Header: header,
458497
Variables: []byte(`{"arg":"a"}`),
459498
})
@@ -462,7 +501,7 @@ func TestPersistedOperationsWithNestedVariablesExtraction(t *testing.T) {
462501
require.Equal(t, `{"data":{"rootFieldWithListOfInputArg":[{"arg":"a"}]}}`, res.Body)
463502
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
464503
OperationName: []byte(`"NormalizationQuery"`),
465-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "nestedVariableExtraction"}}`),
504+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + nestedVariableExtraction + `"}}`),
466505
Header: header,
467506
Variables: []byte(`{"arg":"b"}`),
468507
})
@@ -476,11 +515,12 @@ func TestPersistedOperationCacheWithVariablesAndDefaultValues(t *testing.T) {
476515
t.Parallel()
477516

478517
testenv.Run(t, &testenv.Config{}, func(t *testing.T, xEnv *testenv.Environment) {
518+
skipVariableWithDefault := "4000000000000000000000000000000000000000000000000000000000000000"
479519
header := make(http.Header)
480520
header.Add("graphql-client-name", "my-client")
481521
res, err := xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
482522
OperationName: []byte(`"MyQuery"`),
483-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "skipVariableWithDefault"}}`),
523+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + skipVariableWithDefault + `"}}`),
484524
Header: header,
485525
Variables: []byte(`{}`),
486526
})
@@ -489,7 +529,7 @@ func TestPersistedOperationCacheWithVariablesAndDefaultValues(t *testing.T) {
489529
require.Equal(t, `{"data":{"employee":{"details":{"forename":"Jens","surname":"Neuse"}}}}`, res.Body)
490530
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
491531
OperationName: []byte(`"MyQuery"`),
492-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "skipVariableWithDefault"}}`),
532+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + skipVariableWithDefault + `"}}`),
493533
Header: header,
494534
Variables: []byte(`{}`),
495535
})
@@ -498,7 +538,7 @@ func TestPersistedOperationCacheWithVariablesAndDefaultValues(t *testing.T) {
498538
require.Equal(t, `{"data":{"employee":{"details":{"forename":"Jens","surname":"Neuse"}}}}`, res.Body)
499539
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
500540
OperationName: []byte(`"MyQuery"`),
501-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "skipVariableWithDefault"}}`),
541+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + skipVariableWithDefault + `"}}`),
502542
Header: header,
503543
Variables: []byte(`{"yes":false}`),
504544
})
@@ -507,7 +547,7 @@ func TestPersistedOperationCacheWithVariablesAndDefaultValues(t *testing.T) {
507547
require.Equal(t, "MISS", res.Response.Header.Get(core.PersistedOperationCacheHeader))
508548
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
509549
OperationName: []byte(`"MyQuery"`),
510-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "skipVariableWithDefault"}}`),
550+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + skipVariableWithDefault + `"}}`),
511551
Header: header,
512552
Variables: []byte(`{"yes":false}`),
513553
})
@@ -516,7 +556,7 @@ func TestPersistedOperationCacheWithVariablesAndDefaultValues(t *testing.T) {
516556
require.Equal(t, "HIT", res.Response.Header.Get(core.PersistedOperationCacheHeader))
517557
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
518558
OperationName: []byte(`"MyQuery"`),
519-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "skipVariableWithDefault"}}`),
559+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + skipVariableWithDefault + `"}}`),
520560
Header: header,
521561
Variables: []byte(`{"yes":true}`),
522562
})
@@ -525,7 +565,7 @@ func TestPersistedOperationCacheWithVariablesAndDefaultValues(t *testing.T) {
525565
require.Equal(t, "MISS", res.Response.Header.Get(core.PersistedOperationCacheHeader))
526566
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
527567
OperationName: []byte(`"MyQuery"`),
528-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "skipVariableWithDefault"}}`),
568+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + skipVariableWithDefault + `"}}`),
529569
Header: header,
530570
Variables: []byte(`{"yes":true}`),
531571
})
@@ -539,11 +579,12 @@ func TestPersistedOperationCacheWithVariablesCoercion(t *testing.T) {
539579
t.Parallel()
540580

541581
testenv.Run(t, &testenv.Config{}, func(t *testing.T, xEnv *testenv.Environment) {
582+
listArgQuery := "1000000000000000000000000000000000000000000000000000000000000000"
542583
header := make(http.Header)
543584
header.Add("graphql-client-name", "my-client")
544585
res, err := xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
545586
OperationName: []byte(`"MyQuery"`),
546-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQuery"}}`),
587+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQuery + `"}}`),
547588
Header: header,
548589
Variables: []byte(`{"arg": "a"}`),
549590
})
@@ -552,7 +593,7 @@ func TestPersistedOperationCacheWithVariablesCoercion(t *testing.T) {
552593
require.Equal(t, "MISS", res.Response.Header.Get(core.PersistedOperationCacheHeader))
553594
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
554595
OperationName: []byte(`"MyQuery"`),
555-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQuery"}}`),
596+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQuery + `"}}`),
556597
Header: header,
557598
Variables: []byte(`{"arg": "a"}`),
558599
})
@@ -561,17 +602,18 @@ func TestPersistedOperationCacheWithVariablesCoercion(t *testing.T) {
561602
require.Equal(t, "HIT", res.Response.Header.Get(core.PersistedOperationCacheHeader))
562603
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
563604
OperationName: []byte(`"MyQuery"`),
564-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQuery"}}`),
605+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQuery + `"}}`),
565606
Header: header,
566607
Variables: []byte(`{"arg": "b"}`),
567608
})
568609
require.NoError(t, err)
569610
require.Equal(t, `{"data":{"rootFieldWithListArg":["b"]}}`, res.Body)
570611
require.Equal(t, "HIT", res.Response.Header.Get(core.PersistedOperationCacheHeader))
571612

613+
listArgQueryWithDefault := "2000000000000000000000000000000000000000000000000000000000000000"
572614
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
573615
OperationName: []byte(`"MyQuery"`),
574-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQueryWithDefault"}}`),
616+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQueryWithDefault + `"}}`),
575617
Header: header,
576618
Variables: []byte(`{}`),
577619
})
@@ -580,7 +622,7 @@ func TestPersistedOperationCacheWithVariablesCoercion(t *testing.T) {
580622
require.Equal(t, "MISS", res.Response.Header.Get(core.PersistedOperationCacheHeader))
581623
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
582624
OperationName: []byte(`"MyQuery"`),
583-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQueryWithDefault"}}`),
625+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQueryWithDefault + `"}}`),
584626
Header: header,
585627
Variables: []byte(`{}`),
586628
})
@@ -589,7 +631,7 @@ func TestPersistedOperationCacheWithVariablesCoercion(t *testing.T) {
589631
require.Equal(t, "HIT", res.Response.Header.Get(core.PersistedOperationCacheHeader))
590632
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
591633
OperationName: []byte(`"MyQuery"`),
592-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQueryWithDefault"}}`),
634+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQueryWithDefault + `"}}`),
593635
Header: header,
594636
Variables: []byte(`{"arg": "b"}`),
595637
})
@@ -598,18 +640,19 @@ func TestPersistedOperationCacheWithVariablesCoercion(t *testing.T) {
598640
require.Equal(t, "HIT", res.Response.Header.Get(core.PersistedOperationCacheHeader))
599641
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
600642
OperationName: []byte(`"MyQuery"`),
601-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQueryWithDefault"}}`),
643+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQueryWithDefault + `"}}`),
602644
Header: header,
603645
Variables: []byte(`{"arg": ["c"]}`),
604646
})
605647
require.NoError(t, err)
606648
require.Equal(t, `{"data":{"rootFieldWithListArg":["c"]}}`, res.Body)
607649
require.Equal(t, "HIT", res.Response.Header.Get(core.PersistedOperationCacheHeader))
608650

651+
nestedEnum := "3000000000000000000000000000000000000000000000000000000000000000"
609652
// nested list of enums
610653
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
611654
OperationName: []byte(`"MyQuery"`),
612-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "nestedEnum"}}`),
655+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + nestedEnum + `"}}`),
613656
Header: header,
614657
Variables: []byte(`{"arg":{"enums":"A"}}`),
615658
})
@@ -618,7 +661,7 @@ func TestPersistedOperationCacheWithVariablesCoercion(t *testing.T) {
618661
require.Equal(t, "MISS", res.Response.Header.Get(core.PersistedOperationCacheHeader))
619662
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
620663
OperationName: []byte(`"MyQuery"`),
621-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "nestedEnum"}}`),
664+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + nestedEnum + `"}}`),
622665
Header: header,
623666
Variables: []byte(`{"arg":{"enums":"B"}}`),
624667
})

router-tests/telemetry/telemetry_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4039,11 +4039,12 @@ func TestFlakyTelemetry(t *testing.T) {
40394039
TraceExporter: exporter,
40404040
MetricReader: metricReader,
40414041
}, func(t *testing.T, xEnv *testenv.Environment) {
4042+
listArgQuery := "1000000000000000000000000000000000000000000000000000000000000000"
40424043
header := make(http.Header)
40434044
header.Add("graphql-client-name", "my-client")
40444045
res, err := xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
40454046
OperationName: []byte(`"MyQuery"`),
4046-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQuery"}}`),
4047+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQuery + `"}}`),
40474048
Header: header,
40484049
Variables: []byte(`{"arg": "a"}`),
40494050
})
@@ -4071,7 +4072,7 @@ func TestFlakyTelemetry(t *testing.T) {
40714072

40724073
res, err = xEnv.MakeGraphQLRequest(testenv.GraphQLRequest{
40734074
OperationName: []byte(`"MyQuery"`),
4074-
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "listArgQuery"}}`),
4075+
Extensions: []byte(`{"persistedQuery": {"version": 1, "sha256Hash": "` + listArgQuery + `"}}`),
40754076
Header: header,
40764077
Variables: []byte(`{"arg": "a"}`),
40774078
})

0 commit comments

Comments
 (0)