Skip to content

feat(router): disallow inline argument values (ENG-9586)#3044

Draft
gausie wants to merge 21 commits into
mainfrom
worktree-playful-rolling-lecun
Draft

feat(router): disallow inline argument values (ENG-9586)#3044
gausie wants to merge 21 commits into
mainfrom
worktree-playful-rolling-lecun

Conversation

@gausie

@gausie gausie commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Implements the security.disallow_inline_arguments router policy: warn mode logs and annotates responses with extensions.inlineArguments, enforce mode rejects operations using inline argument values. Applies to both HTTP and WebSocket transports; persisted operations are exempt by default.

RFC: #2998

Summary by CodeRabbit

  • New Features
    • Added security.disallow_inline_arguments to detect hardcoded inline GraphQL argument values on both HTTP and WebSocket.
    • Supports off, warn, and enforce modes, including configurable enforcement HTTP status and GraphQL error code/message.
  • Bug Fixes
    • Enforce/warn behavior is now consistent across HTTP and WebSocket, with persisted operations exempt by default (configurable to include).
  • Documentation
    • Added dedicated documentation and updated the security configuration reference/navigation.
  • Tests
    • Added coverage for detection, extensions.inlineArguments, telemetry (wg.operation.inline_arguments.count), and WebSocket response/error behavior.

@github-actions github-actions Bot added the router label Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Router-nonroot image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-05186dbe8c295a9caab49f70ebd2f23aa72beb11-nonroot

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a configurable DisallowInlineArguments policy, wires it through request handling and WebSocket execution, and updates responses, telemetry, docs, and tests for warn/enforce behavior.

Changes

Disallow Inline Arguments Feature

Layer / File(s) Summary
Configuration and docs
router/pkg/config/config.go, router/pkg/config/config.schema.json, router/pkg/config/fixtures/full.yaml, router/pkg/config/testdata/config_defaults.json, router/pkg/config/testdata/config_full.json, router/pkg/otel/attributes.go, docs-website/docs.json, docs-website/router/configuration.mdx, docs-website/router/security/disallow-inline-arguments.mdx
Adds the DisallowInlineArguments config types and schema, updates fixtures and docs, and introduces the OTEL count attribute.
Inline arguments detection and checker core
router/core/inline_arguments.go, router/core/inline_arguments_test.go, router/core/context.go
Implements inline-argument detection, warn/enforce result handling, error payload types, and unit tests, plus stores the response annotation on the operation context.
Error response rendering refactor
router/core/errors.go
Adds inline-argument error extensions and centralizes raw JSON error framing for HTTP transports.
Pre-handler and HTTP response wiring
router/core/graph_server.go, router/core/graphql_prehandler.go, router/core/graphql_handler.go
Constructs and runs the checker during request handling and injects extensions.inlineArguments into captured HTTP responses.
WebSocket wiring and enforcement
router/core/websocket.go
Threads the checker through websocket handling and returns inline-argument GraphQL errors on subscription violations.
Integration test suite
router-tests/security/disallow_inline_arguments_test.go
Adds HTTP and WebSocket end-to-end tests for off, warn, and enforce modes, tracing, logging, and persisted operations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PreHandler
  participant InlineArgumentsChecker
  participant GraphQLHandler
  participant WebSocketConnectionHandler

  Client->>PreHandler: HTTP GraphQL request
  PreHandler->>InlineArgumentsChecker: Check(operation, doc, clientInfo)
  InlineArgumentsChecker-->>PreHandler: result, annotation, or error
  alt enforce mode violation
    PreHandler-->>Client: error response
  else warn mode
    PreHandler->>GraphQLHandler: operation with inlineArgumentsAnnotation
    GraphQLHandler->>GraphQLHandler: inject extensions.inlineArguments
    GraphQLHandler-->>Client: annotated response
  end

  Client->>WebSocketConnectionHandler: subscription message
  WebSocketConnectionHandler->>InlineArgumentsChecker: Check(operation, doc)
  InlineArgumentsChecker-->>WebSocketConnectionHandler: result or error
  alt violation
    WebSocketConnectionHandler-->>Client: GraphQL error payload
  else warn/off
    WebSocketConnectionHandler-->>Client: next message
  end
