fix(warthunder): 数据层 CORS 回到 fail-closed,移除隐式 Origin 兜底 - #2514
Conversation
上游 #2371(5dcec54d2)把 wt_server 的 CORS 做成显式白名单 + 默认拒绝: create_http_server(..., cors_origins=()) 默认空集,CLI 提供 --cors-origin。 #1542 在此之上加了一个模块级 _ALLOWED_CORS_ORIGINS 兜底:cors_origins 为空时 回退到主服务端口的 loopback Origin 集合。而两条启动路径都不传 cors_origins (adapters/data_layer_process.py:180 的 in-process 路径、:372-379 的子进程 cmd 只带 --host/--port),所以这个兜底 100% 生效 —— 等于把上游刚定的默认拒绝 改回了默认放行。 数据层没有浏览器消费者::8112 的调用方是 adapters/telemetry_client.py 的 Python HTTP 客户端和 data_layer_process 的 health check,都不受 CORS 约束; plugin/plugins/neko_warthunder/ui/panel.tsx 里 grep 不到任何 fetch/axios/ XMLHttpRequest,它走的是插件后端。所以放行范围应当为空。 改动: - 删除 _read_port_config / _read_main_server_port / _build_allowed_cors_origins 与模块级 _ALLOWED_CORS_ORIGINS(-71 行),连带删掉因此变孤儿的 import platform - _cors() 与 do_OPTIONS() 去掉 `if not allowed_origins: allowed_origins = _ALLOWED_CORS_ORIGINS` 兜底,只认 server.cors_origins - 保留 _cors() 里 `getattr(self, "server", None)` 的防御(裸 handler 可测) 测试:5 个断言兜底行为的用例(only_echoes_approved_neko_origins / uses_configured_main_server_port / accepts_normalized_http_origins_on_port_80 / supports_legacy_main_server_port_env / uses_electron_port_config)换成 3 个 断言 fail-closed 语义的用例:默认不放行任何 Origin、显式配置后只回显白名单内的、 以及一条反回归守卫(模块不得再出现 _ALLOWED_CORS_ORIGINS 等符号)。 验证:变异验证——把兜底加回去后 test_data_layer_cors_is_closed_by_default 与 test_data_layer_cors_has_no_implicit_origin_fallback 两条立刻变红,还原后 6 passed; uv run pytest plugin/tests -q → 2752 passed, 20 skipped; uv run pytest tests/unit -q → 6788 passed, 26 skipped, 0 failed。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
WalkthroughChangesCORS 显式白名单
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested labels: Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
Greptile Summary本次改动恢复 War Thunder 数据层的 fail-closed CORS 默认行为喵。
Confidence Score: 5/5该 PR 看起来可以安全合并喵。 未发现仍会阻碍合并的具体故障;现有消费者为不受 CORS 约束的 Python HTTP 客户端,而显式浏览器 Origin 配置路径仍被保留喵。
|
| Filename | Overview |
|---|---|
| plugin/plugins/neko_warthunder/data_layer/data process/wt_server.py | 移除隐式 loopback Origin 兜底,使响应和预检统一遵循显式 CORS 白名单喵。 |
| tests/unit/test_neko_warthunder_review_regressions.py | 用聚焦的 fail-closed 回归测试替换旧有隐式 Origin 兜底测试喵。 |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
R[Browser request with Origin] --> C{Origin explicitly configured?}
C -->|Yes| A[Emit CORS headers / allow preflight]
C -->|No| D[Omit CORS headers / reject preflight]
P[Python telemetry and health clients] --> H[HTTP access unaffected by CORS]
Reviews (2): Last reviewed commit: "style(test): docstring 改英文以过 DOCSTRING_C..." | Re-trigger Greptile
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/unit/test_neko_warthunder_review_regressions.py (1)
75-93: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win补充
do_OPTIONS()的默认拒绝回归测试喵Line 75-93 只验证了
_cors()不回显 Origin;请直接断言未配置或未列入白名单的 Origin 发起 OPTIONS 时返回 403,避免预检路径单独回退为放行喵。🤖 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 `@tests/unit/test_neko_warthunder_review_regressions.py` around lines 75 - 93, Extend test_data_layer_cors_is_closed_by_default to invoke do_OPTIONS() for each unconfigured or non-whitelisted Origin and assert that the preflight response status is 403. Keep the existing _cors() non-echo assertions, and use the handler’s existing response/emission capture mechanism to verify the OPTIONS result.
🤖 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.
Nitpick comments:
In `@tests/unit/test_neko_warthunder_review_regressions.py`:
- Around line 75-93: Extend test_data_layer_cors_is_closed_by_default to invoke
do_OPTIONS() for each unconfigured or non-whitelisted Origin and assert that the
preflight response status is 403. Keep the existing _cors() non-echo assertions,
and use the handler’s existing response/emission capture mechanism to verify the
OPTIONS result.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a10f2d46-cf5e-431f-9df7-93b9646e54d1
📒 Files selected for processing (2)
plugin/plugins/neko_warthunder/data_layer/data process/wt_server.pytests/unit/test_neko_warthunder_review_regressions.py
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Project-N-E-K-O/N.E.K.O.-PC(manual)
💤 Files with no reviewable changes (1)
- plugin/plugins/neko_warthunder/data_layer/data process/wt_server.py
#1542 收尾第 3 批。这是收尾清单里唯一有实际安全影响的一条。
问题
上游 #2371(
5dcec54d2,7-16)把wt_server的 CORS 做成了显式白名单 + 默认拒绝:create_http_server(..., cors_origins=())默认空集,CLI 提供--cors-origin。#1542 在此之上加了一个模块级
_ALLOWED_CORS_ORIGINS兜底 ——cors_origins为空时回退到主服务端口的 loopback Origin 集合:而两条启动路径都不传
cors_origins:adapters/data_layer_process.py:180的 in-process 路径:wt_server.create_http_server(host, port):372-379的子进程路径:cmd只带--host/--port所以这个兜底 100% 生效,等于把上游刚立的默认拒绝改回了默认放行。
需要说明的是这不是作者的疏忽:他写这段时(7-13 / 7-14)上游那套参数还不存在,是被 bot 追着从
*→ 硬编码 48911 → 读 env → 读 Electronport_config.json一路补出来的。上游 7-16 才用另一套机制解决同一问题,此后两套就叠在了一起。为什么放行范围应当为空
数据层没有浏览器消费者:
:8112的调用方是adapters/telemetry_client.py的 Python HTTP 客户端和data_layer_process的 health check —— 服务端 HTTP,不受 CORS 约束plugin/plugins/neko_warthunder/ui/panel.tsx里 grep 不到任何fetch/axios/XMLHttpRequest,它走的是插件后端CORS 只对浏览器有意义。既然没有浏览器直连,默认放行范围就该是空集;真需要时通过
--cors-origin显式给。改动
_read_port_config/_read_main_server_port/_build_allowed_cors_origins与模块级_ALLOWED_CORS_ORIGINS(-71 行),连带删掉因此变孤儿的import platform_cors()与do_OPTIONS()去掉兜底分支,只认server.cors_origins_cors()里getattr(self, "server", None)的防御(让裸 handler 可测)create_http_server(cors_origins=...)与 CLI--cors-origin未动测试:5 个断言兜底行为的用例换成 3 个断言 fail-closed 语义的用例 —— 默认不放行任何 Origin、显式配置后只回显白名单内的、以及一条反回归守卫(模块不得再出现
_ALLOWED_CORS_ORIGINS等符号)。回归报告 / Regression Report
cors_origins时,改动前会回显主服务 loopback Origin,改动后不回显任何 Origin。显式传cors_origins的行为完全不变。因为生产路径上没有浏览器直连,实际运行行为无差异。fetch:8112 调试遥测」这一用法,改动后会被 CORS 拦下 —— 恢复方式是启动数据层时加--cors-origin http://localhost:48911。test_data_layer_cors_is_closed_by_default与test_data_layer_cors_has_no_implicit_origin_fallback立刻变红,还原后 6 passed;uv run pytest plugin/tests -q→ 2752 passed, 20 skipped;uv run pytest tests/unit -q→ 6788 passed, 26 skipped, 0 failed。不拆分理由 / Why Not Split
生产代码与其测试必须同 PR:只删兜底会让 5 个旧用例立刻变红,只改测试则回退没做。共 2 个文件、-162/+45。
Summary by CodeRabbit
安全性改进
--cors-origin显式配置的来源进行跨域放行。*或基于本地端口、环境配置推导的隐式来源。测试