Files
hms/plans/audit-report-2026-04-26.md
iven 83fe89cbcd
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
fix: 全系统审计问题修复 — 安全/数据完整性/功能缺陷/UX (Phase 1-5)
Phase 1 安全热修复:
- P0-1: /uploads 文件服务添加 JWT 认证中间件(支持 header + query param)
- P0-2: analytics/batch 路由从 public 移到 protected_routes
- P0-3: plugin engine SQL 注入修复(format! → 参数化查询)
- P0-new: stats_service compute_avg_field 字段白名单 + FLOAT8 类型转换

Phase 2 数据完整性:
- P0-4: 组织删除级联检查(添加部门存在性校验)
- P0-5: 部门删除级联检查(添加岗位 + 用户存在性校验)
- P0-8: workflow on_tenant_deleted 实现 5 实体批量删除
- P0-7: 并行网关 race condition 修复(consumed → completed 原子转换)

Phase 3 P1 后端 Bug:
- P1-12: plugin host 表名消毒(使用 sanitize_identifier)
- P1-10: workflow deprecated 状态转换(published → deprecated)
- P1-11: workflow 更新验证条件(nodes/edges 任一变化即验证)
- P0-9: 小程序 .gitignore 添加 .env/.env.*/日志
- P1-19: 小程序加密密钥替换为 64 字符强密钥

Phase 4 消息模块:
- P1-5: 通知偏好 GET 路由 + handler
- P1-4: 消息模板 update/delete CRUD + version
- P2-8: mark_all_read SQL 添加 version + 1
- P2-7: markAsRead 改为乐观更新 + 失败回滚

Phase 5 前端修复:
- P2-9: 通知面板点击导航到 /messages
- P2-1: 随访任务患者名批量 ID 解析(替代 UUID 显示)
- P2-5: AppointmentList 分离 patient_id/doctor_id 分别调用 API
- P2-17: PluginMarket installed 字段修正(name → id)
- P3-3: 路由标题 fallback 改为模式匹配(支持 :id 动态路径)
- P2-15: workflow updateDefinition 添加 version 字段
- P3-9: Kanban 版本使用记录实际 version
- P2-21: secure-storage 生产环境无密钥时阻止存储
- P3-11: destroyOnHidden → destroyOnClose
- P3-13: PendingTasks 深色模式 Tag 颜色适配

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 19:16:23 +08:00

17 KiB
Raw Blame History

HMS 全系统前端审计报告

日期: 2026-04-26 | 审计范围: 全系统(基础模块 + 健康模块 + 插件 + 小程序) 方法: 代码扫描 + 浏览器操作测试 + 后端日志分析 审计人: Claude Code 自动化审计


执行摘要

指标
审计覆盖模块 12 个auth/config/workflow/message/plugin/health/ai/settings/小程序)
浏览器验证页面 18 个 Web 页面 + 通知面板
代码级审计模块 10 个4 个后台 agent + 6 个手动/浏览器)
总问题数 72 个
P0 阻断 9 个
P1 严重 20 个
P2 中等 22 个
P3 轻微 15 个
P4 优化 6 个

风险矩阵

风险域 状态 关键发现
安全 🔴 高风险 2 个 P0文件无认证访问 + SQL 注入)
数据完整性 🔴 高风险 级联删除缺失 × 3并行网关死锁
功能完整性 🟡 中等 多个模块 CRUD 不完整(模板/偏好/组织)
实时性 🟡 中等 消息 60 秒轮询,无 WebSocket
代码质量 🟢 良好 动态表 SQL 注入防护好,租户隔离一致

P0 阻断问题8 个)

P0-1: 上传文件无认证访问

  • 模块: erp-server 静态文件服务
  • 类型: 安全漏洞
  • 复现步骤: 直接访问 http://localhost:3000/uploads/{任意文件名}
  • 预期行为: 需 JWT 认证才能访问医疗文档
  • 实际行为: 所有上传文件公开可访问,包括医疗文档
  • 影响: 医疗隐私数据泄露,违反 HIPAA 合规
  • 相关文件: crates/erp-server/src/main.rs:545-546
  • 建议修复: 在 ServeDir 前添加 JWT 认证中间件,或使用签名 URL