Loading

Estimated code review effort: 4 (Complex) | ~60 minutes

Compact metadata:

  • Related issues: Not specified in provided data.
  • Related PRs: Not specified in provided data.
  • Suggested labels: security, router, feature
  • Suggested reviewers: Not specified in provided data.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title clearly matches the main change: adding a router policy to disallow inline argument values.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.42907% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.60%. Comparing base (3ed663b) to head (b283df6).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
router/core/inline_arguments.go 90.22% 11 Missing and 2 partials ⚠️
router/core/errors.go 83.01% 6 Missing and 3 partials ⚠️
router/core/graphql_handler.go 60.86% 6 Missing and 3 partials ⚠️
router/core/operation_processor.go 70.96% 6 Missing and 3 partials ⚠️
router/core/defer_response_writer.go 78.57% 2 Missing and 1 partial ⚠️
router/core/graph_server.go 75.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3044      +/-   ##
==========================================
+ Coverage   61.46%   61.60%   +0.14%     
==========================================
  Files         261      262       +1     
  Lines       30596    30792     +196     
==========================================
+ Hits        18805    18970     +165     
- Misses      10277    10301      +24     
- Partials     1514     1521       +7     
Files with missing lines Coverage Δ
router/core/context.go 74.92% <ø> (ø)
router/core/graphql_prehandler.go 85.81% <100.00%> (+0.13%) ⬆️
router/core/websocket.go 78.28% <100.00%> (+0.43%) ⬆️
router/internal/persistedoperation/client.go 91.80% <100.00%> (+0.27%) ⬆️
router/pkg/config/config.go 82.29% <ø> (ø)
router/pkg/otel/attributes.go 62.50% <ø> (ø)
router/core/graph_server.go 85.30% <75.00%> (-0.09%) ⬇️
router/core/defer_response_writer.go 71.18% <78.57%> (-0.25%) ⬇️
router/core/errors.go 78.60% <83.01%> (-1.58%) ⬇️
router/core/graphql_handler.go 62.32% <60.86%> (+0.40%) ⬆️
... and 2 more
🚀 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.

Comment thread router/core/websocket.go Outdated
ForwardInitialPayload: h.config.ForwardInitialPayload,
OperationProcessor: h.operationProcessor,
OperationBlocker: h.operationBlocker,
InlineArgumentsChecker: h.inlineArgumentsChecker,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added this because it feels technically correct but... do we actually want to do this check for subscriptions?

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
router/core/inline_arguments.go (1)

182-235: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Possible duplication between extensionJSON and writeInlineArgumentsError.

Both build an inlineArgumentsExtension from the same inlineArgumentsError fields. If extensionJSON isn't consumed elsewhere (e.g., errors.go in the next cohort), consider having writeInlineArgumentsError reuse it to avoid maintaining two marshal paths.

