Skip to content

fix(mcp)!: require allowed hosts for non-loopback HTTP binds - #7172

Merged
gensokyo-dev merged 6 commits into
morluto:mainfrom
DeryFerd:fix/mcp-http-nonloopback-host-allowlist
Oct 26, 2026
Merged

gensokyo-dev merged 6 commits into
morluto:mainfrom
DeryFerd:fix/mcp-http-nonloopback-host-allowlist

Conversation

@DeryFerd

@DeryFerd DeryFerd commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A non-loopback MCP HTTP bind previously skipped Host and Origin validation. Require an operator-declared hostname allowlist and keep both request gates active. Publish a client URL admitted by that list, using the actual bound port.

Configuration validates complete hostname/IP tokens, canonicalizes and deduplicates them once, and passes typed identities through CLI, environment serving and HTTP. URL components, ports and encoded/control syntax are refused at input. Missing declarations have a typed listener failure and one recovery owner shared by MCP and environment-serve; native bind failures retain their recovery meanings. No DNS lookup or extra protocol request is added.

Exported AgentEnv bundles declare loopback client names and their compose service name. One bundle-name owner validates folder/id/HTTP identity and the DNS label bound before preparing state or writing files. CLI guides, transport contracts and current specifications describe the new behavior; historical verification records retain their original scope.

Migration

BREAKING CHANGE: non-loopback deployments must set MCP_ALLOWED_HOSTS=127.0.0.1,rea.lan, or pass --allowed-hosts 127.0.0.1,rea.lan to rea mcp --http, listing hostnames/IPs clients use without URLs or ports. environment-serve uses the environment setting. Loopback defaults retain their behavior. Bundle names must remain unchanged as HTTP hostnames and fit one 63-octet DNS label; use a name such as replay-1 for IP-shaped labels.

Validation

Integration base: 19447516a4bdbc9437d112ebd3f12a5fd062db83. Final head: 505ad66013b7936dbbe547594ea7d9b36f3ebef4.

  • Real packaged Linux CLI/MCP HTTP: base accepts foreign Host/Origin/null initialization. Fix returns 403, legitimate/no-Origin initialization returns 200, and the announced endpoint itself initializes successfully. Missing/invalid configuration exits 1 with the correct repair. Loopback controls preserve prior outcomes.
  • 184 tests pass on the refreshed main integration across HTTP/options/bundle/build/remediation/runtime, compiled export and the complete CLI input-schema suite. Typecheck/lint, runtime build and docs checks pass. Regressions include malformed-token admission, numeric/IP bundle names, DNS length, no export writes on refusal, port release, IDN/IPv6 and direct SDK use of the announced URL.
  • Real local DNS encoding: a 63-octet label emits a query; 64 fails with EBADNAME before any query. This is the generated service's protocol constraint, not an arbitrary limit.
  • Final six-request packaged HTTP probe: 2.810s wall, 2.920s CPU, 314.20 MiB sampled family RSS on two CPUs. Fresh process/private HOME, uncontrolled filesystem cache; no speedup claim. 100ms RSS sampling can miss short peaks/double-count shared pages. Agent token count unmeasured; analysis Evidence/catalog contracts are unchanged.
  • Final CI 40341507311: source/static/docs/build, real AgentEnv export/run and all three native package baselines pass. Portable tests fail only keyedArchiveScalarStructure.test.ts (same assertion reproduced on exact main 1944751); six boundary shards were cancelled by fail-fast, so their final-head coverage is unverified. Windows capability/package lane also passes. Codex completed with no findings and Greptile 5/5 on the current head. Earlier same-base failures and exact attribution remain in validation comments. Main independently fixed the prior Zod CLI-default regression in 2b84efe; the full 18-case CLI schema suite now passes in this integration. Other earlier client/cleanup failures were attributed against the pre-upgrade baseline and their latest main fixes are inherited here.

Ordinary tightening of the existing listener boundary; no new provider, target execution, installation or dependency policy. No local Docker, Windows or analysis-engine execution is claimed; hosted platform/bundle lanes are reported for their exact heads.

Author readiness

Human owner: @DeryFerd

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T14:46:33.275662Z 505ad66 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues were found.

Summary

