council(draft): SecurityEngineer - create plan.md for Phase 2 security audit
Analyzes two critical issues: 1. Sidebar garbled text (UTF-8/Latin1 encoding corruption in vrt_power/plugins tables) 2. Plugin controller routing failure (wrong directory structure vs freightfee Admin.php pattern) Proposes root cause hypotheses and step-by-step investigation + fix plan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>refactor/vr-ticket-20260416
parent
5749edf6ad
commit
d75eaa80a1
171
plan.md
171
plan.md
|
|
@ -1,107 +1,126 @@
|
||||||
# vr-shopxo-plugin 编辑器方案调研 — plan.md
|
# vr-shopxo-plugin 安全审计与路由修复方案
|
||||||
|
|
||||||
> 版本:v1.1(Round 2 更新)| 日期:2026-04-15 | Agent:council/FrontendDev + BackendArchitect
|
> 版本:v1.0(Round 1 初稿)| 日期:2026-04-16 | Agent:council/SecurityEngineer
|
||||||
> 背景:ShopXO 票务插件后台编辑器设计方案调研(Q1 JSON 编辑器复杂度评估 + ShopXO DIY 组件参考)
|
> 背景:Phase 2 后台两个致命问题——侧栏乱码 + 路由无法渲染
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 任务背景
|
## 问题摘要
|
||||||
|
|
||||||
vr-shopxo-plugin 是 ShopXO 票务插件,需要调研后台编辑器设计方案。
|
| # | 症状 | 根因推测 | 优先级 |
|
||||||
|
|---|------|----------|--------|
|
||||||
**已知 seat_map JSON 结构**:
|
| **P1** | 侧栏显示 `VR票务` 而非 `VR票务` | 数据库/配置字段编码损坏(UTF-8→Latin1 misinterpretation) | P1 |
|
||||||
```json
|
| **P2** | 访问 `adminwatekc.php?s=VrTicket/SeatTemplateList` → 空白 "template not exists" | 控制器路径错误 + strtolower+ucfirst 匹配失败 | P1 |
|
||||||
{
|
| **P3** | 视图路径 `../../../plugins/view/...` 在容器内无法解析 | 符号链接绝对路径 vs 相对路径问题 | P2 |
|
||||||
"map": ["AAAAAA", "BBBBBB", "CCCCCC"],
|
|
||||||
"seats": { "A": { "price": 899, "color": "#e74c3c", "label": "VIP区" }, ... },
|
|
||||||
"row_labels": ["A", "B", "C"],
|
|
||||||
"sections": [{ "name": "VIP区", "color": "..." }, ...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
venue 字段完全不存在(硬编码 "国家体育馆")。
|
|
||||||
|
|
||||||
**引入"场馆"后的嵌套层级**:
|
|
||||||
```
|
|
||||||
venue(name/address/image)
|
|
||||||
└── seat_map(map/seats/row_labels/sections/zones)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 核心问题
|
|
||||||
|
|
||||||
| # | 问题 | 负责 |
|
|
||||||
|---|------|------|
|
|
||||||
| **Q1** | JSON 编辑器复杂度评估:ShopXO 是否有现成组件?4 层嵌套 Vue3 编辑器实现成本?JSON vs 拆表方案成本对比? | FrontendDev |
|
|
||||||
| **Q2** | 商品发布页替换方案(替换页面)可行性:`plugins_view_admin_goods_save` 能否完全替换表单? | BackendArchitect |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 任务清单
|
## 任务清单
|
||||||
|
|
||||||
- [x] **Q1.1**: 调研 ShopXO 后台是否有现成 JSON 编辑器组件(ShopXO DIY 组件) `[Done: FrontendDev]` — 商业闭源,无公开源码,仅预构建 SPA,无 JSON 编辑能力
|
### 阶段一:根因分析与方案设计
|
||||||
- [x] **Q1.2**: 评估 Vue3 表单可视化编辑器复杂度(代码量/工时) `[Done: FrontendDev]` — 嵌套深度3层(非4层),表单编辑器~500行/1-1.5人天
|
|
||||||
- [x] **Q1.3**: JSON 编辑器 vs 拆表方案开发和维护成本对比 `[Done: FrontendDev]` — JSON单表成本低50%+,拆表仅座位万级+时值得
|
- [ ] **S1.1** 读取 `vrt_power` 表的 name 字段值,检查是否为乱码的 Latin1 字节序列
|
||||||
- [x] **Q2**: 商品发布页替换方案可行性(BackendArchitect 并行调研) `[Done: BackendArchitect]` ✅
|
- [ ] **S1.2** 检查 `plugins` 表中 vr_ticket 条目的 `name` / `title` 字段编码
|
||||||
- **结论**:钩子仅注入非替换;Save()支持标准POST;推荐钩子注入+JSON编辑器
|
- [ ] **S1.3** 对比正常插件(freightfee)的 `plugins` 表记录,找出字段差异
|
||||||
- [x] **Final**: 输出 `council-output/EDITOR_RESEARCH.md` 并给出明确推荐 `[Done: FrontendDev]` ✅ — Q1+Q2 完成,推荐:钩子注入+表单可视化编辑器
|
- [ ] **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 乱码根因分析
|
||||||
|------|------|------|
|
|
||||||
| **Round 1(规划)** | 各自创建 plan.md | 所有成员 |
|
**假设**:数据库 `vrt_power.name` 或 `plugins.title` 字段存储的是经过两次编码的中文字符串:
|
||||||
| **Round 2(执行)** | 深入调研 + 代码级确认 | FrontendDev + BackendArchitect |
|
- 原始中文 `票务` → UTF-8 字节 → 被当作 Latin1 存储 → 解码时又用 UTF-8 → 变成 `票务`
|
||||||
| **Round 3(综合)** | 输出 EDITOR_RESEARCH.md + 最终推荐 | FrontendDev |
|
|
||||||
|
**两种可能路径**:
|
||||||
|
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 有基础过滤)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 依赖关系
|
## 依赖关系
|
||||||
|
|
||||||
- Q2(BackendArchitect)先完成,后端替换可行性影响前端方案选择
|
- S1.1–S1.7 全部完成前,不进行 S2.x 修复
|
||||||
- Q1.1 调研 ShopXO DIY 组件是 Q1.2 的前置
|
- S2.2 和 S2.3 可并行执行
|
||||||
|
- S3.x 需要在 main 分支合并后执行
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 调研路径
|
## claim 状态
|
||||||
|
|
||||||
### Q1 调研路径(FrontendDev)
|
| 任务 | claim |
|
||||||
|
|------|-------|
|
||||||
1. 检查 `shopxo/` 目录中是否存在 DIY JSON 编辑器组件
|
| S1.1–S1.7 | [Claimed: council/SecurityEngineer] |
|
||||||
- `static/diy/js/entry/index-*.js` — Vue3 SPA 组件
|
| S2.1–S2.4 | [Claimed: council/SecurityEngineer] |
|
||||||
- `custom` 组件类型参考
|
| S3.x | [Claimed: council/SecurityEngineer] |
|
||||||
2. 如果无现成组件,评估 Vue3 + JSON Schema form 实现方案
|
|
||||||
3. 对比 JSON 单表 vs 拆多表方案的开发和维护成本
|
|
||||||
|
|
||||||
### Q2 调研路径(BackendArchitect)
|
|
||||||
|
|
||||||
1. 检查 `app/admin/controller/Goods.php` 中 SaveInfo() 和 Save() 方法
|
|
||||||
2. 确认 `plugins_view_admin_goods_save` 钩子调用位置和可替换性
|
|
||||||
3. 验证替换后数据能否正常保存
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BackendArchitect Round 2 深入分析(Q2)
|
## GitHub 参考插件分析
|
||||||
|
|
||||||
详细分析见 `council-output/EDITOR_RESEARCH.md`(Q2 部分)。
|
如果有时间,可检查以下插件结构(ShopXO 应用商店中已验证可用的后台插件):
|
||||||
|
1. `freightfee` — 根目录 Admin.php,直接继承 think\Controller
|
||||||
|
2. `answers` — freightfee 子插件,同一结构
|
||||||
|
3. 任何带 `admin/view/` 的插件
|
||||||
|
|
||||||
**核心结论**:
|
**关键发现**:所有正常工作的 ShopXO 插件都使用根目录 Admin.php 模式,不使用子目录控制器。
|
||||||
1. `plugins_view_admin_goods_save` 在 `SaveInfo()` 中位于模板渲染**之前**被调用,结果注入 `$assign['plugins_view_admin_goods_save_data']`
|
|
||||||
2. 钩子仅是**注入点**,不是**替换点**——注入位置在 base tab 的 `<div class="am-form-group">` 内,form/tabs/核心字段无法被替换
|
|
||||||
3. 完全替换需覆盖核心 `saveinfo.html`,失去 ShopXO 升级兼容性
|
|
||||||
4. `Goods::Save()` 数据源是标准 `$_POST`(`$this->data_request`),任何自定义表单都能提交
|
|
||||||
5. `plugins_service_goods_save_handle` 钩子以引用接收 `$data`,插件可修改或阻止保存流程
|
|
||||||
|
|
||||||
**推荐**:钩子注入(注入 ticket 专属表单 + JSON Schema 编辑器)+ 两条数据保存路径(填最小字段走标准流,或自行处理返回)
|
---
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 必须截图验证所有修复
|
||||||
| 任务 | 状态 |
|
- 先解决路由(P2),再解决乱码(P1)
|
||||||
|------|------|
|
- 不在无充分证据时修改数据库字段
|
||||||
| Q1.1 | [Done: FrontendDev] |
|
|
||||||
| Q1.2 | [Done: FrontendDev] |
|
|
||||||
| Q1.3 | [Done: FrontendDev] |
|
|
||||||
| Q2 | [Done: BackendArchitect] |
|
|
||||||
| Final Output | [Done: FrontendDev] — Q1+Q2 complete, final recommendation added |
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue