Skip to content

fix(warthunder): 数据层 CORS 回到 fail-closed,移除隐式 Origin 兜底 - #2514

Merged
wehos merged 2 commits into
mainfrom
fix/wt-cors-fail-closed
Jul 28, 2026
Merged

fix(warthunder): 数据层 CORS 回到 fail-closed,移除隐式 Origin 兜底#2514
wehos merged 2 commits into
mainfrom
fix/wt-cors-fail-closed

Conversation

@wehos

@wehos wehos commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

#1542 收尾第 3 批。这是收尾清单里唯一有实际安全影响的一条。

问题

上游 #23715dcec54d2,7-16)把 wt_server 的 CORS 做成了显式白名单 + 默认拒绝:create_http_server(..., cors_origins=()) 默认空集,CLI 提供 --cors-origin

#1542 在此之上加了一个模块级 _ALLOWED_CORS_ORIGINS 兜底 —— cors_origins 为空时回退到主服务端口的 loopback Origin 集合:

allowed_origins = getattr(server, "cors_origins", frozenset()) if server else frozenset()
# 兼容本地开发:未显式配置 cors_origins 时,回退到主服务端口的 loopback Origin 集合。
if not allowed_origins:
    allowed_origins = _ALLOWED_CORS_ORIGINS

两条启动路径都不传 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 → 读 Electron port_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

  • 改动了什么:移除 warthunder 数据层 CORS 的隐式 Origin 兜底,恢复上游的 fail-closed 默认;同步替换对应测试。
  • 理由 / 必要性:兜底必然生效,把「默认不允许任何跨源读取遥测」变成了「默认允许主服务端口的 loopback origin 读取」。虽只放行 loopback,但这是安全默认值的方向性回退,且收益为零(无浏览器消费者)。
  • 改动前后的表现对比:不传 cors_origins 时,改动前会回显主服务 loopback Origin,改动后不回显任何 Origin。显式传 cors_origins 的行为完全不变。因为生产路径上没有浏览器直连,实际运行行为无差异。
  • 潜在回归点:若有人依赖「在主服务页面里手动 fetch :8112 调试遥测」这一用法,改动后会被 CORS 拦下 —— 恢复方式是启动数据层时加 --cors-origin http://localhost:48911
  • 验证:变异验证 —— 把兜底加回去后 test_data_layer_cors_is_closed_by_defaulttest_data_layer_cors_has_no_implicit_origin_fallback 立刻变红,还原后 6 passed;uv run pytest plugin/tests -q2752 passed, 20 skippeduv run pytest tests/unit -q6788 passed, 26 skipped, 0 failed

不拆分理由 / Why Not Split

生产代码与其测试必须同 PR:只删兜底会让 5 个旧用例立刻变红,只改测试则回退没做。共 2 个文件、-162/+45。

Summary by CodeRabbit

  • 安全性改进

    • 跨域请求默认关闭,未配置允许来源时不再返回跨域响应头。
    • 仅对通过 --cors-origin 显式配置的来源进行跨域放行。
    • 不再支持通配符 * 或基于本地端口、环境配置推导的隐式来源。
  • 测试

    • 新增并更新跨域行为验证,覆盖默认拒绝和显式白名单场景。

上游 #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>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

CORS 显式白名单

Layer / File(s) Summary
移除隐式 CORS 来源
plugin/plugins/neko_warthunder/data_layer/data process/wt_server.py
删除平台与端口配置读取,以及本地开发 CORS 回退集合的构建逻辑喵。
执行显式来源校验
plugin/plugins/neko_warthunder/data_layer/data process/wt_server.py, tests/unit/test_neko_warthunder_review_regressions.py
_Handlerdo_OPTIONS() 仅接受 server.cors_origins 中的 Origin;测试覆盖默认拒绝、显式白名单和无隐式回退喵。

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested labels: enhancement

Poem

端口回退悄悄眠,喵
白名单前守边关,喵
未经明许不放行,喵
OPTIONS 也要验身份,喵
CORS 变得真乖巧喵

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了本次将数据层 CORS 恢复为 fail-closed 并移除隐式 Origin 兜底的核心改动喵
Description check ✅ Passed 描述包含了改动背景、回归报告、不拆分理由和测试结果,整体已覆盖模板要求喵
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

本次改动恢复 War Thunder 数据层的 fail-closed CORS 默认行为喵。

  • 删除根据主服务端口生成隐式 loopback Origin 白名单的逻辑喵。
  • CORS 响应与预检现在仅接受显式配置的 server.cors_origins 喵。
  • 更新回归测试,覆盖默认拒绝、显式白名单和禁止隐式兜底三种语义喵。

Confidence Score: 5/5

该 PR 看起来可以安全合并喵。

未发现仍会阻碍合并的具体故障;现有消费者为不受 CORS 约束的 Python HTTP 客户端,而显式浏览器 Origin 配置路径仍被保留喵。

Important Files Changed

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]
Loading

Reviews (2): Last reviewed commit: "style(test): docstring 改英文以过 DOCSTRING_C..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 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

📥 Commits

Reviewing files that changed from the base of the PR and between 92ed281 and 9b1b1ac.

📒 Files selected for processing (2)
  • plugin/plugins/neko_warthunder/data_layer/data process/wt_server.py
  • tests/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

@wehos
wehos merged commit 41caae7 into main Jul 28, 2026
14 checks passed
@wehos
wehos deleted the fix/wt-cors-fail-closed branch July 28, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant