# vr-shopxo-plugin 安全审计与路由修复方案 > 版本:v1.0(Round 1 初稿)| 日期:2026-04-16 | Agent:council/SecurityEngineer > 背景:Phase 2 后台两个致命问题——侧栏乱码 + 路由无法渲染 --- ## 问题摘要 | # | 症状 | 根因推测 | 优先级 | |---|------|----------|--------| | **P1** | 侧栏显示 `VR票务` 而非 `VR票务` | 数据库/配置字段编码损坏(UTF-8→Latin1 misinterpretation) | P1 | | **P2** | 访问 `adminwatekc.php?s=VrTicket/SeatTemplateList` → 空白 "template not exists" | 控制器路径错误 + strtolower+ucfirst 匹配失败 | P1 | | **P3** | 视图路径 `../../../plugins/view/...` 在容器内无法解析 | 符号链接绝对路径 vs 相对路径问题 | P2 | --- ## 任务清单 ### 阶段一:根因分析与方案设计 - [ ] **S1.1** 读取 `vrt_power` 表的 name 字段值,检查是否为乱码的 Latin1 字节序列 - [ ] **S1.2** 检查 `plugins` 表中 vr_ticket 条目的 `name` / `title` 字段编码 - [ ] **S1.3** 对比正常插件(freightfee)的 `plugins` 表记录,找出字段差异 - [ ] **S1.4** 读取 vr_ticket 插件根目录 config.json,检查其中 name/title 字段编码 - [ ] **S1.5** 读取 ShopXO 核心 `Plugins/Index.php`,确认 strtolower+ucfirst 类名匹配逻辑 - [ ] **S1.6** 对比 freightfee/Admin.php(根目录)vs vr_ticket/SeatTemplate.php(子目录)的命名模式 - [ ] **S1.7** 检查 freightfee 的视图路径引用方式(相对 vs 绝对) ### 阶段二:修复实施 - [ ] **S2.1** 修复乱码:如果 config.json 中文损坏,重写正确的 name/title - [ ] **S2.2** 修复路由:按 freightfee 模式,在 vr_ticket 根目录创建 Admin.php,删除子目录控制器 - [ ] **S2.3** 修复视图路径:采用 freightfee 的相对路径方式 - [ ] **S2.4** 验证 strtolower+ucfirst 匹配:确认 VrTicket vs Vrticket 类名大小写问题 ### 阶段三:验证 - [ ] **S3.1** 访问 `adminwatekc.php?s=VrTicket/SeatTemplateList`,截图确认内容区正常渲染 - [ ] **S3.2** 截图确认侧栏插件名显示为中文 `VR票务` 而非乱码 - [ ] **S3.3** 提交修复 commit 到 council/SecurityEngineer --- ## 根因假设与分析 ### P1 乱码根因分析 **假设**:数据库 `vrt_power.name` 或 `plugins.title` 字段存储的是经过两次编码的中文字符串: - 原始中文 `票务` → UTF-8 字节 → 被当作 Latin1 存储 → 解码时又用 UTF-8 → 变成 `票务` **两种可能路径**: 1. 插件安装时,中文 name 被以 Latin1 编码写入 `vrt_power` 表 2. config.json 中的 name 字段本身就是乱码,安装时复制到数据库 **修复方案**: - 直接修改数据库中 `vrt_power` 表和 `plugins` 表的 name/title 字段 - 或重写 config.json 的 name 字段为正常中文,重新安装 ### P2 路由根因分析 ShopXO 核心 `Plugins/Index.php` 调用插件控制器逻辑大致为: ```php $class = strtolower(ucfirst($controller_name)); // e.g. "Vrticket" 或 "Vrticketadmin" $class_file = "$plugin_path/$class.php"; // 期望文件在根目录 ``` vr_ticket 的控制器在 `admin/controller/SeatTemplate.php`,而 ShopXO 期望 `VrTicket.php` 在根目录。 **正确模式(freightfee)**: ``` freightfee/Admin.php ← 根目录,类名 = "Admin" freightfee/Hook.php ``` **错误模式(vr_ticket)**: ``` vr_ticket/admin/controller/SeatTemplate.php ← 子目录,类名 = "SeatTemplate" ``` **修复方案**:在 vr_ticket 根目录创建 `Admin.php` 作为主入口,参考 freightfee/Admin.php 结构。 ### P3 视图路径分析 freightfee 用相对路径 `../../../plugins/view/...`,这是相对于 PHP 文件位置计算的。当 ShopXO 运行时,其 working directory 是 web root,所以这个相对路径能正确解析到 `ROOT/plugins/view/...`。 **安全关注点**: - 硬编码 `../../../` 路径可能在不同部署环境下失效 - 如果插件视图路径拼接时未做 sanitize,可能存在路径遍历风险(低风险,因为 ShopXO 有基础过滤) --- ## 依赖关系 - S1.1–S1.7 全部完成前,不进行 S2.x 修复 - S2.2 和 S2.3 可并行执行 - S3.x 需要在 main 分支合并后执行 --- ## claim 状态 | 任务 | claim | |------|-------| | S1.1–S1.7 | [Claimed: council/SecurityEngineer] | | S2.1–S2.4 | [Claimed: council/SecurityEngineer] | | S3.x | [Claimed: council/SecurityEngineer] | --- ## GitHub 参考插件分析 如果有时间,可检查以下插件结构(ShopXO 应用商店中已验证可用的后台插件): 1. `freightfee` — 根目录 Admin.php,直接继承 think\Controller 2. `answers` — freightfee 子插件,同一结构 3. 任何带 `admin/view/` 的插件 **关键发现**:所有正常工作的 ShopXO 插件都使用根目录 Admin.php 模式,不使用子目录控制器。 --- ## 约束 - 必须截图验证所有修复 - 先解决路由(P2),再解决乱码(P1) - 不在无充分证据时修改数据库字段