fix(security): 认证/越权/注入/上传/凭据全链路加固
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复: - JWT 生产必填、密码 8-72 字节、防枚举;全局 ValidationPipe - classes/dashboard/schedules/students/attendance/archive/exams 越权与 IDOR 修复 - LIKE 通配符转义(14 处);上传 10MB 上限 + MIME 白名单 + 附件 XSS - 集成配置 appSecret AES 加密 + 回填脚本;审计 best-effort;IP 来源防伪造 Reviewed-by: OCR (open-codereview.ai)
This commit is contained in:
52
apps/server/src/common/mime.spec.ts
Normal file
52
apps/server/src/common/mime.spec.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { normalizeMimeType, isInlineSafeMimeType } from './mime';
|
||||
|
||||
describe('normalizeMimeType', () => {
|
||||
it.each([
|
||||
['report.pdf', 'application/pdf'],
|
||||
['photo.png', 'image/png'],
|
||||
['pic.jpeg', 'image/jpeg'],
|
||||
['pic.jpg', 'image/jpeg'],
|
||||
['anim.webp', 'image/webp'],
|
||||
['doc.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
|
||||
['sheet.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
|
||||
['data.csv', 'text/csv'],
|
||||
['notes.txt', 'text/plain'],
|
||||
])('maps %s to %s by extension', (filename, expected) => {
|
||||
expect(normalizeMimeType(filename)).toBe(expected);
|
||||
});
|
||||
|
||||
it('is case-insensitive for the extension', () => {
|
||||
expect(normalizeMimeType('REPORT.PDF')).toBe('application/pdf');
|
||||
expect(normalizeMimeType('Photo.JPG')).toBe('image/jpeg');
|
||||
});
|
||||
|
||||
it('ignores a spoofed client mimeType', () => {
|
||||
expect(normalizeMimeType('evil.png', 'text/html')).toBe('image/png');
|
||||
expect(normalizeMimeType('evil.html', 'image/png')).toBe('application/octet-stream');
|
||||
});
|
||||
|
||||
it('falls back to octet-stream for unknown/unsafe extensions', () => {
|
||||
expect(normalizeMimeType('script.svg')).toBe('application/octet-stream');
|
||||
expect(normalizeMimeType('payload.html')).toBe('application/octet-stream');
|
||||
expect(normalizeMimeType('virus.exe')).toBe('application/octet-stream');
|
||||
expect(normalizeMimeType('noextension')).toBe('application/octet-stream');
|
||||
expect(normalizeMimeType('')).toBe('application/octet-stream');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInlineSafeMimeType', () => {
|
||||
it('allows images and pdf', () => {
|
||||
expect(isInlineSafeMimeType('application/pdf')).toBe(true);
|
||||
expect(isInlineSafeMimeType('image/png')).toBe(true);
|
||||
expect(isInlineSafeMimeType('image/jpeg')).toBe(true);
|
||||
expect(isInlineSafeMimeType('image/webp')).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects document, archive and scriptable types', () => {
|
||||
expect(isInlineSafeMimeType('application/octet-stream')).toBe(false);
|
||||
expect(isInlineSafeMimeType('text/html')).toBe(false);
|
||||
expect(isInlineSafeMimeType('image/svg+xml')).toBe(false);
|
||||
expect(isInlineSafeMimeType('text/plain')).toBe(false);
|
||||
expect(isInlineSafeMimeType('')).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user