审计记录:文件管理页全面巡检(面包屑 + 路径 + 浏览契约)
审计信息
- 日期: 2026-06-01
- 审计人: Claude (AI)
- 触发原因: 用户反馈
#/files 面包屑不可用,要求全面巡检
审计范围(实际改动文件清单)
| 文件 |
行数 |
状态 |
frontend/src/pages/FilesPage.vue |
615 |
☑ 已审(全文) |
frontend/src/utils/remotePath.ts |
72 |
☑ 已审(全文) |
frontend/src/utils/fileBrowse.ts |
51 |
☑ 已审(全文) |
frontend/src/types/api.ts |
BrowseResponse 段 |
☑ 已审 |
server/api/sync_v2.py |
browse_directory L142-216 |
☑ 已审 |
巡检结论摘要
| 问题 |
根因 |
处置 |
| 面包屑点击无效 |
Vuetify 4 不消费 props.onClick |
#item 槽 + onBreadcrumbClick(index) |
| 路径栏与列表路径不一致 |
未规范化 //、无尾斜杠 |
normalizeRemotePath 统一 |
| 空目录不显示 |
items?.length 误判 |
Array.isArray(body.items) |
| 无法返回上级 |
列表过滤 .. |
工具栏「上级」+ 面包屑 |
| 批量删除失败无反馈 |
空 catch |
统计成功/失败条数 |
审计8步结果
Step 1: 登记 ✅
变更主题:文件管理 SPA 路径导航与 browse API 契约修复。
Step 2: 全文 Read ✅
FilesPage.vue:模板 + script 全文 Read
remotePath.ts、fileBrowse.ts:全文 Read
sync_v2.py:browse_directory 及相邻 file-ops 对照 Read
Step 3: 规则扫描 H
| H# |
规则 |
文件:行号 |
说明 |
| PY-03 |
SSH 命令拼接 |
sync_v2.py:166 |
ls -la + shlex.quote(path) → 扫描 |
| PY-05 |
JWT |
sync_v2.py:146 |
Depends(get_current_admin) → 扫描 |
| PY-09 |
静默吞错 |
FilesPage.vue:427-435(改前) |
批量删除空 catch → 已改为计数 |
| FE-01 |
XSS |
FilesPage.vue:83 |
{{ item.name }} 文本插值 → 扫描 |
| FE-04 |
裸 fetch |
FilesPage.vue |
均经 http 封装 → 无命中 |
Step 4: Closure 表
| 文件 |
行号 |
规则 |
判定 |
严重度 |
理由 |
| sync_v2.py |
166 |
PY-03 |
SAFE |
— |
path 经 Pydantic + shlex.quote,不经 shell 拼接用户片段 |
| sync_v2.py |
146 |
PY-05 |
SAFE |
— |
JWT 管理员鉴权 + 审计 browse_directory |
| FilesPage.vue |
83 |
FE-01 |
SAFE |
— |
Vue 默认转义;文件名来自 SSH ls 输出 |
| FilesPage.vue |
427-438 |
PY-09 |
SAFE |
— |
批量删除逐条 try/catch 并汇总 fail 计数 |
| FilesPage.vue |
293-301(旧) |
UX/BUG |
FINDING→已修复 |
P2 |
props.onClick 在 Vuetify 4 无效;已用 #item 槽 |
Step 5: 入口表
| 方法 |
路径 |
鉴权 |
说明 |
| POST |
/api/sync/browse |
JWT get_current_admin |
远程目录列表(本次契约 items) |
| POST |
/api/sync/file-ops |
JWT |
删/改名/建目录 |
| POST |
/api/sync/read-file |
JWT |
读文件(预览/编辑) |
| POST |
/api/sync/upload |
JWT |
上传 |
| POST |
/api/sync/download |
JWT |
下载 |
| POST |
/api/sync/chmod |
JWT |
改权限 |
Step 6: 输入→Sink
| 输入 |
Sink |
防护 |
currentPath(路径栏/面包屑) |
POST /sync/browse body.path |
前端 normalizeRemotePath;后端 shlex.quote |
item.name(行点击) |
joinRemotePath → browse |
单段拼接,不含 / |
mkdirName |
file-ops mkdir |
joinRemotePath + 后端 shlex.quote |
上传 FormData |
/sync/upload |
JWT + 既有上传校验(未改) |
Step 7: 归类
Step 8: DoD ✅
FINDING 列表
无未关闭 FINDING(面包屑 P2 已在本次提交中修复)。
结论
☑ 审计通过,0 未关闭 FINDING,可部署前端构建产物至 web/app/。