♻️ Possible consolidation
 func writeInlineArgumentsError(r *http.Request, w http.ResponseWriter, e *inlineArgumentsError, logger *zap.Logger, headerPropagation *HeaderPropagation) {
+	extJSON := e.extensionJSON(logger)
 	body, err := json.Marshal(inlineArgumentsErrorResponse{
 		Errors: []inlineArgumentsErrorEntry{{
 			Message: e.message,
 			Extensions: inlineArgumentsErrorExtensions{
 				Code: e.code,
-				InlineArguments: inlineArgumentsExtension{
-					Code:      e.code,
-					Message:   e.message,
-					Arguments: e.arguments,
-				},
+				InlineArguments: extJSON,
 			},
 		}},
 	})

Note: this requires changing inlineArgumentsErrorExtensions.InlineArguments to json.RawMessage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router/core/inline_arguments.go` around lines 182 - 235, The inline arguments
error response is assembling the same inlineArgumentsExtension data twice, once
in inlineArgumentsError.extensionJSON and again in writeInlineArgumentsError. If
extensionJSON is the canonical builder, update writeInlineArgumentsError to
reuse it and change inlineArgumentsErrorExtensions.InlineArguments to
json.RawMessage so both paths share one marshal source and stay consistent; keep
the existing symbols inlineArgumentsError, extensionJSON, and
writeInlineArgumentsError as the main touchpoints.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@router/core/graphql_handler.go`:
- Line 286: The final client write in the GraphQL handler is discarding errors,
so failures after annotation injection are lost when captureBuf is enabled.
Update the write path in the handler that uses hpw.Write to check and
propagate/log the returned error instead of ignoring both return values. Make
sure the fix preserves the existing captureBuf flow and only changes the final
response write handling in the GraphQL response pipeline.

In `@router/pkg/config/config.go`:
- Line 508: The DisallowInlineArguments security block in SecurityConfiguration
is currently only wired for YAML, so environment overrides won’t work even
though envDefault is present. Update the DisallowInlineArguments field and its
child config type to include the appropriate env tags, or add an envPrefix on
the SecurityConfiguration.DisallowInlineArguments block so it behaves like the
other security sections and can be overridden from the environment.

---

Nitpick comments:
In `@router/core/inline_arguments.go`:
- Around line 182-235: The inline arguments error response is assembling the
same inlineArgumentsExtension data twice, once in
inlineArgumentsError.extensionJSON and again in writeInlineArgumentsError. If
extensionJSON is the canonical builder, update writeInlineArgumentsError to
reuse it and change inlineArgumentsErrorExtensions.InlineArguments to
json.RawMessage so both paths share one marshal source and stay consistent; keep
the existing symbols inlineArgumentsError, extensionJSON, and
writeInlineArgumentsError as the main touchpoints.
🪄 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: 2b5bdba0-4a6c-4b38-9d3d-504c45f31afd

📥 Commits

Reviewing files that changed from the base of the PR and between a9650ee and b3c4900.

📒 Files selected for processing (15)
  • router-tests/security/disallow_inline_arguments_test.go
  • router/core/context.go
  • router/core/errors.go
  • router/core/graph_server.go
  • router/core/graphql_handler.go
  • router/core/graphql_prehandler.go
  • router/core/inline_arguments.go
  • router/core/inline_arguments_test.go
  • router/core/websocket.go
  • router/pkg/config/config.go
  • router/pkg/config/config.schema.json
  • router/pkg/config/fixtures/full.yaml
  • router/pkg/config/testdata/config_defaults.json
  • router/pkg/config/testdata/config_full.json
  • router/pkg/otel/attributes.go

Comment thread router/core/graphql_handler.go Outdated
Comment thread router/pkg/config/config.go Outdated
@mintlify

mintlify Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
wundergraphinc 🟢 Ready View Preview Jul 2, 2026, 2:22 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@gausie gausie marked this pull request as ready for review July 2, 2026 14:46
@gausie gausie requested review from a team as code owners July 2, 2026 14:46
@gausie gausie changed the title feat(router): disallow inline argument values feat(router): disallow inline argument values (ENG-9586) Jul 2, 2026
…ing-lecun

# Conflicts:
#	router/core/errors.go
@gausie gausie marked this pull request as draft July 3, 2026 13:36
… document

Move detection from post-parse to after NormalizeOperation/ValidateOperation so
only the executed operation is checked: non-executed sibling operations, unused
fragments and statically resolved @skip/@include are no longer flagged. Detection
walks the document via a pooled astvisitor walker (the flat doc.Arguments slice
keeps orphaned nodes on a normalization cache miss), and line/column are dropped
from the annotation payload since positions on the normalized document do not
reliably map to the submitted text. Enforce errors are held and surfaced during
validation, after the schema-validation error.
if logger := o.operationProcessor.logger; logger != nil {
logger.Warn("failed to look up persisted operation for a request carrying its own query body, classifying as APQ",
zap.String("sha256_hash", o.parsedOperation.GraphQLRequestExtensions.PersistedQuery.Sha256Hash),
zap.Error(err))
gausie added 2 commits July 4, 2026 14:09
Fold inlineArgumentsError into the shared HttpError mechanism with a
single graphqlError builder for the HTTP and WebSocket transports,
restore the body+hash APQ short-circuit when nothing consumes the
registered/APQ classification, drop redundant initial-part state from
the defer writer, and dedupe the security test config boilerplate.
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.

2 participants