6.9 KiB
6.9 KiB
2026-05-26 — 服务器表单 UX 优化 + Bug 修复 + 安全加固
变更摘要
- 分类字段改为 select+input combobox — 替换不可见的
<datalist>,用<select>下拉 +__custom__自定义输入模式,用户可从已有分类选择或自由输入新分类 - Agent 端口隐藏 —
srvAgentPort改为type="hidden",默认值 8601 不变,减少表单认知负担 - 目标路径默认值 — 从
/home/deploy/target改为/www/wwwroot - CSV 模板修正 — 移除 Agent端口/平台/节点 列,更新示例行(target_path=/www/wwwroot, category=商城);后端保留
_CSV_IMPORT_ONLY_COLUMNS向后兼容旧模板导入 - Bug 修复:showEditServer 分类同步 — 修复编辑服务器时分类值丢失的两个 bug:
_refreshCategoryDatalist()在分类同步逻辑之后调用,导致 select 选项为空- 第二次冗余
_refreshCategoryDatalist()调用会触发_onCategoryChange()清空自定义分类值
- XSS 加固 —
agent_version渲染加esc()转义;esc()函数增加双引号转义("),防止 HTML 属性上下文注入 - XSS 加固(审计追加) — CSV 导入错误显示中
e.name、e.domain、e.row加esc()转义;预设/SSH密钥预设<option value>中p.id加esc()防属性突破 - 空 catch 消除 — 4 处空
catch(e){}全部添加console.warn()日志(Agent 轮询器、预设加载、SSH 密钥预设加载、剪贴板写入);后端 3 处except Exception: pass改为logger.warning()(CSV platform/node 加载、批量 Agent 回滚×2) - Nginx 缓存控制 — 为
/app/*.js和/app/*.css添加Cache-Control: no-store,防止浏览器缓存旧版静态文件 - Bug 修复:SSH 密钥双重加密 —
update_server中从 SSH 密钥预设解析密钥时预先encrypt_value(),遍历ALLOWED_FIELDS时又加密一次,导致ssh_key_private被双重加密,WebSSH 连接报Invalid private key。修复为与create_server一致:预设解析后存明文,统一在遍历步骤中单次加密
动机
<datalist>在大多数浏览器中不显示下拉箭头,用户无法发现分类选项- Agent 端口极少修改,展示增加表单复杂度
/home/deploy/target不是中国用户典型部署路径,/www/wwwroot更符合宝塔面板习惯- CSV 模板包含用户不使用的列(Agent端口/平台/节点),造成困惑
- 代码审计发现 showEditServer 中分类同步 bug 和 XSS 隐患
涉及文件
| 文件 | 改动 |
|---|---|
web/app/servers.html |
分类 combobox(select+input)、Agent 端口 hidden、目标路径默认值、showEditServer 分类 bug 修复、agent_version XSS 修复、esc() 双引号转义、CSV 导入错误 XSS 修复、预设 option value esc() 加固、4 处空 catch 加日志、移除冗余 _onCategoryChange() 调用 |
server/api/servers.py |
CSV_COLUMNS 重构(移除 Agent端口/平台/节点)、新增 _CSV_IMPORT_ONLY_COLUMNS 向后兼容、模板示例更新、3 处空 except 改 logger.warning()、SSH 密钥预设双重加密 bug 修复 |
| Nginx 配置(远程) | /app/*.js 和 /app/*.css 添加 Cache-Control: no-store |
Bug 详情
showEditServer 分类同步 bug(HIGH)
现象:编辑服务器时,若服务器分类不在已有选项中,分类值被清空。
根因:
_refreshCategoryDatalist()在分类同步逻辑(614-618 行)之后调用 → select 选项为空时无法匹配- 第二次
_refreshCategoryDatalist()调用触发_onCategoryChange(),当sel.value==='__custom__'时执行inp.value='',覆盖了刚设置的自定义分类值
修复:
- 将
_refreshCategoryDatalist()调用移到同步逻辑之前 - 删除冗余的第二次
_refreshCategoryDatalist()调用 showEditServer中直接设置 select/input 状态,不调用_onCategoryChange()
agent_version XSS(MEDIUM)
现象:s.agent_version 未转义直接插入 HTML。
修复:改为 esc(s.agent_version)||'--'
esc() 双引号不转义(LOW)
现象:esc() 使用 textContent→innerHTML 方法,不转义双引号,在 <option value="${esc(c)}"> 属性上下文中可能被突破。
修复:esc() 末尾追加 .replace(/"/g,'"')
CSV 模板示例行列数不匹配(MEDIUM)
现象:CSV 模板 header 有 11 列,但 example 行只有 10 个值。缺少 私钥内容(ssh_key_private)列的空值。
根因:密钥路径和私钥内容都是空值,需要两个连续逗号 ,,,,但 example 行只写了一个 ,,。
修复:example 行从 your-password,,商城 改为 your-password,,,商城
SSH 密钥双重加密(HIGH)
现象:WebSSH 连接使用 SSH 密钥预设的服务器时,报 Invalid private key 错误。
根因:update_server 中从 SSH 密钥预设解析密钥时执行 encrypt_value(decrypt_value(preset.encrypted_private_key)),将预设密钥解密后立即加密存入 update_data。随后遍历 ALLOWED_FIELDS 时,对 ssh_key_private 字段又执行了一次 encrypt_value(value),导致双重加密。create_server 路由无此问题(预设解析后存明文,统一加密步骤只执行一次)。
修复:
update_server预设解析改为存明文(与create_server一致):decrypt_value(preset.encrypted_private_key)- 已受影响的 server 8 数据修复:双重解密后重新单次加密
是否需迁移/重启
- DB 迁移: 无
- 需重启: 是(servers.py 后端改动需重启服务,已执行)
- Nginx: 已添加 no-cache 头(需 reload,已执行)
验证方式
- 编辑「机器人」服务器(分类=商城)→ 分类下拉正确显示"商城"选中 ✅
- 编辑「冲量银海」服务器(无分类)→ 分类下拉显示"— 无分类 —" ✅
- 添加服务器 → 分类默认"— 无分类 —"、目标路径
/www/wwwroot、Agent 端口 8601(hidden) ✅ - 选择"✏️ 自定义输入…"→ 文本输入框出现,输入值正确同步到
srvCategory✅ - 切换回已有分类 → 自定义输入框隐藏,值正确同步 ✅
- CSV 模板下载 → 无 Agent端口/平台/节点 列,11列 header = 11列 example,含
/www/wwwroot✅ esc()函数转义双引号 →esc('test"val')返回test"val✅- 远程服务器 Nginx 为 JS/CSS 返回
Cache-Control: no-store✅ - 逐行审计
servers.html:0 FINDING,1 SAFE(布尔表达式) ✅ - 逐行审计
servers.py:0 FINDING,SQL/命令注入/敏感泄露/大规模赋值全 SAFE ✅ - 空 catch 块:前端 0 处、后端仅保留
json.JSONDecodeError特定异常 ✅ - 部署后验证:
esc(p.id)出现 2 次、console.warn出现 4 次、空catch(e){}出现 0 次 ✅ - Server 8 SSH 密钥解密验证:单次
decrypt_value()得到-----BEGIN OPENSSH PRIVATE KEY-----✅