-
Notifications
You must be signed in to change notification settings - Fork 10
security guards
Verdict: fail-closed HTTP guards, most of them before body aggregation. Loopback-only by default; allowedHosts widens Host for non-browser clients but never widens Origin.
mcp-remote client its connection: the preflight mirrors Mcp-Method: initialize with no MCP-Protocol-Version (SEP-2243's own canonical example), and a pre-aggregation proxy-check answered it 400.
| Guard | Rule | Proof |
|---|---|---|
| DNS rebinding |
Host must be localhost, localhost., 127.0.0.1, [::1] (any port) or allowlisted; missing Host on HTTP/1.1 ⇒ 403; multiple Host/Origin ⇒ 403; Origin present ⇒ must be loopback; Origin: null ⇒ 403 |
DnsRebindingProtectionHandler |
| allowedHosts parse | trimmed, lowercase, rejects whitespace/control// @ ? #
|
DnsRebindingProtectionHandler#DnsRebindingProtectionHandler, DnsRebindingProtectionHandler#validateHostEntry
|
| Endpoint | normalized path ≠ endpoint ⇒ 404 | EndpointValidatorHandler#channelRead |
| Header guard | dup MCP-Protocol-Version/MCP-Session-Id/Last-Event-ID/Mcp-Method/Mcp-Name/Mcp-Param-* ⇒ 400 (even identical values; case variants collapse); header name not echoed |
McpHeaderGuardHandler#isSingleton |
| Accept | POST needs both application/json and text/event-stream (wildcards ok, q=0 rejects) ; GET needs text/event-stream ⇒ 406 |
AcceptValidationHandler |
| Stateless guard | session/Last-Event-ID headers ⇒ 404; DELETE ⇒ 405 | StatelessValidatorHandler#channelRead |
| Body limit | 1 MB default, 413 |
McpChannelInitializer#DEFAULT_MAX_CONTENT_LENGTH, McpChannelInitializer#initChannel
|
| CORS | Netty CorsHandler from allowedOrigins/allowNullOrigin/allowPrivateNetworks/allowedHeaders
|
NettyServerConfig.buildCorsConfig, McpChannelInitializer#initChannel
|
| Body/header agreement (all versions) | SEP-2243 mirror present ⇒ must match body, whichever version negotiated — a gateway must not route on a header the server never executes |
McpHeaderMatchHandler, protocol-versions
|
| Mirror required (2026-07-28 only) | the revision that adopted SEP-2243 also demands the mirrors be present; runs after agreement | RequiredHeadersHandler#requireMirrors |
| Pending-request ownership | client response must come from owning session (stateful) / channel (stateless) | DefaultTachyonServer#failPendingRequest |
| Error message hygiene | bare IAE message hidden; client-controlled values not echoed in header errors | errors |
| SSE comment injection | CR/LF flattened | PostSseStream#doWriteComment |
Rejection path: rejectAndClose marks channel rejected (drops remaining chunks) + Connection: close ChannelHandlerUtils#rejectAndClose.
Tests: DnsRebindingProtectionHandlerTest, McpHeaderGuardHandlerTest, McpHeaderMatchHandlerTest, EndpointValidatorHandlerTest, e2e DnsRebindingTest, AcceptHeaderValidationTest, MaxContentLengthTest, v2025_11_25/HeaderValidationTest (optional mirrors, mcp-remote initialize preflight), v2026_07_28/HeaderValidationTest, CustomHeaderValidationTest.
Related: netty-pipeline, errors.
📄 source .llm-wiki/concepts/security-guards.md · updated 2026-09-17 · verified at 1a4081f4 · tags [concept, security, transport]
🧭 Start
⚙️ Concepts (cross-cutting)
- request-lifecycle
- netty-pipeline
- protocol-versions
- sessions
- sse-streams
- feature-registries
- tasks
- extensions
- json-layer
- errors
- concurrency
- declarative-configuration
- configuration
- security-guards
- observability
- api-stability
📦 Modules