Skip to content

feat: add bypassIfValuesNull to openfed__subscriptionFilter#2815

Draft
jensneuse wants to merge 4 commits intomainfrom
streams-bypass-filtering-techniques
Draft

feat: add bypassIfValuesNull to openfed__subscriptionFilter#2815
jensneuse wants to merge 4 commits intomainfrom
streams-bypass-filtering-techniques

Conversation

@jensneuse
Copy link
Copy Markdown
Member

@jensneuse jensneuse commented May 4, 2026

Summary

Adds an optional bypassIfValuesNull: Boolean field to openfed__SubscriptionFieldCondition. When set to true, an IN condition whose templated value resolves to nil or GraphQL null evaluates as match instead of skip — letting customers express "no filter when the argument is absent or null" for optional subscription arguments.

Default is false; existing behavior is unchanged.

Linear: ENG-9357. Customer ask: Pylon #2869 (Openphone).

Customer SDL after this change:

type Subscription {
  orderReceived(product: String): Order!
    @openfed__subscriptionFilter(condition: {
      IN: {
        fieldPath: "productName"
        values: ["{{ args.product }}"]
        bypassIfValuesNull: true
      }
    })
}

Status: 🚧 Draft — blocked on upstream

Depends on wundergraph/graphql-go-tools#1484. The router currently consumes that PR's head commit (5a00844995b5) via go.mod version pin.

The PR is rebased on graphql-go-tools master (post-v2.0.0), which has API changes the cosmo router has not yet absorbed. Two known breakages:

  • plan.FieldWeightplan.FieldCost (mechanical rename, fixed in this PR at router/core/factoryresolver.go).
  • resolve.SubscriptionCloseKind and SubscriptionCloseKindNormal were removed; SubscriptionResponseWriter.Close(SubscriptionCloseKind) became Complete(). This affects router/core/flushwriter.go, router/core/websocket.go, and the router/pkg/pubsub/datasource package (mocks + subscription_event_updater). Not addressed in this PR.

CI will be red until the upstream is rebased onto a compatible base, or the cosmo router is bumped to consume v2.0.0 master.

Changes