P0-2: analytics/batch 端点公开

  • 模块: erp-server API
  • 类型: 安全漏洞
  • 复现步骤: 无需认证 POST /api/v1/analytics/batch
  • 影响: 任意 JSON 事件注入,可伪造分析数据
  • 相关文件: crates/erp-server/src/main.rs 路由注册
  • 建议修复: 添加 JWT 认证守卫

P0-3: load_plugin_config SQL 注入

  • 模块: erp-plugin 引擎
  • 类型: 安全漏洞
  • 复现步骤: 恶意 WASM 插件 manifest 中 craft plugin_id 包含 SQL 注入 payload
  • 影响: 通过 format!() 拼接 SQLplugin_id 仅用 replace('\'', "''") 转义,不安全
  • 相关文件: crates/erp-plugin/src/engine.rs:630-637
  • 建议修复: 改用参数化查询 $N 占位符

P0-4: 组织删除无级联检查

  • 模块: erp-auth 组织管理
  • 类型: 数据完整性
  • 复现步骤: 删除包含子部门/岗位/用户关联的组织
  • 影响: 软删除组织后,子部门、岗位、用户关联成为孤儿数据
  • 建议修复: 删除前检查并阻止存在子记录的删除操作

P0-5: 部门删除无级联检查

  • 模块: erp-auth 部门管理
  • 类型: 数据完整性
  • 复现步骤: 删除包含子部门或关联用户的部门
  • 影响: 同 P0-4部门树结构断裂
  • 建议修复: 递归检查子部门和用户关联

P0-6: 岗位删除无级联检查

  • 模块: erp-auth 岗位管理
  • 类型: 数据完整性
  • 影响: 删除岗位后用户-岗位关联悬空
  • 建议修复: 检查用户关联后阻止或级联更新

P0-7: 并行网关 Join 逻辑可能导致死锁

  • 模块: erp-workflow 执行引擎
  • 类型: 功能缺陷
  • 相关文件: crates/erp-workflow/src/engine/executor.rs:369-425
  • 影响: 并行分支汇聚时token 查询可能匹配到历史迭代的 stale token导致提前/错误完成或永久等待
  • 建议修复: 添加 token lineage/correlation 机制,区分不同 fork 产生的 token

P0-8: workflow on_tenant_deleted 是空操作

  • 模块: erp-workflow 租户清理
  • 类型: 数据完整性
  • 相关文件: crates/erp-workflow/src/module.rs:149-154
  • 影响: 删除租户后,流程定义/实例/任务/Token 残留,跨租户数据泄漏风险
  • 建议修复: 实现级联软删除所有租户相关数据

P1 严重问题18 个)

P1-1: health-data 统计端点 500 错误

  • 模块: erp-health 统计
  • 类型: 功能缺陷
  • 根因: PostgreSQL AVG() 返回 NUMERIC 类型Rust 代码期望 f64FLOAT8
  • 相关文件: crates/erp-health/src/service/stats_service.rs
  • 浏览器验证: 统计报表页面显示"加载统计数据失败 500"
  • 建议修复: 使用 Decimal 类型或 SQL 中显式 CAST(AVG(...) AS FLOAT8)

P1-2: 行级数据权限未生效

  • 模块: erp-auth 权限
  • 类型: 功能缺陷
  • 影响: department_ids 已填充但未用于数据过滤查询
  • 建议修复: 在查询构建器中加入 department_ids 过滤条件

P1-3: 消息无实时推送

  • 模块: erp-message
  • 类型: 功能缺失
  • 影响: 医疗告警最多延迟 60 秒才能到达医护人员,临床不可接受
  • 建议修复: 实现 WebSocket 或 SSE 推送

P1-4: 消息模板 CRUD 不完整

  • 模块: erp-message
  • 类型: 功能缺失
  • 影响: 模板只能创建和列表,无法编辑、删除,且 render() 方法未接入发送管道
  • 相关文件: crates/erp-message/src/template_service.rs

