fix(security): 修复上传名单文件可被 URL 直连访问(nginx 下可 getshell)
严重漏洞:roster_import 使用 $_FILES 的原始扩展名拼 roster_YYYYMMDD_HHMMSS.<ext>
落在 Web 根目录内的 data/uploads/,既无扩展名白名单(可上传 .php),文件名又
可按秒枚举;而 bootstrap 只写了 data/.htaccess,nginx 不读 .htaccess,
生产环境(nginx + PHP-FPM)下可直接远程代码执行。
修复(api.php roster_import 四层防护):
- 扩展名白名单:仅 xlsx / xlsm / csv / txt
- 随机文件名:bin2hex(random_bytes(16)),杜绝枚举
- 解析完成后 @unlink 立即删除,Web 目录下不留存任何上传文件
- 20MB 大小限制 + UPLOAD_ERR 检查
加固(bootstrap.php):data 及 cache/uploads/snapshots 每个目录都写
.htaccess(Deny from all)+ index.html 占位;README 与文档补充 nginx
location ^~ /data/ { deny all; return 404; } 规则。
测试:新增 e2e tools/test-upload-guard.js(11 断言,覆盖 .php/.phtml 被拒、
无残留、合法 csv 导入成功且即删、无可枚举文件名),并入 tests/run-all.sh。
全套 90 项通过。
docs: 新增 VitePress 文档站(18 页)
- 使用指南 12 页:简介/部署/快速上手/系统设置/名单管理/统计口径/试卷设置/
快照趋势异常/导出打印/数据看板/随机抽取/常见问题
- 安全 2 页:安全总览(含本次漏洞复盘)与部署加固清单(P0-P2)
- 开发 4 页:架构与目录/接口一览/SQLite 存储/测试
- 配图迁移至 docs/public/images/;.gitignore 忽略文档构建产物
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
# 快照、趋势与异常检测
|
||||
|
||||
这一组功能用于**跨时间**看成绩,而不是只看当前这一刻。
|
||||
|
||||
## 快照
|
||||
|
||||
工具栏 **存快照** 打开快照面板,填写标签(留空自动用「快照 MM-DD HH:II」)后保存。
|
||||
|
||||
一份快照包含:
|
||||
|
||||
- 保存时间与标签;
|
||||
- 试卷 ID 与名称;
|
||||
- 当时的整体统计(总人数、已提交、平均分、合格率……);
|
||||
- 每名学生的 `学号 / 姓名 / 班级 / 组别 / 成绩 / 最高分 / 考试次数 / 最近提交时间`(按学号排序)。
|
||||
|
||||
存储位置:`data/snapshots/<projectId>/<unix秒>_<md5(标签)>.json`。
|
||||
|
||||
快照**按试卷分目录**,所以不同试卷的快照互不干扰。
|
||||
|
||||
| 操作 | 位置 |
|
||||
| --- | --- |
|
||||
| 保存当前快照 | 快照弹窗 → 保存当前快照 |
|
||||
| 刷新列表 | 快照弹窗 → 刷新列表 |
|
||||
| 删除某份 | 列表每行的删除按钮 |
|
||||
|
||||
## 趋势
|
||||
|
||||
**趋势** 面板把同一试卷的多份快照横向铺开,按学号对齐,可以直接看出每名学生
|
||||
在几个时间点上的分数变化(上升 / 下降 / 缺考)。
|
||||
|
||||
典型用法:
|
||||
|
||||
1. 月考卷 A 考完 → 存快照「10月月考」;
|
||||
2. 期中卷 B 考完 → 存快照「期中」;
|
||||
3. 打开趋势,逐人对比两次成绩。
|
||||
|
||||
## 异常检测
|
||||
|
||||
工具栏 **异常** 打开异常线索面板。它**不是判定作弊**,只是把值得人工复核的对象挑出来,
|
||||
避免在上千行数据里肉眼找。
|
||||
|
||||
### 短时长高分
|
||||
|
||||
命中条件:
|
||||
|
||||
```
|
||||
有时长记录
|
||||
且 时长 < max(60秒, 平均时长 × 40%)
|
||||
且 成绩 ≥ 满分提示值 × 85%
|
||||
```
|
||||
|
||||
> 「满分提示值」取当前数据中的最高分(`max_score`),至少为 1。
|
||||
|
||||
### 高频重考
|
||||
|
||||
命中条件:`考试次数 ≥ max(4, ceil(平均考试次数 + 3))`
|
||||
|
||||
即:至少 4 次才算高频,且要明显高于整体平均水平。
|
||||
|
||||
### 雷同成绩
|
||||
|
||||
同一分数出现 **≥ 3 人** 时列为疑似,按人数降序排列并列出成员。
|
||||
|
||||
::: warning 请人工复核
|
||||
异常检测基于统计特征,误报很正常(例如题目全是选择题时同分很常见)。
|
||||
请把它当作"值得看一眼"的线索,不要直接作为处理依据。
|
||||
:::
|
||||
|
||||
## 相关
|
||||
|
||||
- [成绩明细与统计口径](./scores.md)
|
||||
- [导出与打印](./export-print.md)
|
||||
Reference in New Issue
Block a user