[P2][CR-F-P2] 前端杂项:按键冲突、重复 change 处理器、i18n、URL 参数无 allowlist、new Function、tsconfig #534
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题描述
这里收集了 6 个前端小问题。单看每个都不大,但它们有同一个特点:缺少统一入口。键盘事件、语言状态、页面参数、公式求值、类型检查配置,都是各写各的。
#act下拉框挂了两个 change 处理器,一次选择会发起两次方向相反的导航。isEn ? … : …,语言状态有两个来源,入口 HTML 有三份几乎相同的拷贝。new Function求值,出错时静默返回 0。除特别说明外,以下结论均为代码阅读得出,未单独复现。
证据
1. 按键冲突
KeyboardInput把Enter当作攻击键(input.ts:39),把Tab当作地图键(input.ts:48)。onKeyDown(input.ts:97-117)只看event.code,不看event.target。只要是它认识的键,一律preventDefault()。聊天栏也在
window上监听 Enter,用来打开聊天框(toolbar-chat-ui.ts:206-219)。结果:
<select>上时,方向键和 Tab 仍被当作游戏输入并阻止默认行为:下拉框无法用键盘操作,Tab 也无法切换焦点。stopPropagation()(toolbar-chat-ui.ts:191-192)。三个
window级 keydown 处理器,三套过滤规则:KeyboardInputtoolbar-chat-uidocument.activeElement是否为 input / select / textareaHudManager(hud-manager.ts:718-719)e.target是否为 input / select(不含 textarea)原版 1.13c 中 Enter 用来打开聊天输入框,键盘上并没有"攻击"键。
KeyJ/Enter攻击是项目为键盘演示模式加的。2.
#act上的两个 change 处理器level参数addEventListener('change'),然后赋值location.searchonchange,然后赋值location.search一次 change 会连续发起两次导航,而且意图相反。最后停在哪个 URL,取决于浏览器怎么处理连续导航。
3. i18n
src/中有 246 处isEn ? … : …,分布在 13 个文件里。算上其他按语言判断的三元表达式,约 320 处,分布在 24 个文件里。scene/backend/skill-caster.ts:65 处(后端模块)ui/world-panels.ts:62 处ui/hotkeys.ts:38 处HudManager.lang和getTblLang()(lang.ts:84-86)。(hudManager?.lang ?? getTblLang()) === 'ENG'。acts.html(133 行)、acts-zh.html(132 行)、acts-en.html(132 行)。acts.html和acts-zh.html只差 1 行。.tbl不一致:.tbl生成的字典里已经有这些名字,例如CANONICAL_CHI_TBL_DICT的Druid(canonical-tbl-strings.ts:4569)。4. URL 参数没有 allowlist
pack、base${pack}/index.json加载资源包和角色资源data、mpq、wsfetch(mpq)后按 MPQ 解析(net-scene.ts:571-577);连接哪个中继innerHTML的路径(例如 net-scene.ts:486-488 只插入数字),所以暂不构成 XSS。5.
new Function求值技能描述公式evalSkillDescFormula(skill-calc-engine.ts:577)的做法是:先用正则把公式改写成 JS 表达式,再交给new Function('Math', 'return ' + s)(skill-calc-engine.ts:642-648)。skill-calc-engine被hud-manager、skill-tree-panel、class-starter-profiles等生产代码引用。静默兜底,违反
AGENTS.md的 fail-fast 约定:sklvl(...)、miss(...)直接被替换成0(skill-calc-engine.ts:607-609);catch返回0;Number(res) || 0把 NaN 也变成 0。结果是 tooltip 上的数字错了,也不会有任何报错。
安全与 CSP:
src/data/skills-catalog.ts,所以现在还不是注入漏洞。?mpq=可以指定任意 MPQ),这里就会变成任意代码执行。new Function要求 CSP 放开'unsafe-eval',和第 4 点要加的 CSP 冲突。仓库里已经有正规的求值器:
calc-ast.ts的evaluateCalc(calc-ast.ts:574)。它先分词再递归下降解析,文件头列出了它覆盖的 1.13c 表中 5 个边界情况。skill-tree.ts里还有一处new Function(skill-tree.ts:4765)。这个文件只被测试引用,src/里没有任何引用。6. tsconfig
include同时包含src、scripts、vite.config.ts和tests(tsconfig.json:18)。types: ["node"](tsconfig.json:16)。浏览器代码里误用process、Buffer、node:fs也能通过类型检查,问题要到运行时才暴露。src/mpq/file-source.ts直接import('node:fs/promises')(file-source.ts:17)。tests/e2e-drop-parity(5 个文件)和tests/fail-fast-drop.test.ts被排除在类型检查之外(tsconfig.json:19-22),但 vitest 仍然会运行它们。根因
修复指南
window级 keydown 监听器(例如InputRouter),按优先级分发:可编辑控件 → 聊天 → HUD 快捷键 → 游戏输入。isEditableTarget(e.target)判断可编辑控件,覆盖 input、textarea、select 和contenteditable。焦点在可编辑控件上时,游戏输入不处理,也不preventDefault。ATTACK_KEYS里去掉Enter,和原版一致:Enter 只用来打开聊天。KeyJ如果只是演示和测试用的,就放到显式开关后面。#act只保留一个 change 处理器:addEventListener('change'),或者把两段逻辑合并成一个函数;.onchange =统一改成addEventListener,并接入 #533 的Disposables。src/i18n/strings.ts,按键管理界面文字,例如t('toolbar.class')。.tbl取,不在代码或 HTML 里手写译名。如果项目决定显示简体中文,就做一个有记录的转换层,而不是逐条手写。src/i18n/lang.ts一个来源,删除HudManager.lang。后端不再读语言,文字格式化放到前端。acts*.html合并成一个模板。语言由 URL 参数或navigator.language决定,文字在运行时填充。skill-caster.ts)里的文字移出去,再按文件逐个替换。readPageConfig()集中解析页面参数,每个参数都有类型和取值范围。pack、base、data、mpq只接受同源路径,即new URL(value, location.href).origin === location.origin。不合法就报错并拒绝加载,不要静默回退到默认值。ws只接受配置好的中继地址列表,开发环境可以放行localhost。default-src 'self'; script-src 'self'; connect-src 'self' <中继地址>,不开'unsafe-eval'。calc-ast替换new Function:evalSkillDescFormula复用calc-ast的分词和解析,只把符号解析(lvl、par1..8、ln12、skill('…'.blvl)等)接到SKILLS_CATALOG上。skill-tree.ts里仅供测试的new Function。no-new-func固化(#537)。tsconfig.base.json:公共选项。tsconfig.app.json:src/**,只用 DOM 类型,不引入node。tsconfig.node.json:scripts/**、vite.config.ts,引入node。tsconfig.test.json:tests/**。tsc -b一次检查全部;npm run typecheck改为调用它。src/mpq/file-source.ts这类只在 Node 下运行的代码,移到 Node 侧或放进 node 配置。验收标准
<select>或<input>上时,方向键、Tab 不被游戏拦截;有对应的 DOM 事件测试。window级 keydown 监听器。#act做一次 change,只触发一次导航。src/中没有isEn ? … : …;只剩一个acts.html;游戏内名称全部来自.tbl。(可以分多个 PR 完成。)pack/base/data/mpq,以及不在列表里的ws,都会被拒绝并给出明确的错误。'unsafe-eval'。src/中没有new Function。技能描述公式用calc-ast求值,并且有测试覆盖原来会被替换成 0 的sklvl(...)/miss(...)。src不再引入node类型,不再排除任何测试文件。npm run typecheck0 error;npx vitest run全部通过。相关
hudManager.lang。Disposables和监听器的生命周期。ws被指向恶意中继时的输入伪造。no-new-func规则。