P1-5: 通知偏好设置无法加载已有配置

  • 模块: erp-message + 前端
  • 类型: 功能缺陷
  • 影响: 后端无 GET /message-subscriptions 端点,前端无法加载用户已保存的偏好;第二次保存因缺少 version 字段必然失败
  • 相关文件: crates/erp-message/src/module.rs, NotificationPreferences.tsx

P1-6: 工作流 ServiceTask 是空操作

  • 模块: erp-workflow
  • 类型: 功能缺失
  • 相关文件: crates/erp-workflow/src/engine/executor.rs:277
  • 影响: 所有 ServiceTask 被自动跳过,service_type 字段无效

P1-7: 工作流未注册任何事件处理器

  • 模块: erp-workflow
  • 类型: 功能缺失
  • 影响: register_event_handlers 为空函数,工作流模块不响应任何外部事件
  • 相关文件: crates/erp-workflow/src/module.rs:137

P1-8: candidate_groups角色/部门分配)存储但未使用

  • 模块: erp-workflow
  • 类型: 功能缺失
  • 影响: 配置了 candidate_groups 但无 assignee 的 UserTask 对所有用户不可见,任务成为孤儿
  • 相关文件: crates/erp-workflow/src/service/task_service.rs:25-36

P1-9: 工作流超时检查仅记录日志

  • 模块: erp-workflow
  • 类型: 功能缺失
  • 影响: 超时任务无升级/自动完成/通知,永久停留在 pending 状态
  • 相关文件: crates/erp-workflow/src/engine/timeout.rs

P1-10: 工作流 deprecated 状态不可达

  • 模块: erp-workflow + 前端
  • 类型: 功能缺陷
  • 影响: 前端定义了 deprecated 状态样式,但后端无转换路径
  • 相关文件: ProcessDefinitions.tsx:19

P1-11: 工作流定义更新跳过校验

  • 模块: erp-workflow
  • 类型: 功能缺陷
  • 影响: 只更新 nodes 而不提供 edges 时,不做图结构验证
  • 相关文件: crates/erp-workflow/src/service/definition_service.rs:174-181

P1-12: 编号序列表名未充分消毒

  • 模块: erp-plugin
  • 类型: 安全隐患
  • 影响: plugin_id 格式化到 DDL/DML 语句时仅 replace('-', "_"),不处理引号/分号
  • 相关文件: crates/erp-plugin/src/host.rs:339

P1-13 ~ P1-18: 组织模块 P1 问题

  • P1-13: 组织/部门/岗位缺少列表 API 中的树形结构返回
  • P1-14: 部门树重组操作(拖拽移动父节点)未实现
  • P1-15: 组织/部门名称唯一性校验缺失
  • P1-16: 部门详情 GET 端点缺失
  • P1-17: 岗位分配/取消分配 API 缺失
  • P1-18: 消息群发(角色/部门/全员fan-out 未实现

P2 中等问题20 个)

ID 模块 问题 文件
P2-1 health 随访任务患者名显示为 UUID 片段 FollowUpTaskList.tsx
P2-2 health 前端测试覆盖率极低3 个文件) apps/web/src/
P2-3 health 深色模式样式重复 ~19 页内联 isDark 各健康页面
P2-4 health useDarkMode 和 useThemeMode 重叠 hooks/
P2-5 health AppointmentList 冗余 404 请求 AppointmentList.tsx
P2-6 message 未读计数不即时刷新(等 60s 轮询) stores/message.ts
P2-7 message markAsRead 失败不回滚乐观更新 stores/message.ts:57-69
P2-8 message mark_all_read 不更新 version 字段 message_service.rs:298-326
P2-9 message 通知面板点击不导航到详情 NotificationPanel.tsx:81-85
P2-10 message 轮询间隔 60s 对医疗告警不可接受 NotificationPanel.tsx:28-31
P2-11 workflow N+1 查询问题(实例/任务列表) instance_service.rs, task_service.rs
P2-12 workflow 排他网关表达式错误被静默吞掉 executor.rs:176
P2-13 workflow ProcessDesigner 不支持边条件配置 ProcessDesigner.tsx
P2-14 workflow 委派 API 要求手动输入 UUID PendingTasks.tsx:207-210
P2-15 workflow 前端 UpdateDefinitionRequest 缺少 version workflowDefinitions.ts:45-51
P2-16 plugin reconcile_references 表名注入模式 data_service.rs:1204-1206
P2-17 plugin PluginMarket installed 判断字段不匹配 PluginMarket.tsx:68
P2-18 plugin 模板渲染未接入发送管道 template_service.rs:92-99
P2-19 plugin 偏好设置 version 字段未发送导致更新失败 NotificationPreferences.tsx:26-37
P2-20 plugin 偏好设置仅暴露 DNDchannel_preferences 隐藏 NotificationPreferences.tsx:60

