[W-04][medium] 一次性密钥展示:导入结果 CSV 不转义会错列、手填密码时显示 undefined、复制失败无提示、导入弹窗无下载按钮 #54

Closed
opened 2026-09-30 13:57:04 +08:00 by nixevol · 1 comment
Owner

编号:W-04 严重级:medium 工作线:后台网页(web/) 来源:审查 A-05、A-06、A-26
依赖:无 被依赖:W-05 (#55)(同改 EndpointsView.vue,本条先合)

结论与统一方案

采用审查 A-05、A-06、A-26 的方案:

  1. 新增 web/src/utils/csv.ts:按 RFC 4180 给所有字段加引号并把 " 双写,用 CRLF 换行,文件头加 BOM,第一行写表头 id,name,login_password;名称列以公式字符开头时前缀 ',密码列只加引号、不改值。
  2. SecretOnceAlert 增加 mime 属性,导入流程改用 text/csv。
  3. 类型改为 login_password?: string;有值才显示一次性面板,没有就提示"已开通 {id}";假数据与后端一致。
  4. 复制等待 writeText 的结果并提示成功或失败;不支持 clipboard 时回退为选中文本再复制。
  5. 导入结果弹窗加"下载"按钮。

改动文件

web/src/utils/csv.ts(新)、components/SecretOnceAlert.vue、views/EndpointsView.vue、api/types.ts、api/mock.ts。

与其他问题的交互 / 冲突说明

EndpointsView.vue 同时被 W-05 修改,网页线内顺序合入。

验收与测试

  • csv 工具的 Vitest,覆盖逗号、引号、换行、公式前缀。
  • 手填密码开通后页面不出现 undefined,也不显示一次性面板。
  • 模拟不支持 clipboard 的环境,复制有提示。

问题明细(各区审查原文,证据含文件与行号)

以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。

[A-05] 导入结果下载的 CSV 不做转义,一次性密码文件会错列

  • 严重级:medium
  • 分类:数据 / 安全
  • 现象与影响:
    • 下载文件由 ${id},${name},${login_password} 直接拼接。名称是自由文本(CSV 导入本身允许带引号的逗号),接口原样回显的密码也可能含 ,、" 或换行,都会导致列错位,管理员按列保存的密码就是错的。
    • 名称以 = + - @ 开头时,用 Excel 打开会被当成公式执行。
    • 文件没有表头,也没有 BOM,Excel 打开中文会乱码。
  • 证据:
    • EndpointsView.vue:320-324:secret: res.items.map((i) => \${i.id},${i.name},${i.login_password}`).join("\n"),filename: "import-passwords.csv"`。
    • SecretOnceAlert.vue:18-26 原样下载。
    • endpoints_csv.go:55-60,296 回显原文密码。
  • 文档依据:PRD F01(约 131 行)"生成的密码只在结果里出现这一次,可以下载保存";DEVELOPMENT §8(约 923 行)"前端提供一次性下载"。
  • 为何不是故意设计:DEVIATIONS 没有相关条目。
  • 解决方案:
    1. 新增 web/src/utils/csv.ts:按 RFC 4180 给所有字段加引号并把 " 双写,用 CRLF 换行,文件头加 BOM,第一行写表头 id,name,login_password。
    2. 名称列以公式字符开头时前缀 '。密码列只加引号、不改值,避免破坏密码。
    3. SecretOnceAlert 增加 mime 属性,导入流程改用 text/csv。
  • 改动文件:web/src/utils/csv.ts(新)、EndpointsView.vue、SecretOnceAlert.vue。
  • 交互/冲突风险:无。
  • 需补测试:csv 工具的 Vitest,覆盖逗号、引号、换行、公式前缀;导入组件测试断言下载内容。
  • 置信度:代码阅读确定

[A-06] 开通端时手填了密码,"只显示一次"的密码显示为 undefined

  • 严重级:medium
  • 分类:协议一致性 / 界面
  • 现象与影响:后端只在生成密码时返回 login_password,前端却无条件显示 ${res.id}\t${res.login_password}。结果是"登录密码只显示一次"的框里写着 undefined,下载的文件内容也是它。
  • 证据:
    • EndpointsView.vue:208-213。
    • endpoints.go:291-296:if pwGenerated { data[loginPasswordOnceKey] = loginPW }。
    • types.ts:73-76 把 login_password 声明为必有。
    • mock.ts:357,372 的假数据总是返回密码,掩盖了这个问题。
  • 文档依据:admin-api 4.2(约 240 行)"只在本次生成时返回";PRD F01(约 128 行)。
  • 为何不是故意设计:前端类型与契约不符。
  • 解决方案:把类型改成 login_password?: string。有值才显示一次性面板,没有就提示"已开通 {id}"。假数据改成与后端一致。
  • 改动文件:types.ts、EndpointsView.vue、mock.ts。
  • 交互/冲突风险:无。
  • 需补测试:组件测试,手填密码开通后页面不出现 undefined,也不显示一次性面板。
  • 置信度:代码阅读确定

[A-26] 一次性密钥的"复制"可能静默失败,导入结果弹窗没有下载按钮

  • 严重级:low
  • 分类:界面
  • 现象与影响:在内网 IP 上用明文 HTTP 访问后台时,浏览器不提供 navigator.clipboard,"复制"按钮什么都不做也不提示;即使复制成功也没有反馈。导入结果弹窗写着"请下载保存",却只有"关闭"按钮。
  • 证据:SecretOnceAlert.vue:14-16;EndpointsView.vue:478-487。
  • 文档依据:PRD F01、F17 要求只显示一次;界面规则要求关键状态直接展示。
  • 为何不是故意设计:DEVIATIONS 没有相关条目。
  • 解决方案:等待 writeText 的结果,捕获异常,成功或失败都给提示;不支持时回退为选中文本再复制;导入弹窗加"下载"按钮。
  • 改动文件:SecretOnceAlert.vue、EndpointsView.vue。
  • 交互/冲突风险:无。
  • 需补测试:组件测试模拟不支持 clipboard 的环境。
  • 置信度:代码阅读确定

复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。

**编号**:W-04 **严重级**:medium **工作线**:后台网页(web/) **来源**:审查 A-05、A-06、A-26 **依赖**:无 **被依赖**:W-05 (#55)(同改 EndpointsView.vue,本条先合) ### 结论与统一方案 采用审查 A-05、A-06、A-26 的方案: 1. 新增 `web/src/utils/csv.ts`:按 RFC 4180 给所有字段加引号并把 `"` 双写,用 CRLF 换行,文件头加 BOM,第一行写表头 `id,name,login_password`;名称列以公式字符开头时前缀 `'`,密码列只加引号、不改值。 2. `SecretOnceAlert` 增加 `mime` 属性,导入流程改用 `text/csv`。 3. 类型改为 `login_password?: string`;有值才显示一次性面板,没有就提示"已开通 {id}";假数据与后端一致。 4. 复制等待 `writeText` 的结果并提示成功或失败;不支持 clipboard 时回退为选中文本再复制。 5. 导入结果弹窗加"下载"按钮。 ### 改动文件 `web/src/utils/csv.ts`(新)、`components/SecretOnceAlert.vue`、`views/EndpointsView.vue`、`api/types.ts`、`api/mock.ts`。 ### 与其他问题的交互 / 冲突说明 EndpointsView.vue 同时被 W-05 修改,网页线内顺序合入。 ### 验收与测试 - csv 工具的 Vitest,覆盖逗号、引号、换行、公式前缀。 - 手填密码开通后页面不出现 `undefined`,也不显示一次性面板。 - 模拟不支持 clipboard 的环境,复制有提示。 --- ### 问题明细(各区审查原文,证据含文件与行号) > 以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。**解决方案以本 issue 上方的"结论与统一方案"为准**;原文里的方案与之不一致时,按上方执行。 #### [A-05] 导入结果下载的 CSV 不做转义,一次性密码文件会错列 - 严重级:medium - 分类:数据 / 安全 - 现象与影响: - 下载文件由 `${id},${name},${login_password}` 直接拼接。名称是自由文本(CSV 导入本身允许带引号的逗号),接口原样回显的密码也可能含 `,`、`"` 或换行,都会导致列错位,管理员按列保存的密码就是错的。 - 名称以 `= + - @` 开头时,用 Excel 打开会被当成公式执行。 - 文件没有表头,也没有 BOM,Excel 打开中文会乱码。 - 证据: - `EndpointsView.vue:320-324`:`secret: res.items.map((i) => \`${i.id},${i.name},${i.login_password}\`).join("\n")`,`filename: "import-passwords.csv"`。 - `SecretOnceAlert.vue:18-26` 原样下载。 - `endpoints_csv.go:55-60,296` 回显原文密码。 - 文档依据:PRD F01(约 131 行)"生成的密码只在结果里出现这一次,可以下载保存";DEVELOPMENT §8(约 923 行)"前端提供一次性下载"。 - 为何不是故意设计:DEVIATIONS 没有相关条目。 - 解决方案: 1. 新增 `web/src/utils/csv.ts`:按 RFC 4180 给所有字段加引号并把 `"` 双写,用 CRLF 换行,文件头加 BOM,第一行写表头 `id,name,login_password`。 2. 名称列以公式字符开头时前缀 `'`。密码列只加引号、不改值,避免破坏密码。 3. `SecretOnceAlert` 增加 `mime` 属性,导入流程改用 `text/csv`。 - 改动文件:`web/src/utils/csv.ts`(新)、`EndpointsView.vue`、`SecretOnceAlert.vue`。 - 交互/冲突风险:无。 - 需补测试:csv 工具的 Vitest,覆盖逗号、引号、换行、公式前缀;导入组件测试断言下载内容。 - 置信度:代码阅读确定 #### [A-06] 开通端时手填了密码,"只显示一次"的密码显示为 `undefined` - 严重级:medium - 分类:协议一致性 / 界面 - 现象与影响:后端只在生成密码时返回 `login_password`,前端却无条件显示 `${res.id}\t${res.login_password}`。结果是"登录密码只显示一次"的框里写着 `undefined`,下载的文件内容也是它。 - 证据: - `EndpointsView.vue:208-213`。 - `endpoints.go:291-296`:`if pwGenerated { data[loginPasswordOnceKey] = loginPW }`。 - `types.ts:73-76` 把 `login_password` 声明为必有。 - `mock.ts:357,372` 的假数据总是返回密码,掩盖了这个问题。 - 文档依据:admin-api 4.2(约 240 行)"只在本次生成时返回";PRD F01(约 128 行)。 - 为何不是故意设计:前端类型与契约不符。 - 解决方案:把类型改成 `login_password?: string`。有值才显示一次性面板,没有就提示"已开通 {id}"。假数据改成与后端一致。 - 改动文件:`types.ts`、`EndpointsView.vue`、`mock.ts`。 - 交互/冲突风险:无。 - 需补测试:组件测试,手填密码开通后页面不出现 `undefined`,也不显示一次性面板。 - 置信度:代码阅读确定 #### [A-26] 一次性密钥的"复制"可能静默失败,导入结果弹窗没有下载按钮 - 严重级:low - 分类:界面 - 现象与影响:在内网 IP 上用明文 HTTP 访问后台时,浏览器不提供 `navigator.clipboard`,"复制"按钮什么都不做也不提示;即使复制成功也没有反馈。导入结果弹窗写着"请下载保存",却只有"关闭"按钮。 - 证据:`SecretOnceAlert.vue:14-16`;`EndpointsView.vue:478-487`。 - 文档依据:PRD F01、F17 要求只显示一次;界面规则要求关键状态直接展示。 - 为何不是故意设计:DEVIATIONS 没有相关条目。 - 解决方案:等待 `writeText` 的结果,捕获异常,成功或失败都给提示;不支持时回退为选中文本再复制;导入弹窗加"下载"按钮。 - 改动文件:`SecretOnceAlert.vue`、`EndpointsView.vue`。 - 交互/冲突风险:无。 - 需补测试:组件测试模拟不支持 clipboard 的环境。 - 置信度:代码阅读确定 --- <sub>复审基线:main `4059a15`(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。</sub>
nixevol added the P2-mediumlane/webreview-2026-09-30 labels 2026-09-30 13:57:05 +08:00
Author
Owner

已合入 origin/main 0c9b459。落地提交 15802bf fix: 校正一次性密码展示与 CSV 下载 (#54)。

已合入 origin/main `0c9b459`。落地提交 `15802bf` fix: 校正一次性密码展示与 CSV 下载 (#54)。
Sign in to join this conversation.