Vue 3 + Vite + Tailwind frontend, Fastify API server, execa shell bridge. All components built, server validated, 41 unit tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
36 lines
1.2 KiB
JavaScript
36 lines
1.2 KiB
JavaScript
import { describe, it, expect } from 'vitest'
|
|
import path from 'node:path'
|
|
|
|
const SAFE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.tiff', '.tif', '.avif'])
|
|
|
|
function sanitizeExt(filename) {
|
|
const rawExt = path.extname(filename || '').toLowerCase()
|
|
return SAFE_EXTS.has(rawExt) ? rawExt : '.jpg'
|
|
}
|
|
|
|
describe('extension sanitization', () => {
|
|
it('allows known image extensions', () => {
|
|
expect(sanitizeExt('photo.png')).toBe('.png')
|
|
expect(sanitizeExt('photo.jpg')).toBe('.jpg')
|
|
expect(sanitizeExt('photo.WEBP')).toBe('.webp')
|
|
})
|
|
|
|
it('falls back to .jpg for dangerous extensions', () => {
|
|
expect(sanitizeExt('exploit.svg')).toBe('.jpg')
|
|
expect(sanitizeExt('exploit.mvg')).toBe('.jpg')
|
|
expect(sanitizeExt('exploit.msl')).toBe('.jpg')
|
|
expect(sanitizeExt('exploit.sh')).toBe('.jpg')
|
|
expect(sanitizeExt('exploit.eps')).toBe('.jpg')
|
|
})
|
|
|
|
it('falls back to .jpg for missing or empty filename', () => {
|
|
expect(sanitizeExt('')).toBe('.jpg')
|
|
expect(sanitizeExt(null)).toBe('.jpg')
|
|
})
|
|
|
|
it('ignores path components — only extension is used', () => {
|
|
expect(sanitizeExt('../../etc/passwd.png')).toBe('.png')
|
|
expect(sanitizeExt('../../etc/passwd')).toBe('.jpg')
|
|
})
|
|
})
|