Layer What changed
proto/wg/cosmo/node/v1/node.proto New optional bool bypass_if_values_null = 3 on SubscriptionFieldCondition. Wire-compatible.
Generated proto (router/gen, connect-go, connect/src) Regenerated via pnpm generate + make generate-go + composition-go go generate.
composition/ New BYPASS_IF_VALUES_NULL constant; injected SDL field on openfed__SubscriptionFieldCondition; new BYPASS_IF_VALUES_NULL switch arm in validateSubscriptionFieldCondition (Boolean-only, dedup-checked, explicit false collapses to undefined); type updated in router-configuration/types.ts.
shared/src/router-config/graphql-configuration.ts generateSubscriptionFilterCondition only emits the proto field when condition.in.bypassIfValuesNull === true.
router/core/factoryresolver.go mapProtoFilterToPlanFilter propagates BypassIfValuesNull onto plan.SubscriptionFieldCondition.
router/go.mod, router-tests/go.mod Pinned to graphql-go-tools v2.1.1-0.20260504064838-5a00844995b5 (PR #1484 head).

Test plan

  • composition — 6 new tests cover bypassIfValuesNull: true preserved, false collapsed, omitted, propagated through nested OR, non-Boolean type rejected, duplicate field rejected. Full suite green.
  • shared — 4 new tests cover true emits, undefined leaves unset, explicit false leaves unset, propagation through nested OR. All green.
  • router/core — new factoryresolver_test.go: nil input, true/unset/explicit-false on IN, propagation through OR and AND > NOT. All green.
  • router-tests/events/nats_subscription_filter_bypass_test.go — 4 end-to-end NATS subscription cases:
    • explicit filterById matches event id
    • filterById set to non-matching value drops events
    • explicit null $filterById variable triggers bypass
    • declared $filterById variable absent from variables map triggers bypass
  • CI green — blocked on upstream API drift (see "Status").

Summary by CodeRabbit

  • New Features

    • Optional "bypass if values are null" for subscription filters — when enabled, filters are bypassed if their filter values are null or omitted, allowing broader event delivery.
  • Tests

    • Expanded unit and end-to-end tests covering bypass behavior, nested conditions, type validation, duplicate-field checks, and subscription event delivery scenarios.

Adds an optional `bypassIfValuesNull: Boolean` field to
`openfed__SubscriptionFieldCondition`. When set to `true`, an `IN`
condition whose templated value resolves to `nil` or GraphQL `null`
evaluates as match instead of skip, so customers with optional
subscription arguments can express "no filter when the argument is
absent or null".

Default `false`; existing behavior unchanged. The flag flows from
SDL through composition validation, the proto wire format, the shared
serializer, and the router's proto-to-plan mapper into the upstream
graphql-go-tools runtime where the bypass is evaluated as a preflight
before the event payload is consulted.

Coverage:
- composition: validate Boolean type, dedup, explicit `false`
  collapses to undefined, propagation through nested OR
- shared: serializer emits proto field only when explicitly `true`
- router/core: unit tests for `mapProtoFilterToPlanFilter` covering
  IN, OR, AND > NOT propagation
- router-tests/events: end-to-end NATS tests for explicit value match,
  non-matching value drops, explicit null variable bypass, and
  declared-but-absent variable bypass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

Walkthrough

Adds an optional boolean flag bypassIfValuesNull to subscription field conditions and propagates it across GraphQL input schemas, validation, protobuf definitions, TS types, plan mapping, config generation, and unit/integration tests.

Changes

Subscription Filter Bypass Feature

Layer / File(s) Summary
Data Shape: Type & Proto Definitions
composition/src/router-configuration/types.ts, proto/wg/cosmo/node/v1/node.proto, connect/src/wg/cosmo/node/v1/node_pb.ts
SubscriptionFieldCondition gains optional bypassIfValuesNull (TS type) and protobuf optional bool bypass_if_values_null = 3.
Constants & String Literals
composition/src/utils/string-constants.ts
Adds exported BYPASS_IF_VALUES_NULL = 'bypassIfValuesNull'.
Schema Definitions
composition/src/v1/constants/non-directive-definitions.ts, composition/tests/v1/utils/utils.ts
GraphQL input openfed__SubscriptionFieldCondition updated to include bypassIfValuesNull: Boolean; BOOLEAN_SCALAR and BYPASS_IF_VALUES_NULL imported where needed.
Validation & Federation
composition/src/v1/federation/federation-factory.ts, composition/tests/v1/directives/subscription-filter.test.ts
validateSubscriptionFieldCondition recognizes BYPASS_IF_VALUES_NULL, detects duplicates, enforces Boolean literal type, and sets condition.bypassIfValuesNull = true only for explicit true. Added federation tests and subgraph fixtures for success, nested propagation, invalid type, and duplicates.
Config Generation (TS → Proto)
shared/src/router-config/graphql-configuration.ts, shared/test/subscription-filter.test.ts
generateSubscriptionFilterCondition now sets protoMessage.in via a local fieldCondition and conditionally sets fieldCondition.bypassIfValuesNull = true when input is true. Added tests verifying proto generation and nested propagation.
Proto → Plan Mapping & Cost Mapping
router/core/factoryresolver.go, router/core/factoryresolver_test.go
mapProtoFilterToPlanFilter propagates protobuf BypassIfValuesNull into plan.SubscriptionFieldCondition.In. Cost mapping now builds *plan.FieldCost entries stored in out.CostConfig.Weights. Unit tests cover propagation, defaults, nil handling, and nested OR/AND-NOT scenarios.
End-to-End Routing Tests (NATS)
router-tests/events/nats_subscription_filter_bypass_test.go
Adds buildBypassIfValuesNullConfig and TestNatsSubscriptionFilterBypassIfValuesNull exercising explicit-match, omitted-variable (bypass), explicit-null (bypass), and non-match behaviors with bypassIfValuesNull: true.
Test Schema & Fixtures
composition/tests/v1/directives/subscription-filter.test.ts
Test input schema and new subgraphBypass1subgraphBypass5 fixtures added to drive success, propagation, invalid type, and duplicate cases.
Dependency / Module Updates
router-tests/go.mod, router/go.mod
Bump github.com/wundergraph/graphql-go-tools/v2 to v2.1.1-0.20260504064838-5a00844995b5.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature being added: a new bypassIfValuesNull field to the openfed__subscriptionFilter, matching the primary changes across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Review rate limit: 4/5 reviews remaining, refill in 12 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
router-tests/events/nats_subscription_filter_bypass_test.go (1)

145-151: ⚡ Quick win

Use testenv.WSWriteJSON for subscribe frames instead of raw conn.WriteJSON.

The current writes bypass the retry/deadline behavior used to stabilize websocket tests in this repo.

Suggested change
- err := conn.WriteJSON(&testenv.WebSocketMessage{
+ err := testenv.WSWriteJSON(t, conn, &testenv.WebSocketMessage{
     ID:   "1",
     Type: "subscribe",
     Payload: []byte(
       `{"query":"subscription { filteredEmployeeUpdated(id: 1, filterById: 1) { id } }"}`,
     ),
   })

As per coding guidelines: "Use testenv.WSReadJSON and testenv.WSWriteJSON for WebSocket reads and writes in tests instead of conn.ReadJSON and conn.WriteJSON, as these helpers include retry logic with 2-second deadlines and exponential backoff."

Also applies to: 205-211, 254-261, 302-308

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/events/nats_subscription_filter_bypass_test.go` around lines 145
- 151, Replace direct websocket writes that call conn.WriteJSON with the test
helper testenv.WSWriteJSON to ensure retries and deadlines are applied;
specifically update the subscription writes that currently invoke conn.WriteJSON
(e.g., the call creating a testenv.WebSocketMessage with Type "subscribe" and ID
"1") to use testenv.WSWriteJSON(conn, <message>) and apply the same replacement
for the other occurrences called out (around the blocks at the other mentioned
locations). Locate usages by searching for conn.WriteJSON calls that send
subscribe frames and swap them to testenv.WSWriteJSON so tests benefit from the
built-in 2s deadline and backoff.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@composition/src/v1/federation/federation-factory.ts`:
- Around line 2535-2559: The parser currently lets an IN condition pass
validation when only BYPASS_IF_VALUES_NULL is present because validFieldNames
leftovers don't flip hasErrors; after parsing objectValueNode.fields for the IN
condition (the switch handling BYPASS_IF_VALUES_NULL and other keys), explicitly
validate that required keys "fieldPath" and "values" are present (check
validFieldNames or track presence of fieldPath/values), and if either is missing
set hasErrors = true and push appropriate field error messages (similar to
invalidInputFieldTypeErrorMessage) so you cannot emit an IN filter with empty
fieldPath/values even when condition.bypassIfValuesNull is true; ensure
duplicatedFieldNames/hasSeen handling remains unchanged for
BYPASS_IF_VALUES_NULL and reference the BYPASS_IF_VALUES_NULL constant,
condition.bypassIfValuesNull, validFieldNames, hasErrors, and objectValueNode to
locate where to add this check.

In `@router-tests/events/nats_subscription_filter_bypass_test.go`:
- Around line 345-359: The goroutine uses conn.ReadJSON without a read deadline
which can block forever; replace that pattern by using testenv.WSReadJSON (which
respects timeouts) instead of manual conn.ReadJSON inside the anonymous
goroutine, or at minimum set a read deadline on conn via conn.SetReadDeadline
before calling conn.ReadJSON so the goroutine unblocks when the context timeout
(context.WithTimeout) fires; update the block that creates done := make(chan
testenv.WebSocketMessage, 1) and the goroutine that calls conn.ReadJSON to use
testenv.WSReadJSON (or add conn.SetReadDeadline) so the goroutine cannot leak.

In `@router-tests/go.mod`:
- Line 32: Update the pinned version of the module go.opentelemetry.io/otel/sdk
in both the require and replace sections to at least v1.40.0: locate the require
entry referencing go.opentelemetry.io/otel/sdk (currently v1.39.0) and change it
to v1.40.0 or later, and locate the replace entry for
go.opentelemetry.io/otel/sdk (currently v1.28.0) and bump that to the same
v1.40.0+ version so the transitive grpc v1.79.3 dependency no longer pulls an
older otel/sdk.

In `@router/go.mod`:
- Line 34: Update the go.mod entries so that the module
go.opentelemetry.io/otel/sdk is pinned to v1.40.0 or later in both the require
and replace directives (replace any existing v1.28.0 pin), and ensure the grpc
dependency google.golang.org/grpc is compatible (e.g., keep or set to v1.79.3)
so the otel/sdk version satisfies grpc's minimum; modify the require line and
the corresponding replace line in go.mod to use at least v1.40.0 and verify go
mod tidy builds cleanly.

---

Nitpick comments:
In `@router-tests/events/nats_subscription_filter_bypass_test.go`:
- Around line 145-151: Replace direct websocket writes that call conn.WriteJSON
with the test helper testenv.WSWriteJSON to ensure retries and deadlines are
applied; specifically update the subscription writes that currently invoke
conn.WriteJSON (e.g., the call creating a testenv.WebSocketMessage with Type
"subscribe" and ID "1") to use testenv.WSWriteJSON(conn, <message>) and apply
the same replacement for the other occurrences called out (around the blocks at
the other mentioned locations). Locate usages by searching for conn.WriteJSON
calls that send subscribe frames and swap them to testenv.WSWriteJSON so tests
benefit from the built-in 2s deadline and backoff.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23978f57-20d9-47c0-866e-314b023fb615

📥 Commits

Reviewing files that changed from the base of the PR and between 0c39bc2 and 4d3b15a.

⛔ Files ignored due to path filters (4)
  • connect-go/gen/proto/wg/cosmo/node/v1/node.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • router-tests/go.sum is excluded by !**/*.sum
  • router/gen/proto/wg/cosmo/node/v1/node.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • router/go.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • composition-go/index.global.js
  • composition/src/router-configuration/types.ts
  • composition/src/utils/string-constants.ts
  • composition/src/v1/constants/non-directive-definitions.ts
  • composition/src/v1/federation/federation-factory.ts
  • composition/tests/v1/directives/subscription-filter.test.ts
  • composition/tests/v1/utils/utils.ts
  • connect/src/wg/cosmo/node/v1/node_pb.ts
  • proto/wg/cosmo/node/v1/node.proto
  • router-tests/events/nats_subscription_filter_bypass_test.go
  • router-tests/go.mod
  • router/core/factoryresolver.go
  • router/core/factoryresolver_test.go
  • router/go.mod
  • shared/src/router-config/graphql-configuration.ts
  • shared/test/subscription-filter.test.ts

Comment on lines +2535 to +2559
let hasSeenBypassIfValuesNull = false;
for (const objectFieldNode of objectValueNode.fields) {
const inputFieldName = objectFieldNode.name.value;
const inputFieldPath = inputPath + `.${inputFieldName}`;
switch (inputFieldName) {
case BYPASS_IF_VALUES_NULL: {
if (hasSeenBypassIfValuesNull) {
hasErrors = true;
duplicatedFieldNames.add(BYPASS_IF_VALUES_NULL);
break;
}
hasSeenBypassIfValuesNull = true;
if (objectFieldNode.value.kind !== Kind.BOOLEAN) {
fieldErrorMessages.push(
invalidInputFieldTypeErrorMessage(inputFieldPath, BOOLEAN, kindToNodeType(objectFieldNode.value.kind)),
);
hasErrors = true;
break;
}
// Only persist when explicitly true. Explicit false collapses to undefined so the
// serializer can emit the smallest proto for unchanged configs.
if (objectFieldNode.value.value === true) {
condition.bypassIfValuesNull = true;
}
break;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Still fail IN conditions when fieldPath or values is missing.

This new optional field makes IN: { bypassIfValuesNull: true } validate successfully, because leftover entries in validFieldNames never flip hasErrors. That lets composition emit an empty fieldPath/values filter downstream.

Suggested fix
     }
-    if (!hasErrors) {
+    if (validFieldNames.size > 0) {
+      hasErrors = true;
+    }
+    if (!hasErrors) {
       return true;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let hasSeenBypassIfValuesNull = false;
for (const objectFieldNode of objectValueNode.fields) {
const inputFieldName = objectFieldNode.name.value;
const inputFieldPath = inputPath + `.${inputFieldName}`;
switch (inputFieldName) {
case BYPASS_IF_VALUES_NULL: {
if (hasSeenBypassIfValuesNull) {
hasErrors = true;
duplicatedFieldNames.add(BYPASS_IF_VALUES_NULL);
break;
}
hasSeenBypassIfValuesNull = true;
if (objectFieldNode.value.kind !== Kind.BOOLEAN) {
fieldErrorMessages.push(
invalidInputFieldTypeErrorMessage(inputFieldPath, BOOLEAN, kindToNodeType(objectFieldNode.value.kind)),
);
hasErrors = true;
break;
}
// Only persist when explicitly true. Explicit false collapses to undefined so the
// serializer can emit the smallest proto for unchanged configs.
if (objectFieldNode.value.value === true) {
condition.bypassIfValuesNull = true;
}
break;
let hasSeenBypassIfValuesNull = false;
for (const objectFieldNode of objectValueNode.fields) {
const inputFieldName = objectFieldNode.name.value;
const inputFieldPath = inputPath + `.${inputFieldName}`;
switch (inputFieldName) {
case BYPASS_IF_VALUES_NULL: {
if (hasSeenBypassIfValuesNull) {
hasErrors = true;
duplicatedFieldNames.add(BYPASS_IF_VALUES_NULL);
break;
}
hasSeenBypassIfValuesNull = true;
if (objectFieldNode.value.kind !== Kind.BOOLEAN) {
fieldErrorMessages.push(
invalidInputFieldTypeErrorMessage(inputFieldPath, BOOLEAN, kindToNodeType(objectFieldNode.value.kind)),
);
hasErrors = true;
break;
}
// Only persist when explicitly true. Explicit false collapses to undefined so the
// serializer can emit the smallest proto for unchanged configs.
if (objectFieldNode.value.value === true) {
condition.bypassIfValuesNull = true;
}
break;
}
if (validFieldNames.size > 0) {
hasErrors = true;
}
if (!hasErrors) {
return true;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@composition/src/v1/federation/federation-factory.ts` around lines 2535 -
2559, The parser currently lets an IN condition pass validation when only
BYPASS_IF_VALUES_NULL is present because validFieldNames leftovers don't flip
hasErrors; after parsing objectValueNode.fields for the IN condition (the switch
handling BYPASS_IF_VALUES_NULL and other keys), explicitly validate that
required keys "fieldPath" and "values" are present (check validFieldNames or
track presence of fieldPath/values), and if either is missing set hasErrors =
true and push appropriate field error messages (similar to
invalidInputFieldTypeErrorMessage) so you cannot emit an IN filter with empty
fieldPath/values even when condition.bypassIfValuesNull is true; ensure
duplicatedFieldNames/hasSeen handling remains unchanged for
BYPASS_IF_VALUES_NULL and reference the BYPASS_IF_VALUES_NULL constant,
condition.bypassIfValuesNull, validFieldNames, hasErrors, and objectValueNode to
locate where to add this check.

Comment on lines +345 to +359
ctx, cancel := context.WithTimeout(xEnv.Context, 500*time.Millisecond)
defer cancel()
done := make(chan testenv.WebSocketMessage, 1)
go func() {
var unexpected testenv.WebSocketMessage
if err := conn.ReadJSON(&unexpected); err == nil {
done <- unexpected
}
}()
select {
case unexpected := <-done:
t.Fatalf("unexpected message arrived after filtered events: %+v", unexpected)
case <-ctx.Done():
// no further messages — filter held.
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

conn.ReadJSON without a read deadline can block indefinitely and leak the goroutine.

context.WithTimeout here does not cancel ReadJSON, so the goroutine can stay stuck on socket read and make this test flaky under slow/quiet conditions.

Suggested change
- ctx, cancel := context.WithTimeout(xEnv.Context, 500*time.Millisecond)
- defer cancel()
- done := make(chan testenv.WebSocketMessage, 1)
- go func() {
-   var unexpected testenv.WebSocketMessage
-   if err := conn.ReadJSON(&unexpected); err == nil {
-     done <- unexpected
-   }
- }()
- select {
- case unexpected := <-done:
-   t.Fatalf("unexpected message arrived after filtered events: %+v", unexpected)
- case <-ctx.Done():
-   // no further messages — filter held.
- }
+ require.NoError(t, conn.SetReadDeadline(time.Now().Add(500*time.Millisecond)))
+ var unexpected testenv.WebSocketMessage
+ err = conn.ReadJSON(&unexpected)
+ assert.Error(t, err, "expected timeout/no message after filtered event")

As per coding guidelines: "Use manual SetReadDeadline with conn.ReadJSON only when expecting errors (e.g., websocket close after config hot reload); otherwise use testenv.WSReadJSON."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/events/nats_subscription_filter_bypass_test.go` around lines 345
- 359, The goroutine uses conn.ReadJSON without a read deadline which can block
forever; replace that pattern by using testenv.WSReadJSON (which respects
timeouts) instead of manual conn.ReadJSON inside the anonymous goroutine, or at
minimum set a read deadline on conn via conn.SetReadDeadline before calling
conn.ReadJSON so the goroutine unblocks when the context timeout
(context.WithTimeout) fires; update the block that creates done := make(chan
testenv.WebSocketMessage, 1) and the goroutine that calls conn.ReadJSON to use
testenv.WSReadJSON (or add conn.SetReadDeadline) so the goroutine cannot leak.

Comment thread router-tests/go.mod
github.com/wundergraph/cosmo/router v0.0.0-20260318232543-0e5fa811a191
github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e
github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.269
github.com/wundergraph/graphql-go-tools/v2 v2.1.1-0.20260504064838-5a00844995b5
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspecting router-tests/go.mod security-relevant pins"
rg -n 'google\.golang\.org/grpc v1\.79\.3|go\.opentelemetry\.io/otel/sdk' router-tests/go.mod
echo
echo "Expected:"
echo "- grpc is pinned at v1.79.3"
echo "- otel/sdk should be pinned to v1.40.0 or later in both require/replace"
echo "- current output will show it is still below that floor"

Repository: wundergraph/cosmo

Length of output: 568


Pin go.opentelemetry.io/otel/sdk to v1.40.0+ in both require and replace sections.

This module pins grpc v1.79.3 at line 42, which can transitively pull a vulnerable otel/sdk version. Currently, otel/sdk is set to v1.39.0 (line 34) and v1.28.0 in the replace section (line 204), both below the required v1.40.0 floor. Update both pins to v1.40.0 or later to match the security requirement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/go.mod` at line 32, Update the pinned version of the module
go.opentelemetry.io/otel/sdk in both the require and replace sections to at
least v1.40.0: locate the require entry referencing go.opentelemetry.io/otel/sdk
(currently v1.39.0) and change it to v1.40.0 or later, and locate the replace
entry for go.opentelemetry.io/otel/sdk (currently v1.28.0) and bump that to the
same v1.40.0+ version so the transitive grpc v1.79.3 dependency no longer pulls
an older otel/sdk.

Comment thread router/go.mod
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.269
github.com/wundergraph/graphql-go-tools/v2 v2.1.1-0.20260504064838-5a00844995b5
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspecting router/go.mod security-relevant pins"
rg -n 'google\.golang\.org/grpc v1\.79\.3|go\.opentelemetry\.io/otel/sdk' router/go.mod
echo
echo "Expected:"
echo "- grpc is pinned at v1.79.3"
echo "- otel/sdk should be pinned to v1.40.0 or later in both require/replace"
echo "- current output will show it is still below that floor"

Repository: wundergraph/cosmo

Length of output: 562


Pin go.opentelemetry.io/otel/sdk to v1.40.0+ in both require and replace directives before merging.

This module currently resolves go.opentelemetry.io/otel/sdk to v1.39.0 in the require section and v1.28.0 via a replace directive, both below the safe floor required when using google.golang.org/grpc v1.79.3. Update both pins to v1.40.0 or later while modifying the grpc dependency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router/go.mod` at line 34, Update the go.mod entries so that the module
go.opentelemetry.io/otel/sdk is pinned to v1.40.0 or later in both the require
and replace directives (replace any existing v1.28.0 pin), and ensure the grpc
dependency google.golang.org/grpc is compatible (e.g., keep or set to v1.79.3)
so the otel/sdk version satisfies grpc's minimum; modify the require line and
the corresponding replace line in go.mod to use at least v1.40.0 and verify go
mod tidy builds cleanly.

…ing-techniques

# Conflicts:
#	composition-go/index.global.js
#	router-tests/go.mod
#	router-tests/go.sum
#	router/core/factoryresolver.go
#	router/go.mod
#	router/go.sum
@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 46.25%. Comparing base (aa5cae1) to head (446531a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2815      +/-   ##
==========================================
+ Coverage   46.24%   46.25%   +0.01%     
==========================================
  Files        1084     1084              
  Lines      145014   145046      +32     
  Branches     9247     9253       +6     
==========================================
+ Hits        67055    67092      +37     
+ Misses      76179    76174       -5     
  Partials     1780     1780              
Files with missing lines Coverage Δ
composition/src/utils/string-constants.ts 100.00% <100.00%> (ø)
...tion/src/v1/constants/non-directive-definitions.ts 100.00% <100.00%> (ø)
...omposition/src/v1/federation/federation-factory.ts 89.12% <100.00%> (+0.07%) ⬆️
router/core/factoryresolver.go 81.23% <100.00%> (+1.00%) ⬆️
router/gen/proto/wg/cosmo/node/v1/node.pb.go 29.81% <100.00%> (+0.15%) ⬆️

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

❌ Internal Query Planner CI checks failed

The Internal Query Planner CI checks failed in the celestial repository, and this is going to stop the merge of this PR.
If you are part of the WunderGraph organization, you can see the PR with more details.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
router/go.mod (1)

46-47: ⚠️ Potential issue | 🔴 Critical

Major: OpenTelemetry SDK version is pinned to a vulnerable version via replace.

Although require sets go.opentelemetry.io/otel/sdk to v1.39.0, the replace block downgrades both go.opentelemetry.io/otel/sdk and go.opentelemetry.io/otel/sdk/metric to v1.28.0. This version is vulnerable to CVE-2026-24051 (path hijacking in resource detection on macOS/Darwin), which allows arbitrary code execution. Upgrade the replace directives to at least v1.40.0 to mitigate this security issue.

Suggested fix: bump `otel/sdk` versions in replace block to v1.40.0+
 replace (
   ...
-  go.opentelemetry.io/otel/sdk => go.opentelemetry.io/otel/sdk v1.28.0
-  go.opentelemetry.io/otel/sdk/metric => go.opentelemetry.io/otel/sdk/metric v1.28.0
+  go.opentelemetry.io/otel/sdk => go.opentelemetry.io/otel/sdk v1.40.0
+  go.opentelemetry.io/otel/sdk/metric => go.opentelemetry.io/otel/sdk/metric v1.40.0
   ...
 )

Also applies to: 192-193

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router/go.mod` around lines 46 - 47, The go.mod currently forces
go.opentelemetry.io/otel/sdk and go.opentelemetry.io/otel/sdk/metric to a
vulnerable v1.28.0 via a replace; update the replace directives so both packages
point to at least v1.40.0 (or a newer non-vulnerable tag) to mitigate
CVE-2026-24051, ensuring the require entries remain consistent with
go.opentelemetry.io/otel/sdk v1.39.0 (or are bumped appropriately) so the
replace no longer downgrades the SDK; locate the replace entries referencing
"go.opentelemetry.io/otel/sdk" and "go.opentelemetry.io/otel/sdk/metric" and
change their versions to v1.40.0+.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@router/go.mod`:
- Around line 46-47: The go.mod currently forces go.opentelemetry.io/otel/sdk
and go.opentelemetry.io/otel/sdk/metric to a vulnerable v1.28.0 via a replace;
update the replace directives so both packages point to at least v1.40.0 (or a
newer non-vulnerable tag) to mitigate CVE-2026-24051, ensuring the require
entries remain consistent with go.opentelemetry.io/otel/sdk v1.39.0 (or are
bumped appropriately) so the replace no longer downgrades the SDK; locate the
replace entries referencing "go.opentelemetry.io/otel/sdk" and
"go.opentelemetry.io/otel/sdk/metric" and change their versions to v1.40.0+.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4746faaf-90d9-4645-b3f7-dfc51993c41e

📥 Commits

Reviewing files that changed from the base of the PR and between 4d3b15a and ca888a4.

⛔ Files ignored due to path filters (3)
  • connect-go/gen/proto/wg/cosmo/node/v1/node.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • router-tests/go.sum is excluded by !**/*.sum
  • router/gen/proto/wg/cosmo/node/v1/node.pb.go is excluded by !**/*.pb.go, !**/gen/**
📒 Files selected for processing (8)
  • composition/src/router-configuration/types.ts
  • composition/src/utils/string-constants.ts
  • composition/src/v1/federation/federation-factory.ts
  • connect/src/wg/cosmo/node/v1/node_pb.ts
  • proto/wg/cosmo/node/v1/node.proto
  • router-tests/go.mod
  • router/core/factoryresolver.go
  • router/go.mod
✅ Files skipped from review due to trivial changes (3)
  • composition/src/utils/string-constants.ts
  • connect/src/wg/cosmo/node/v1/node_pb.ts
  • router/core/factoryresolver.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • composition/src/router-configuration/types.ts
  • router-tests/go.mod

…n composition-go bundle

- Rename `WebSocketClientReadTimeout` → `WebSocketServerReadTimeout` in
  the new NATS bypass integration tests; the field was renamed in main.
- Regenerate `composition-go/index.global.js` so the embedded TS bundle
  picks up the new `bypassIfValuesNull` SDL field and validator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
router-tests/events/nats_subscription_filter_bypass_test.go (1)

345-359: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bound the final read with a socket deadline (current goroutine can block indefinitely).

Line [345]-Line [359] uses context.WithTimeout, but that does not cancel conn.ReadJSON in the goroutine. Set a read deadline and do a direct read (expecting timeout/no message), or use the helper pattern for this assertion.

Suggested fix
-			ctx, cancel := context.WithTimeout(xEnv.Context, 500*time.Millisecond)
-			defer cancel()
-			done := make(chan testenv.WebSocketMessage, 1)
-			go func() {
-				var unexpected testenv.WebSocketMessage
-				if err := conn.ReadJSON(&unexpected); err == nil {
-					done <- unexpected
-				}
-			}()
-			select {
-			case unexpected := <-done:
-				t.Fatalf("unexpected message arrived after filtered events: %+v", unexpected)
-			case <-ctx.Done():
-				// no further messages — filter held.
-			}
+			require.NoError(t, conn.SetReadDeadline(time.Now().Add(500*time.Millisecond)))
+			var unexpected testenv.WebSocketMessage
+			err = conn.ReadJSON(&unexpected)
+			assert.Error(t, err, "expected timeout/no message after filtered event")

As per coding guidelines: "Use manual SetReadDeadline with conn.ReadJSON only when expecting errors (e.g., websocket close after config hot reload); otherwise use testenv.WSReadJSON."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/events/nats_subscription_filter_bypass_test.go` around lines 345
- 359, The goroutine using conn.ReadJSON can block indefinitely because
context.WithTimeout doesn't cancel ReadJSON; replace this pattern by either
setting a socket read deadline on conn (call conn.SetReadDeadline to now+500ms)
and doing a direct conn.ReadJSON into a testenv.WebSocketMessage expecting a
timeout/no message, or use the existing helper testenv.WSReadJSON which handles
deadlines correctly; remove the background goroutine and the context.WithTimeout
usage and perform the bounded read directly (references: conn.ReadJSON,
conn.SetReadDeadline, testenv.WSReadJSON, testenv.WebSocketMessage).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@router-tests/events/nats_subscription_filter_bypass_test.go`:
- Around line 145-152: Replace direct WebSocket writes that call
conn.WriteJSON(...) with the test helper testenv.WSWriteJSON(...) so the writes
use the built-in retry/deadline logic; locate each occurrence of conn.WriteJSON
in the file (e.g., the call that writes the subscribe message in the
subscription subtests) and change the call to testenv.WSWriteJSON(t, conn, <same
payload>) preserving the exact message payload and error handling, and repeat
the same replacement for the other occurrences mentioned (the writes around the
blocks using subscription filteredEmployeeUpdated). Ensure you import/use the
testenv helper signature (t, conn, msg) and remove any manual require.NoError
after the call if testenv.WSWriteJSON already fails the test on error.

---

Duplicate comments:
In `@router-tests/events/nats_subscription_filter_bypass_test.go`:
- Around line 345-359: The goroutine using conn.ReadJSON can block indefinitely
because context.WithTimeout doesn't cancel ReadJSON; replace this pattern by
either setting a socket read deadline on conn (call conn.SetReadDeadline to
now+500ms) and doing a direct conn.ReadJSON into a testenv.WebSocketMessage
expecting a timeout/no message, or use the existing helper testenv.WSReadJSON
which handles deadlines correctly; remove the background goroutine and the
context.WithTimeout usage and perform the bounded read directly (references:
conn.ReadJSON, conn.SetReadDeadline, testenv.WSReadJSON,
testenv.WebSocketMessage).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a8b4dd20-c06c-457b-98cc-faafc56cdba8

📥 Commits

Reviewing files that changed from the base of the PR and between ca888a4 and 4093ad5.

📒 Files selected for processing (2)
  • composition-go/index.global.js
  • router-tests/events/nats_subscription_filter_bypass_test.go

Comment on lines +145 to +152
err := conn.WriteJSON(&testenv.WebSocketMessage{
ID: "1",
Type: "subscribe",
Payload: []byte(
`{"query":"subscription { filteredEmployeeUpdated(id: 1, filterById: 1) { id } }"}`,
),
})
require.NoError(t, err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace raw websocket writes with the test helper.

conn.WriteJSON is used directly in all four subtests. Please switch these writes to testenv.WSWriteJSON(...) to keep retry/deadline behavior consistent and reduce websocket timing flakes in CI.

As per coding guidelines: "Use testenv.WSReadJSON and testenv.WSWriteJSON for WebSocket reads and writes in tests instead of conn.ReadJSON and conn.WriteJSON, as these helpers include retry logic with 2-second deadlines and exponential backoff."

Also applies to: 205-212, 254-262, 302-309

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/events/nats_subscription_filter_bypass_test.go` around lines 145
- 152, Replace direct WebSocket writes that call conn.WriteJSON(...) with the
test helper testenv.WSWriteJSON(...) so the writes use the built-in
retry/deadline logic; locate each occurrence of conn.WriteJSON in the file
(e.g., the call that writes the subscribe message in the subscription subtests)
and change the call to testenv.WSWriteJSON(t, conn, <same payload>) preserving
the exact message payload and error handling, and repeat the same replacement
for the other occurrences mentioned (the writes around the blocks using
subscription filteredEmployeeUpdated). Ensure you import/use the testenv helper
signature (t, conn, msg) and remove any manual require.NoError after the call if
testenv.WSWriteJSON already fails the test on error.

…test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
composition/tests/v1/directives/subscription-filter.test.ts (1)

513-540: ⚡ Quick win

Cover one nested non-OR branch too.

This test proves propagation through OR, but not through nested AND/NOT paths. Since the behavior is recursive, a regression in those branches would still slip through.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@composition/tests/v1/directives/subscription-filter.test.ts` around lines 513
- 540, The test only covers propagation through an OR branch; add a case
verifying propagation through a nested non-OR branch (e.g., AND and/or NOT) by
extending or adding a test that calls federateSubgraphsSuccess (with subgraphB
and subgraphBypass3 and ROUTER_COMPATIBILITY_VERSION_ONE) and asserts
fieldConfigurations.subscriptionFilterCondition contains an AND/NOT wrapper
around an in clause that has bypassIfValuesNull: true; update expectations to
include the nested AND/NOT structure (referring to symbols:
federateSubgraphsSuccess, subgraphB, subgraphBypass3,
ROUTER_COMPATIBILITY_VERSION_ONE, SUBSCRIPTION, fieldConfigurations,
subscriptionFilterCondition, in, bypassIfValuesNull) so the recursive
propagation through non-OR branches is validated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@composition/tests/v1/directives/subscription-filter.test.ts`:
- Around line 513-540: The test only covers propagation through an OR branch;
add a case verifying propagation through a nested non-OR branch (e.g., AND
and/or NOT) by extending or adding a test that calls federateSubgraphsSuccess
(with subgraphB and subgraphBypass3 and ROUTER_COMPATIBILITY_VERSION_ONE) and
asserts fieldConfigurations.subscriptionFilterCondition contains an AND/NOT
wrapper around an in clause that has bypassIfValuesNull: true; update
expectations to include the nested AND/NOT structure (referring to symbols:
federateSubgraphsSuccess, subgraphB, subgraphBypass3,
ROUTER_COMPATIBILITY_VERSION_ONE, SUBSCRIPTION, fieldConfigurations,
subscriptionFilterCondition, in, bypassIfValuesNull) so the recursive
propagation through non-OR branches is validated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 82af321c-0bc4-46e0-932d-ee3812dd3337

📥 Commits

Reviewing files that changed from the base of the PR and between 4093ad5 and 446531a.

📒 Files selected for processing (1)
  • composition/tests/v1/directives/subscription-filter.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant