fix(gateway): harden CORS / Host header / startup host binding#39
Open
YOMXXX wants to merge 1 commit into
Open
fix(gateway): harden CORS / Host header / startup host binding#39YOMXXX wants to merge 1 commit into
YOMXXX wants to merge 1 commit into
Conversation
TDAI Gateway has three CORS/Host/startup-safety holes that pre-date any cc-integration work and are independently exploitable on main today: 1. handleRequest unconditionally emitted Access-Control-Allow-Origin: * and acked OPTIONS preflight with 204. Combined with no Host-header validation, any browser page (via DNS rebinding evil.com -> 127.0.0.1) can talk to the daemon's data endpoints. 2. req.headers.host was never validated. Pairs with #1 to make DNS rebinding viable. 3. server.ts main() (the `tsx src/gateway/server.ts` auto-start path) never validated TDAI_GATEWAY_HOST, so an env-set `TDAI_GATEWAY_HOST=0.0.0.0` would silently bind the daemon to all interfaces. Fixes: - CORS is now opt-in: Access-Control-Allow-* headers are emitted only when TDAI_GATEWAY_CORS_ORIGIN is explicitly set, and OPTIONS preflight only returns 204 in that mode. Default state is no CORS at all — the daemon has no legitimate cross-origin browser use case. - handleRequest now rejects requests whose Host header (after stripping port / IPv6 brackets) is not in {127.0.0.1, localhost, ::1, ::ffff:127.0.0.1}. Set TDAI_GATEWAY_ALLOW_REMOTE=1 to opt in (matching the new assertSafeHost() convention). - server.ts main() now calls assertSafeHost() before constructing the gateway, refusing to bind a non-loopback TDAI_GATEWAY_HOST unless TDAI_GATEWAY_ALLOW_REMOTE=1 is set. Tests: new src/gateway/__tests__/cors-host.test.ts — 17 cases covering CORS opt-in (4), Host allowlist accept (6) / reject (6), and ALLOW_REMOTE opt-in (1). All passing. Signed-off-by: 李冠辰 <liguanchen@xiaomi.com>
This was referenced May 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description | 描述
TDAI Gateway 的 HTTP 层和启动入口有三处独立于任何在飞 feature 的安全缺陷,在当前
main上已经可利用。本 PR 全部修复。Three security holes in the TDAI Gateway HTTP layer and startup path that exist independently on
maintoday, all fixed here.Bugs | 问题
1. CORS hardcoded
Access-Control-Allow-Origin: *(src/gateway/server.ts:177-186)handleRequest无条件下发Access-Control-Allow-Origin: *并对所有 OPTIONS 返回 204。daemon 默认绑127.0.0.1,但浏览器通过 DNS rebinding(攻击者域名evil.com短 TTL DNS → 解析为127.0.0.1)发起的 fetch 直接通过 —— 加上 #2,daemon 数据端点对恶意浏览器页面开放。handleRequestunconditionally emittedAccess-Control-Allow-Origin: *and acked all OPTIONS with 204. Although the daemon binds loopback by default, a malicious page using DNS rebinding (short-TTLevil.com → 127.0.0.1) can issue fetch() and the daemon accepts — combined with #2 this exposes data endpoints to any browser tab.2. Host header unvalidated (
src/gateway/server.ts:172-174)req.headers.host不校验,配合 #1 形成 DNS rebinding 攻击面。req.headers.hostwas not validated; pairs with #1 to enable DNS rebinding.3.
server.ts main()did not validateTDAI_GATEWAY_HOST(src/gateway/server.ts:500-512)tsx src/gateway/server.ts直接启动路径不校验TDAI_GATEWAY_HOST。环境里若设了TDAI_GATEWAY_HOST=0.0.0.0,daemon 静默绑全网卡,把记忆数据端口暴露到 LAN。The auto-start path (
tsx src/gateway/server.ts) bound whateverTDAI_GATEWAY_HOSTwas set in env — a stray0.0.0.0would silently expose the daemon to the LAN.Fix | 修复
Access-Control-Allow-*头仅在TDAI_GATEWAY_CORS_ORIGIN显式设置时下发;OPTIONS preflight 仅在该模式下返回 204。默认状态下 OPTIONS 落入正常路由 404,不再作为永久未鉴权 probe 端点。handleRequest在路由前校验 Host header,剥离端口/IPv6 括号后必须在{127.0.0.1, localhost, ::1, ::ffff:127.0.0.1},否则 403。TDAI_GATEWAY_ALLOW_REMOTE=1时跳过。assertSafeHost()helper: server.tsmain()入口在构造 Gateway 前调用,非 loopbackTDAI_GATEWAY_HOST需要TDAI_GATEWAY_ALLOW_REMOTE=1opt-in 才允许绑定,否则 exit(2)。Tests | 测试
新建
src/gateway/__tests__/cors-host.test.ts(17 个 case):127.0.0.1,127.0.0.1:8421,localhost,localhost:8421,[::1],[::1]:8421(6 cases)evil.com,evil.com:8421,10.0.0.1,example.com,127.0.0.1.evil.com,localhost.evil.com→ 403 (6 cases)TDAI_GATEWAY_ALLOW_REMOTE=1跳过 Host 校验 (1 case)Out of scope | 范围外
不影响 main 上已有功能;与正在 review 的 cc 集成 PR (#7) 解耦。本 PR 修的是 main 上 server.ts 既有 bug,与 #7 无 commit 依赖。
Independent of the in-flight cc-integration PR (#7) — this PR fixes pre-existing server.ts bugs on
mainand does not touch any file introduced by #7.DCO
Commit
2f5b919带Signed-off-by: 李冠辰 <liguanchen@xiaomi.com>。