/** @jest-environment node */ import { execFile } from 'child_process' import { NextRequest } from 'next/server' import { getServerSession } from 'next-auth' import { GET } from '@/app/api/admin/system-logs/route' jest.mock('next-auth', () => ({ getServerSession: jest.fn(), })) jest.mock('@/lib/auth', () => ({ authOptions: {} })) jest.mock('@/lib/department-permission', () => ({ isSuperAdmin: jest.fn().mockResolvedValue(false), hasSystemAdminPermission: jest.fn((role: string, isSuperAdmin: boolean) => isSuperAdmin || role === 'ADMIN' || role === 'SYSTEM_ADMIN'), })) jest.mock('child_process', () => ({ execFile: jest.fn(), })) describe('admin system-logs route', () => { beforeEach(() => { jest.clearAllMocks() ;(getServerSession as jest.Mock).mockResolvedValue({ user: { id: 'admin-1', role: 'ADMIN' }, }) ;(execFile as unknown as jest.Mock).mockImplementation( (_file: string, _args: string[], _options: unknown, callback: Function) => callback(null, 'INFO ready\nERROR failed\n', ''), ) }) test('关键词和行数不得进入 shell,并在进程输出后过滤', async () => { const keyword = '"; touch /tmp/pwn; #' const request = new NextRequest( `http://localhost/api/admin/system-logs?lines=999999&keyword=${encodeURIComponent(keyword)}`, ) const response = await GET(request) const body = await response.json() expect(response.status).toBe(200) expect(execFile).toHaveBeenCalledWith( 'docker', ['logs', '--tail', '5000', 'youyidian-3d-material'], expect.objectContaining({ encoding: 'utf8' }), expect.any(Function), ) expect((execFile as unknown as jest.Mock).mock.calls[0][2]).not.toHaveProperty('shell') expect(JSON.stringify((execFile as unknown as jest.Mock).mock.calls[0].slice(0, 3))).not.toContain(keyword) expect(body.logs).toBe('') }) test('普通用户不得调用 docker', async () => { ;(getServerSession as jest.Mock).mockResolvedValue({ user: { id: 'user-1', role: 'USER' }, }) const response = await GET(new NextRequest('http://localhost/api/admin/system-logs')) expect(response.status).toBe(403) expect(execFile).not.toHaveBeenCalled() }) })