Requires declared client hostnames for non-loopback MCP HTTP listeners while keeping Host and Origin checks active.

  • Non-loopback MCP listeners check declared host names and announce an allowed URL.
  • Exported environment bundles declare the host names their clients use.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[CLI flags or environment] --> B[Parse and deduplicate hostnames]
  B --> C[Bind HTTP listener]
  C --> D{Bound address is loopback?}
  D -->|Yes| E[Use loopback hostname list]
  D -->|No| F{Declared list exists?}
  F -->|No| G[Close listener and report repair]
  F -->|Yes| H[Use declared hostname list]
  E --> I[Announce an admitted URL]
  H --> I
  I --> J[Check Host and Origin]
  J -->|Allowed| K[MCP or environment route]
  J -->|Refused| L[403 response]
Loading

Reviews (8) · Last reviewed commit: "chore: refresh HTTP hardening against cu..." · Reviewed by Greptile

Comment thread src/server/http/serveStreamableHttp.ts Outdated
Comment thread src/server/http/serveStreamableHttp.ts Outdated
Comment thread src/server/http/serveStreamableHttp.test.ts Outdated
Comment thread src/server/http/serveStreamableHttp.test.ts Outdated
…owlist

A streamable HTTP bind outside loopback previously disabled both the Host
(DNS rebinding) and Origin (browser page) gates, because the allowlist was
classified solely from a loopback bound address. MCP_HOST already accepts
any address, so an opt-out listener served the whole MCP catalog with no
browser protection at all.

A non-loopback bind now refuses to serve until the operator declares the
host names clients will use, through MCP_ALLOWED_HOSTS or the new
rea mcp --http --allowed-hosts flag. Every declared name must parse; one
invalid entry refuses startup instead of silently narrowing the allowlist.
Loopback classification, the SDK localhost allowlist, and every existing
refusal behavior are unchanged.

Exported environment bundles bind 0.0.0.0 inside their container, so the
bundle Dockerfile now ships MCP_ALLOWED_HOSTS naming the published loopback
port and the environment's compose service name; the agentenv runner lane
deploys and verifies it end to end.
@DeryFerd
DeryFerd force-pushed the fix/mcp-http-nonloopback-host-allowlist branch from d5215ae to 8c17da5 Compare October 26, 2026 09:03
@gensokyo-dev

Copy link
Copy Markdown
Collaborator

@codex review

@gensokyo-dev gensokyo-dev changed the title fix(mcp): refuse non-loopback HTTP binds without an operator host allowlist fix(mcp)!: require allowed hosts for non-loopback HTTP binds Oct 26, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e8bbab920

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/config/mcpHttpEndpoint.ts
Comment thread src/application/environment/environmentBundle.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e96b0704c2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/server/http/serveStreamableHttp.ts
Comment thread src/main/mcpHttpArguments.ts
@gensokyo-dev

Copy link
Copy Markdown
Collaborator

Final head 505ad66013b7936dbbe547594ea7d9b36f3ebef4, refreshed base 19447516a4bdbc9437d112ebd3f12a5fd062db83:

  • All 184 focused tests, runtime build, type/lint and documentation checks pass. This includes the full CLI omission suite, confirming main commit 2b84efe covers the earlier Zod compatibility regression without a duplicate implementation.
  • Final hosted CI 40341507311 passes source/static/docs/build, real AgentEnv bundle export/run, Linux x64/arm64 and Darwin arm64 native package baselines, and Windows capability/package verification.
  • Only underlying portable failure: keyedArchiveScalarStructure.test.ts, retains own binary dictionary facts when a prototype key changes the decoder prototype, line 66. Re-ran that exact case in an isolated detached checkout of the exact base 1944751 with identical locked dependencies: same failed assertion, where the new main plist producer retains the own __proto__ dictionary fact but the older test expects it dropped. This HTTP PR does not change that producer/test. Six other boundary shards were cancelled by fail-fast; they are unverified, not passes.
  • Current-head Codex completed with no findings; Greptile 5/5. All actionable threads were replied with root cause, structural fix, validation and remaining cleanup, then resolved.
  • Actual final packaged wildcard HTTP listener announces an admitted 127.0.0.1 URL; foreign Host/Origin/null return 403 and legitimate/no-Origin/announced-Host initialization return 200. Two-CPU probe: 2.810s wall, 2.920s CPU, 314.20 MiB sampled process-family RSS, one uncontrolled-cache sample; no speedup claim, agent tokens unmeasured.

Proceeding with the explicitly authorized merge bypass for the reproduced base failure; it is not represented as green CI. No unrelated baseline code changes are included in this PR.

@gensokyo-dev
gensokyo-dev merged commit aa6c1d4 into morluto:main Oct 26, 2026
40 of 49 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants