加固历史恢复入口默认禁用

This commit is contained in:
WuYongJie 2026-05-30 15:21:42 +08:00
parent 2e926451a4
commit 5be1d5ba45
3 changed files with 114 additions and 3 deletions

View File

@ -0,0 +1,87 @@
import { NextRequest } from 'next/server'
jest.mock('@/lib/prisma', () => ({
prisma: {
user: {
create: jest.fn(),
findUnique: jest.fn(),
},
category: {
create: jest.fn(),
},
scene: {
create: jest.fn(),
},
material: {
create: jest.fn(),
},
},
}))
function createRequest(token?: string) {
return new NextRequest('http://localhost/api/restore-data', {
method: 'POST',
headers: token ? { 'x-restore-data-token': token } : undefined,
})
}
async function loadPost() {
const route = await import('../route')
return route.POST
}
describe('POST /api/restore-data', () => {
const originalEnv = process.env
beforeEach(() => {
jest.resetModules()
process.env = { ...originalEnv }
delete process.env.RESTORE_DATA_ENABLED
delete process.env.RESTORE_DATA_TOKEN
delete process.env.RESTORE_ADMIN_PASSWORD
})
afterAll(() => {
process.env = originalEnv
})
it('默认未显式启用时拒绝执行', async () => {
process.env.RESTORE_DATA_TOKEN = 'restore-token'
process.env.RESTORE_ADMIN_PASSWORD = 'configured-password'
const POST = await loadPost()
const response = await POST(createRequest('restore-token'))
const data = await response.json()
expect(response.status).toBe(503)
expect(data.success).toBe(false)
expect(data.message).toContain('RESTORE_DATA_ENABLED=1')
})
it('启用后仍要求配置恢复令牌', async () => {
process.env.RESTORE_DATA_ENABLED = '1'
process.env.RESTORE_ADMIN_PASSWORD = 'configured-password'
const POST = await loadPost()
const response = await POST(createRequest())
const data = await response.json()
expect(response.status).toBe(503)
expect(data.success).toBe(false)
expect(data.message).toContain('RESTORE_DATA_TOKEN')
})
it('启用且配置令牌后会拒绝错误令牌', async () => {
process.env.RESTORE_DATA_ENABLED = '1'
process.env.RESTORE_DATA_TOKEN = 'restore-token'
process.env.RESTORE_ADMIN_PASSWORD = 'configured-password'
const POST = await loadPost()
const response = await POST(createRequest('wrong-token'))
const data = await response.json()
expect(response.status).toBe(401)
expect(data.success).toBe(false)
expect(data.message).toBe('恢复令牌无效')
})
})

View File

@ -9,6 +9,7 @@ import bcrypt from 'bcryptjs';
const UPLOADS_DIR = '/app/uploads';
const S3_BUCKET_URL = process.env.RESTORE_S3_BUCKET_URL as string;
const S3_PUBLIC_URL = process.env.RESTORE_S3_PUBLIC_URL as string;
const RESTORE_DATA_ENABLED = process.env.RESTORE_DATA_ENABLED?.trim() === '1';
const RESTORE_DATA_TOKEN = process.env.RESTORE_DATA_TOKEN?.trim() || '';
// 默认配置
@ -107,6 +108,13 @@ function matchFiles(previewFiles: string[], materialFiles: string[]): Match[] {
export async function POST(req: NextRequest) {
try {
if (!RESTORE_DATA_ENABLED) {
return NextResponse.json({
success: false,
message: '未显式启用 RESTORE_DATA_ENABLED=1,恢复接口已禁用',
}, { status: 503 });
}
if (!RESTORE_DATA_TOKEN) {
return NextResponse.json({
success: false,

View File

@ -539,7 +539,7 @@
- 已修复:`failWithCleanup()` 会在模板校验、数据库写入等失败路径调用 `cleanupUploadedArtifacts()`。
- 已修复:成功创建素材记录后会清空待清理列表,避免误删正常素材文件。
- 已修复:上传入口会在处理预览图、展示图和原始素材文件前先确认模板存在且启用。模板已停用或不存在时会直接拒绝,不再让客户等大文件上传完才失败,也不会写入对象存储。
- 仍需后续处理:上传过程仍有整文件进内存问题,归到 P1-11。
- 仍需后续处理:大文件内存风险已在 P1-11 做过三轮流式收口;当前剩余重点是预览图、PSD 展示图限制,以及后续分片上传/断点续传。
建议动作:
@ -888,6 +888,22 @@ AI 改图以后会消耗外部额度、CPU、存储和客户素材数据。即
- 已完成:删除公开 debug API 路由。
- 已完成:增加生产路由边界测试,后续需要诊断模板结构时应走登录后的管理员页面或受控脚本,不再开放匿名 debug API。
### P1-22 历史数据恢复接口是强写入口(已完成默认禁用加固)
客户影响:
`/api/restore-data` 是旧的一次性数据恢复入口,会创建管理员、默认分类、默认场景,并把本地文件和外部 S3 预览图写回素材表。它不属于普通后台操作,如果客户环境误留恢复令牌,等于留下一个绕过后台登录体系的强写入口。
证据:
- `app/api/restore-data/route.ts`:恢复入口现在必须同时满足 `RESTORE_DATA_ENABLED=1` 和 `RESTORE_DATA_TOKEN` 正确才会执行;默认未显式启用时直接返回禁用。
- `app/api/restore-data/__tests__/route.test.ts`:新增测试覆盖默认禁用、启用后缺少令牌仍拒绝、错误令牌拒绝。
建议动作:
- 已完成:恢复接口默认显式禁用,避免只因误留 token 就开放历史强写入口。
- 后续建议:正式交付包如不再需要该历史恢复能力,应单独评估物理移除或迁移到离线脚本。
## 5. P2 问题
### P2-01 监控页普通登录用户可进入(已修复)
@ -1339,8 +1355,8 @@ NAS 私有化系统可能在平板或窄屏浏览器里使用。模板编辑器
1. 统一超管、全局管理员、部门管理员、普通用户权限 helper。
2. 系统设置、用户管理、破坏性操作拆权限。
3. 统一素材编辑权限。(已完成第一轮)
4. 模板管理增加免费版升级提示。
5. 模板管理增加免费版升级提示。
4. 模板管理增加免费版升级提示。(已完成)
5. 继续确认系统监控、转码统计、模板管理可见性是否需要按部门范围裁剪或仅最高权限可见。
并行推进的前端整改顺序: