[PR #7172] [MERGED] fix(mcp)!: require allowed hosts for non-loopback HTTP binds #7692

Closed
opened 2026-11-03 13:39:41 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/7172
Author: @DeryFerd
Created: 10/25/2026
Status: ✅ Merged
Merged: 10/26/2026
Merged by: @gensokyo-dev

Base: main ← Head: fix/mcp-http-nonloopback-host-allowlist


📝 Commits (6)

  • 8c17da5 fix(mcp): refuse non-loopback HTTP binds without an operator host allowlist
  • 3e8bbab Merge branch 'main' into fix/mcp-http-nonloopback-host-allowlist
  • b91a2b7 fix(mcp): own host allowlist parsing and recovery at their boundaries
  • e96b070 fix(environment): validate generated bundle service hostnames before export
  • 31e4024 fix(mcp): announce an admitted endpoint and document HTTP migration
  • 505ad66 chore: refresh HTTP hardening against current main

📊 Changes

25 files changed (+634 additions, -91 deletions)

View changed files

📝 docs/agent-environments.md (+11 -4)
📝 docs/cli.md (+23 -4)
📝 docs/mcp-contracts.md (+6 -1)
📝 docs/specs/agent-environments/environment-export/spec.md (+5 -3)
📝 docs/specs/agent-environments/environment-serve/spec.md (+4 -2)
📝 docs/specs/agent-environments/environment-serve/verification.md (+4 -2)
📝 docs/specs/agent-environments/http-transport/spec.md (+5 -2)
📝 docs/specs/agent-environments/http-transport/verification.md (+3 -3)
📝 src/application/environment/environmentBuild.ts (+2 -1)
📝 src/application/environment/environmentBundle.test.ts (+34 -0)
📝 src/application/environment/environmentBundle.ts (+46 -9)
📝 src/cli/environmentCommands.ts (+17 -30)
📝 src/config/mcpHttpEndpoint.ts (+47 -1)
➕ src/domain/httpHostname.ts (+37 -0)
📝 src/main.ts (+3 -0)
📝 src/main/mcpHttpArguments.ts (+30 -2)
📝 src/main/transport.ts (+4 -1)
📝 src/server/environment/serveEnvironment.ts (+6 -0)
📝 src/server/http/serveStreamableHttp.test.ts (+78 -1)
📝 src/server/http/serveStreamableHttp.ts (+35 -21)

...and 5 more files

📄 Description

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 19447516); 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 2b84efe0d; 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.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/7172 **Author:** [@DeryFerd](https://github.com/DeryFerd) **Created:** 10/25/2026 **Status:** ✅ Merged **Merged:** 10/26/2026 **Merged by:** [@gensokyo-dev](https://github.com/gensokyo-dev) **Base:** `main` ← **Head:** `fix/mcp-http-nonloopback-host-allowlist` --- ### 📝 Commits (6) - [`8c17da5`](https://github.com/morluto/rea/commit/8c17da53cc45d882460c8b317f0a685657857a7a) fix(mcp): refuse non-loopback HTTP binds without an operator host allowlist - [`3e8bbab`](https://github.com/morluto/rea/commit/3e8bbab920b40994cadd002b42e076acb9342a57) Merge branch 'main' into fix/mcp-http-nonloopback-host-allowlist - [`b91a2b7`](https://github.com/morluto/rea/commit/b91a2b7d18388c36dc6e6a6e1762767b01a21e8f) fix(mcp): own host allowlist parsing and recovery at their boundaries - [`e96b070`](https://github.com/morluto/rea/commit/e96b0704c27c1f71afcef07131d389b011c48ce1) fix(environment): validate generated bundle service hostnames before export - [`31e4024`](https://github.com/morluto/rea/commit/31e4024d417e6ca2f6da00f9338926f9f9abb834) fix(mcp): announce an admitted endpoint and document HTTP migration - [`505ad66`](https://github.com/morluto/rea/commit/505ad66013b7936dbbe547594ea7d9b36f3ebef4) chore: refresh HTTP hardening against current main ### 📊 Changes **25 files changed** (+634 additions, -91 deletions) <details> <summary>View changed files</summary> 📝 `docs/agent-environments.md` (+11 -4) 📝 `docs/cli.md` (+23 -4) 📝 `docs/mcp-contracts.md` (+6 -1) 📝 `docs/specs/agent-environments/environment-export/spec.md` (+5 -3) 📝 `docs/specs/agent-environments/environment-serve/spec.md` (+4 -2) 📝 `docs/specs/agent-environments/environment-serve/verification.md` (+4 -2) 📝 `docs/specs/agent-environments/http-transport/spec.md` (+5 -2) 📝 `docs/specs/agent-environments/http-transport/verification.md` (+3 -3) 📝 `src/application/environment/environmentBuild.ts` (+2 -1) 📝 `src/application/environment/environmentBundle.test.ts` (+34 -0) 📝 `src/application/environment/environmentBundle.ts` (+46 -9) 📝 `src/cli/environmentCommands.ts` (+17 -30) 📝 `src/config/mcpHttpEndpoint.ts` (+47 -1) ➕ `src/domain/httpHostname.ts` (+37 -0) 📝 `src/main.ts` (+3 -0) 📝 `src/main/mcpHttpArguments.ts` (+30 -2) 📝 `src/main/transport.ts` (+4 -1) 📝 `src/server/environment/serveEnvironment.ts` (+6 -0) 📝 `src/server/http/serveStreamableHttp.test.ts` (+78 -1) 📝 `src/server/http/serveStreamableHttp.ts` (+35 -21) _...and 5 more files_ </details> ### 📄 Description ## 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 19447516); 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 2b84efe0d; 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 - [x] 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. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-03 13:39:41 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#7692
No description provided.