P3 轻微问题14 个)

ID 模块 问题
P3-1 health 已完成任务仍显示操作按钮
P3-2 health ArticleEditor 图片上传未实现 (TODO)
P3-3 health PatientDetail 头部标题显示"页面"
P3-4 health 4 处 any 类型使用
P3-5 health 登录硬编码默认 tenant_id
P3-6 settings 审计日志操作用户列显示原始 UUID
P3-7 settings 审计日志资源类型过滤列表硬编码
P3-8 settings 系统参数无列表 API需手动输入 key
P3-9 plugin Kanban 拖拽 version 硬编码 0 导致锁冲突
P3-10 plugin CRUD 排序使用 JSONB 文本提取导致字典序
P3-11 workflow ProcessDesigner 用 destroyOnHidden 而非 destroyOnClose
P3-12 workflow InstanceMonitor 显示 raw node_id 而非名称
P3-13 workflow 待办任务状态 Tag 不适配深色模式
P3-14 message 偏好设置 DND 启用但未填时间范围时无效

P4 优化建议6 个)

ID 模块 建议
P4-1 plugin PluginAdmin purge 按钮状态与后端不一致
P4-2 plugin WASM init() 使用 nil UUID
P4-3 plugin recover_plugins 不按 tenant_id 过滤
P4-4 settings LanguageManager 编辑弹窗无可编辑字段
P4-5 settings ChangePassword 最小长度仅前端校验
P4-6 settings Settings API delete/update URL 编码不一致

模块审计摘要

基础模块

模块 页面数 浏览器验证 代码审计 关键发现
用户/权限 (B1) 2 email 验证宽松
组织架构 (B2) 1 3×P0 级联删除缺失
工作流 (B3) 6 3 页 3×P0 + 6×P1 功能大量缺失
消息 (B4) 3+面板 面板 5×P1 无实时推送

健康模块

模块 页面数 浏览器验证 关键发现
患者管理 (B5) 2 标题"页面"bug
医生/排班/预约 (B6) 3 冗余 404 请求
随访/咨询 (B7) 4 部分 患者 UUID 片段
积分/文章/AI (B8) 9 部分 统计报表 500

系统/插件

模块 页面数 浏览器验证 代码审计 关键发现
插件 (B9) 8 P1 SQL 注入
统计/仪表盘 (B9) 2 500 错误
系统设置 (B10) 8 标签 3 标签 审计日志 UUID

小程序B11-B14

模块 审计方式 关键发现
登录/首页 (B11) 代码审计 P0 .env 泄露风险
健康管理 (B12) 代码审计 P2 错误处理缺失
医患交互 (B13) 代码审计 P1 咨询消息轮询无错误恢复
内容/商城 (B14) 代码审计 P2 空状态处理缺失

小程序专项发现

P0-9: .env 未加入 .gitignore

  • 模块: miniprogram 配置
  • 类型: 安全漏洞
  • 相关文件: apps/miniprogram/.gitignore(仅含 node_modules/ 和 dist/
  • 影响: .env 文件含 TARO_APP_ENCRYPTION_KEY=hms_miniprogram_encryption_key_2026,可能意外提交到 git
  • 当前状态: 未被 git 追踪git ls-files 为空),但风险存在
  • 建议修复: 在 .gitignore 中添加 .env.env.*

