## 文件上传（High）

### 触发信号

- 文件上传接口（`multer`、`multipart`、`upload`、`formidable`、`@UploadFile`）
- 接收 `multipart/form-data` 的路由处理器
- 文件存储操作（`fs.writeFile`、`move`、`save`）紧跟文件接收

### 关键 Sink

| 漏洞类型 | 检查点 |
|---------|--------|
| 文件类型绕过 | MIME 类型验证、扩展名白名单 |
| 文件内容绕过 | Magic bytes 校验、文件内容扫描 |
| 存储路径穿越 | 上传路径拼接用户输入 |
| 文件大小 | Content-Length/size 校验 |
| 文件名注入 | 文件名特殊字符过滤 |

### 判定流程

1. **识别上传入口**：`Read` 提取文件上传中间件和处理器，标记上传处理函数
2. **检查文件类型验证**：`Read` 确认是否有以下验证（需要同时具备）：
   - 扩展名白名单（仅允许 `.jpg`/`.png` 等，非黑名单）
   - MIME 类型校验（`file.mimetype` 检查）
   - Magic bytes 校验（文件头魔数验证，防止扩展名伪造）
3. **检查存储安全**：`Read` 确认文件存储策略：
   - 存储路径是否使用了安全的文件名（UUID 重命名 vs 保留原始文件名）
   - 是否存储在 Web 根目录外（防止直接访问上传文件执行脚本）
   - 存储路径是否可被路径穿越（文件名是否包含 `../`）
4. **检查文件大小限制**：`Read` 确认是否有大小上限（`limits.fileSize`、`maxSize`）
5. **检查文件名处理**：`Read` 确认是否过滤了特殊字符（`;`、`\n`、`..`、空字节）

### 证据构造

确认缺陷后，输出需包含：
- 上传入口（路由和处理函数，含文件:行号）
- 缺失的验证（无类型校验/无大小限制/不安全的存储路径）
- 绕过场景（如双扩展名 `shell.php.jpg`、空字节 `shell.php%00.jpg`）
- 业务影响（WebShell 上传、存储耗尽、路径穿越）

### 排除

- 已使用云存储（S3/OSS）且有类型和大小限制
- 已有完整的类型验证（扩展名 + MIME + Magic bytes）
- 文件已重命名为 UUID 且存储在 Web 根目录外
- 仅内部可信用户可上传（如管理员后台）
