From 2f289660958b74736c6dcfabff74f73ffdb471e9 Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Fri, 20 Feb 2026 23:26:49 +0100 Subject: [PATCH 1/5] chore(deps): update graphql-go-tools to c96ac9e6f088 Fix mutation-during-iteration SIGSEGV in optionallyAllowCustomExtensionProperties. Upstream PR: https://github.com/wundergraph/graphql-go-tools/pull/1402 Co-Authored-By: Claude Opus 4.6 --- router-tests/go.mod | 2 +- router-tests/go.sum | 2 ++ router/go.mod | 2 +- router/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/router-tests/go.mod b/router-tests/go.mod index 329e843568..635ce4ca34 100644 --- a/router-tests/go.mod +++ b/router-tests/go.mod @@ -27,7 +27,7 @@ require ( github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects v0.0.0-20250715110703-10f2e5f9c79e github.com/wundergraph/cosmo/router v0.0.0-20260213130455-6e3277e7b850 github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e - github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 + github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 go.opentelemetry.io/otel v1.36.0 go.opentelemetry.io/otel/sdk v1.36.0 go.opentelemetry.io/otel/sdk/metric v1.36.0 diff --git a/router-tests/go.sum b/router-tests/go.sum index 207458cb59..208f7bfaa1 100644 --- a/router-tests/go.sum +++ b/router-tests/go.sum @@ -358,6 +358,8 @@ github.com/wundergraph/go-arena v1.1.0 h1:9+wSRkJAkA2vbYHp6s8tEGhPViRGQNGXqPHT0Q github.com/wundergraph/go-arena v1.1.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw= github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 h1:yI9kKMx0zF0csxRhwMhH0vsomZDP92OZJPs5xAHos2U= github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 h1:JQXGiaW87OonVvPndBGrvtqIL/YxQUwLSEVLbmqsrPc= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= diff --git a/router/go.mod b/router/go.mod index da722b008e..7b690f8540 100644 --- a/router/go.mod +++ b/router/go.mod @@ -31,7 +31,7 @@ require ( github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/twmb/franz-go v1.16.1 - github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 + github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 // Do not upgrade, it renames attributes we rely on go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 go.opentelemetry.io/contrib/propagators/b3 v1.23.0 diff --git a/router/go.sum b/router/go.sum index 1ee5d504f8..04a96e96b0 100644 --- a/router/go.sum +++ b/router/go.sum @@ -326,8 +326,8 @@ github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLV github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw= github.com/wundergraph/go-arena v1.1.0 h1:9+wSRkJAkA2vbYHp6s8tEGhPViRGQNGXqPHT0QzhdIc= github.com/wundergraph/go-arena v1.1.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 h1:yI9kKMx0zF0csxRhwMhH0vsomZDP92OZJPs5xAHos2U= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 h1:JQXGiaW87OonVvPndBGrvtqIL/YxQUwLSEVLbmqsrPc= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= From d5bbbb3fe09c5ae0956ae97f9cf010608d4df79d Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Sat, 21 Feb 2026 09:02:19 +0100 Subject: [PATCH 2/5] chore(deps): update graphql-go-tools to v2.0.0-rc.254 Co-Authored-By: Claude Opus 4.6 --- router-tests/go.mod | 2 +- router-tests/go.sum | 2 -- router/go.mod | 2 +- router/go.sum | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/router-tests/go.mod b/router-tests/go.mod index 635ce4ca34..329e843568 100644 --- a/router-tests/go.mod +++ b/router-tests/go.mod @@ -27,7 +27,7 @@ require ( github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects v0.0.0-20250715110703-10f2e5f9c79e github.com/wundergraph/cosmo/router v0.0.0-20260213130455-6e3277e7b850 github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e - github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 + github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 go.opentelemetry.io/otel v1.36.0 go.opentelemetry.io/otel/sdk v1.36.0 go.opentelemetry.io/otel/sdk/metric v1.36.0 diff --git a/router-tests/go.sum b/router-tests/go.sum index 208f7bfaa1..207458cb59 100644 --- a/router-tests/go.sum +++ b/router-tests/go.sum @@ -358,8 +358,6 @@ github.com/wundergraph/go-arena v1.1.0 h1:9+wSRkJAkA2vbYHp6s8tEGhPViRGQNGXqPHT0Q github.com/wundergraph/go-arena v1.1.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw= github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 h1:yI9kKMx0zF0csxRhwMhH0vsomZDP92OZJPs5xAHos2U= github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 h1:JQXGiaW87OonVvPndBGrvtqIL/YxQUwLSEVLbmqsrPc= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= diff --git a/router/go.mod b/router/go.mod index 7b690f8540..da722b008e 100644 --- a/router/go.mod +++ b/router/go.mod @@ -31,7 +31,7 @@ require ( github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/twmb/franz-go v1.16.1 - github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 + github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 // Do not upgrade, it renames attributes we rely on go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 go.opentelemetry.io/contrib/propagators/b3 v1.23.0 diff --git a/router/go.sum b/router/go.sum index 04a96e96b0..1ee5d504f8 100644 --- a/router/go.sum +++ b/router/go.sum @@ -326,8 +326,8 @@ github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLV github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw= github.com/wundergraph/go-arena v1.1.0 h1:9+wSRkJAkA2vbYHp6s8tEGhPViRGQNGXqPHT0QzhdIc= github.com/wundergraph/go-arena v1.1.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088 h1:JQXGiaW87OonVvPndBGrvtqIL/YxQUwLSEVLbmqsrPc= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254.0.20260220220527-c96ac9e6f088/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 h1:yI9kKMx0zF0csxRhwMhH0vsomZDP92OZJPs5xAHos2U= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= From 10f8a97b982095005ea110f0af40c5d15c023ef8 Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Sat, 21 Feb 2026 20:13:14 +0100 Subject: [PATCH 3/5] test(router): add integration tests for extension field filtering SIGSEGV Add tests that reproduce the mutation-during-iteration crash in optionallyAllowCustomExtensionProperties when filtering multiple disallowed extension fields from subgraph error responses. Co-Authored-By: Claude Opus 4.6 --- router-tests/error_handling_test.go | 99 +++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/router-tests/error_handling_test.go b/router-tests/error_handling_test.go index aa9388022d..58a140fd1e 100644 --- a/router-tests/error_handling_test.go +++ b/router-tests/error_handling_test.go @@ -7,6 +7,7 @@ import ( "net/http" "slices" "strings" + "sync" "testing" "github.com/stretchr/testify/require" @@ -335,6 +336,104 @@ func TestAllowedExtensions(t *testing.T) { }) }) + t.Run("filtering multiple disallowed extension fields does not crash / wrapped mode", func(t *testing.T) { + testenv.Run(t, &testenv.Config{ + ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { + cfg.Enabled = true + cfg.Mode = config.SubgraphErrorPropagationModeWrapped + cfg.AllowedExtensionFields = []string{"code"} + }, + Subgraphs: testenv.SubgraphsConfig{ + Employees: testenv.SubgraphConfig{ + Middleware: func(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, wErr := w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) + require.NoError(t, wErr) + }) + }, + }, + }, + }, func(t *testing.T, xEnv *testenv.Environment) { + res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{ + Query: `{ employees { id } }`, + }) + require.Equal(t, `{"errors":[{"message":"Failed to fetch from Subgraph 'employees'.","extensions":{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED"}}],"statusCode":403}}],"data":{"employees":null}}`, res.Body) + }) + }) + + t.Run("filtering multiple disallowed extension fields does not crash / passthrough mode", func(t *testing.T) { + testenv.Run(t, &testenv.Config{ + ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { + cfg.Enabled = true + cfg.Mode = config.SubgraphErrorPropagationModePassthrough + cfg.AllowedExtensionFields = []string{"code"} + }, + Subgraphs: testenv.SubgraphsConfig{ + Employees: testenv.SubgraphConfig{ + Middleware: func(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, wErr := w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) + require.NoError(t, wErr) + }) + }, + }, + }, + }, func(t *testing.T, xEnv *testenv.Environment) { + res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{ + Query: `{ employees { id } }`, + }) + require.Equal(t, `{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","statusCode":403}}],"data":{"employees":null}}`, res.Body) + }) + }) + + t.Run("concurrent requests with extension field filtering does not crash", func(t *testing.T) { + testenv.Run(t, &testenv.Config{ + ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { + cfg.Enabled = true + cfg.Mode = config.SubgraphErrorPropagationModePassthrough + cfg.AllowedExtensionFields = []string{"code"} + }, + Subgraphs: testenv.SubgraphsConfig{ + Employees: testenv.SubgraphConfig{ + Middleware: func(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, wErr := w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) + require.NoError(t, wErr) + }) + }, + }, + }, + }, func(t *testing.T, xEnv *testenv.Environment) { + var ( + numOfOperations = 10 + ready, done sync.WaitGroup + ) + ready.Add(numOfOperations) + done.Add(numOfOperations) + trigger := make(chan struct{}) + for i := 0; i < numOfOperations; i++ { + go func() { + ready.Done() + defer done.Done() + <-trigger + res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{ + Query: `{ employees { id } }`, + }) + require.Equal(t, `{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","statusCode":403}}],"data":{"employees":null}}`, res.Body) + }() + } + ready.Wait() + close(trigger) + done.Wait() + }) + }) + } func TestErrorPropagation(t *testing.T) { From 62d5e2b747030f16f988598f16e0dba9488ad124 Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Sat, 21 Feb 2026 20:16:43 +0100 Subject: [PATCH 4/5] test(router): add extension filtering variants for no/partial/full filtering Three new test cases with 3 allowed fields: - all subgraph fields match allowed list (nothing filtered) - subgraph has extra fields beyond allowed list (some filtered) - no subgraph fields match allowed list (all filtered) Co-Authored-By: Claude Opus 4.6 --- router-tests/error_handling_test.go | 101 +++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/router-tests/error_handling_test.go b/router-tests/error_handling_test.go index 58a140fd1e..234952b6b6 100644 --- a/router-tests/error_handling_test.go +++ b/router-tests/error_handling_test.go @@ -336,7 +336,7 @@ func TestAllowedExtensions(t *testing.T) { }) }) - t.Run("filtering multiple disallowed extension fields does not crash / wrapped mode", func(t *testing.T) { + t.Run("filtering multiple disallowed extension fields / wrapped mode", func(t *testing.T) { testenv.Run(t, &testenv.Config{ ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { cfg.Enabled = true @@ -349,8 +349,7 @@ func TestAllowedExtensions(t *testing.T) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusForbidden) - _, wErr := w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) - require.NoError(t, wErr) + _, _ = w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) }) }, }, @@ -363,7 +362,7 @@ func TestAllowedExtensions(t *testing.T) { }) }) - t.Run("filtering multiple disallowed extension fields does not crash / passthrough mode", func(t *testing.T) { + t.Run("filtering multiple disallowed extension fields / passthrough mode", func(t *testing.T) { testenv.Run(t, &testenv.Config{ ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { cfg.Enabled = true @@ -376,8 +375,7 @@ func TestAllowedExtensions(t *testing.T) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusForbidden) - _, wErr := w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) - require.NoError(t, wErr) + _, _ = w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) }) }, }, @@ -390,7 +388,85 @@ func TestAllowedExtensions(t *testing.T) { }) }) - t.Run("concurrent requests with extension field filtering does not crash", func(t *testing.T) { + t.Run("allowing all three extension fields filters nothing / passthrough mode", func(t *testing.T) { + testenv.Run(t, &testenv.Config{ + ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { + cfg.Enabled = true + cfg.Mode = config.SubgraphErrorPropagationModePassthrough + cfg.AllowedExtensionFields = []string{"code", "reason", "details"} + }, + Subgraphs: testenv.SubgraphsConfig{ + Employees: testenv.SubgraphConfig{ + Middleware: func(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","reason":"expired","details":"token expired"}}]}`)) + }) + }, + }, + }, + }, func(t *testing.T, xEnv *testenv.Environment) { + res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{ + Query: `{ employees { id } }`, + }) + require.Equal(t, `{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","reason":"expired","details":"token expired","statusCode":403}}],"data":{"employees":null}}`, res.Body) + }) + }) + + t.Run("allowing three fields filters some when extras present / passthrough mode", func(t *testing.T) { + testenv.Run(t, &testenv.Config{ + ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { + cfg.Enabled = true + cfg.Mode = config.SubgraphErrorPropagationModePassthrough + cfg.AllowedExtensionFields = []string{"code", "reason", "details"} + }, + Subgraphs: testenv.SubgraphsConfig{ + Employees: testenv.SubgraphConfig{ + Middleware: func(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","reason":"expired","details":"token expired","internal":"secret","trace_id":"abc123"}}]}`)) + }) + }, + }, + }, + }, func(t *testing.T, xEnv *testenv.Environment) { + res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{ + Query: `{ employees { id } }`, + }) + require.Equal(t, `{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","reason":"expired","details":"token expired","statusCode":403}}],"data":{"employees":null}}`, res.Body) + }) + }) + + t.Run("allowing three fields filters all when none match / passthrough mode", func(t *testing.T) { + testenv.Run(t, &testenv.Config{ + ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { + cfg.Enabled = true + cfg.Mode = config.SubgraphErrorPropagationModePassthrough + cfg.AllowedExtensionFields = []string{"code", "reason", "details"} + }, + Subgraphs: testenv.SubgraphsConfig{ + Employees: testenv.SubgraphConfig{ + Middleware: func(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"internal":"secret","trace_id":"abc123","debug":"info"}}]}`)) + }) + }, + }, + }, + }, func(t *testing.T, xEnv *testenv.Environment) { + res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{ + Query: `{ employees { id } }`, + }) + require.Equal(t, `{"errors":[{"message":"Unauthorized","extensions":{"statusCode":403}}],"data":{"employees":null}}`, res.Body) + }) + }) + + t.Run("concurrent requests with extension field filtering", func(t *testing.T) { testenv.Run(t, &testenv.Config{ ModifySubgraphErrorPropagation: func(cfg *config.SubgraphErrorPropagationConfiguration) { cfg.Enabled = true @@ -403,8 +479,7 @@ func TestAllowedExtensions(t *testing.T) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusForbidden) - _, wErr := w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) - require.NoError(t, wErr) + _, _ = w.Write([]byte(`{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","foo":"bar","baz":"qux","extra1":"val1","extra2":"val2"}}]}`)) }) }, }, @@ -414,6 +489,8 @@ func TestAllowedExtensions(t *testing.T) { numOfOperations = 10 ready, done sync.WaitGroup ) + expected := `{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","statusCode":403}}],"data":{"employees":null}}` + results := make(chan string, numOfOperations) ready.Add(numOfOperations) done.Add(numOfOperations) trigger := make(chan struct{}) @@ -425,12 +502,16 @@ func TestAllowedExtensions(t *testing.T) { res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{ Query: `{ employees { id } }`, }) - require.Equal(t, `{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHORIZED","statusCode":403}}],"data":{"employees":null}}`, res.Body) + results <- res.Body }() } ready.Wait() close(trigger) done.Wait() + close(results) + for body := range results { + require.Equal(t, expected, body) + } }) }) From fb9082787374eccdc791e6c3b5993bed110c6f3a Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Sat, 21 Feb 2026 22:02:50 +0100 Subject: [PATCH 5/5] chore(deps): update graphql-go-tools to v2.0.0-rc.255 Co-Authored-By: Claude Opus 4.6 --- router-tests/go.mod | 2 +- router-tests/go.sum | 4 ++-- router/go.mod | 2 +- router/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/router-tests/go.mod b/router-tests/go.mod index 329e843568..28d13da9e5 100644 --- a/router-tests/go.mod +++ b/router-tests/go.mod @@ -27,7 +27,7 @@ require ( github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects v0.0.0-20250715110703-10f2e5f9c79e github.com/wundergraph/cosmo/router v0.0.0-20260213130455-6e3277e7b850 github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e - github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 + github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.255 go.opentelemetry.io/otel v1.36.0 go.opentelemetry.io/otel/sdk v1.36.0 go.opentelemetry.io/otel/sdk/metric v1.36.0 diff --git a/router-tests/go.sum b/router-tests/go.sum index 207458cb59..8740bb0404 100644 --- a/router-tests/go.sum +++ b/router-tests/go.sum @@ -356,8 +356,8 @@ github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLV github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw= github.com/wundergraph/go-arena v1.1.0 h1:9+wSRkJAkA2vbYHp6s8tEGhPViRGQNGXqPHT0QzhdIc= github.com/wundergraph/go-arena v1.1.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 h1:yI9kKMx0zF0csxRhwMhH0vsomZDP92OZJPs5xAHos2U= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.255 h1:lN+D5OWay3U1mwtRlA+j7kJqP5ksKdRFMvYA+8XLJ1E= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.255/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= diff --git a/router/go.mod b/router/go.mod index da722b008e..fde3dffcb3 100644 --- a/router/go.mod +++ b/router/go.mod @@ -31,7 +31,7 @@ require ( github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/twmb/franz-go v1.16.1 - github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 + github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.255 // Do not upgrade, it renames attributes we rely on go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 go.opentelemetry.io/contrib/propagators/b3 v1.23.0 diff --git a/router/go.sum b/router/go.sum index 1ee5d504f8..b3f1a180a2 100644 --- a/router/go.sum +++ b/router/go.sum @@ -326,8 +326,8 @@ github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLV github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw= github.com/wundergraph/go-arena v1.1.0 h1:9+wSRkJAkA2vbYHp6s8tEGhPViRGQNGXqPHT0QzhdIc= github.com/wundergraph/go-arena v1.1.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254 h1:yI9kKMx0zF0csxRhwMhH0vsomZDP92OZJPs5xAHos2U= -github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.254/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.255 h1:lN+D5OWay3U1mwtRlA+j7kJqP5ksKdRFMvYA+8XLJ1E= +github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.255/go.mod h1:gfmmrPd2khZONmwYE8RIfnGjwIG+RqL52jYiBzcUST8= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=