P1-19: 小程序加密密钥为弱密钥

  • 模块: miniprogram secure-storage
  • 类型: 安全隐患
  • 相关文件: apps/miniprogram/.env:2, apps/miniprogram/src/utils/secure-storage.ts
  • 影响: hms_miniprogram_encryption_key_2026 为可预测字符串AES 加密形同虚设
  • 建议修复: 使用 python -c "import secrets; print(secrets.token_hex(32))" 生成强密钥

P1-20: project.config.json urlCheck: false

  • 模块: miniprogram 配置
  • 类型: 安全隐患
  • 相关文件: apps/miniprogram/project.config.json:6
  • 影响: 生产环境允许请求任意域名,应限制为后端 API 域名
  • 建议修复: 生产版本设为 true 并配置合法域名白名单

P2-21: secure-storage 未设密钥时明文存储

  • 模块: miniprogram secure-storage
  • 类型: 安全隐患
  • 相关文件: apps/miniprogram/src/utils/secure-storage.ts:12
  • 影响: ENCRYPTION_KEY 为空时 encrypt() 直接返回明文token 和敏感数据不加密存储
  • 建议修复: 强制要求密钥,未设置时阻止存储敏感数据

P2-22: 小程序各页面缺少统一错误边界

  • 模块: miniprogram 全局
  • 类型: 功能缺陷
  • 影响: API 请求失败时页面无友好错误提示,可能白屏
  • 建议修复: 添加全局错误边界组件和网络错误拦截器

P3-15: 小程序部分页面缺少空状态处理

  • 模块: miniprogram 健康管理
  • 类型: UX 问题
  • 影响: 健康数据为空时无引导提示

浏览器验证发现汇总

以下问题通过实际浏览器操作验证:

页面 操作 结果 问题
Users 创建用户(无效邮箱) 成功 email 验证宽松
Users 删除用户 成功
Users 分配角色 成功
Patient List 搜索 成功
Patient Detail 切换标签 成功 标题显示"页面"
Statistics 加载 失败 500 SQL 类型不匹配
Settings-字典 新建/添加项/删除 全部成功
Settings-密码 错误旧密码 正确提示
Settings-审计日志 查看 成功 UUID 而非用户名
Settings-菜单 查看 成功
Workflow-定义 列表 成功
Workflow-设计器 编辑草稿 成功 缺节点属性编辑器
Workflow-待办 查看 成功
通知面板 打开 成功 60s 轮询延迟

优先修复建议

第一优先级P0必须立即修复

  1. 上传文件认证P0-1— 医疗隐私合规要求
  2. SQL 注入修复P0-3— 安全风险,改用参数化查询
  3. analytics/batch 认证P0-2— 防止数据伪造
  4. 级联删除检查P0-4/5/6— 防止数据完整性破坏
  5. 并行网关修复P0-7— 核心工作流引擎可靠性
  6. 租户清理实现P0-8— 多租户数据隔离

第二优先级P12 周内修复)

  1. 统计报表 SQL 类型修复P1-1— 面向用户的功能
  2. 行级数据权限实现P1-2— 安全要求
  3. 消息实时推送P1-3— 医疗告警时效性
  4. 工作流功能补全P1-6~11— 模块可用性
  5. 消息模板/偏好完善P1-4/5— 功能闭环

第三优先级P21 月内修复)

  1. 前端测试覆盖率提升P2-2
  2. 工作流 N+1 查询优化P2-11
  3. 各模块 UX 修复UUID 显示、标题、按钮状态等)

附录:审计方法

  • 代码扫描: Explore agent 深度遍历源码,逐模块检查功能完整性
  • 浏览器测试: Chrome DevTools MCP 实际操作 18 个页面,验证 CRUD 链路
  • 后端日志分析: 解析 Axum tracing 日志定位 500 错误根因
  • 网络面板: 监控 API 请求/响应,发现冗余调用和错误响应