41 lines
2.3 KiB
TypeScript
41 lines
2.3 KiB
TypeScript
/** @jest-environment node */
|
|
import { mkdtemp, rm } from 'fs/promises'
|
|
import os from 'os'
|
|
import path from 'path'
|
|
import { deleteDetailFile, getDetailStorageRoot, readDetailFile, saveDetailFile } from '@/lib/detail-generator/storage'
|
|
|
|
describe('detail generator private storage', () => {
|
|
let root: string
|
|
const oldStorage = process.env.DETAIL_GENERATOR_STORAGE_DIR
|
|
const oldUploads = process.env.UPLOAD_DIR
|
|
beforeEach(async () => {
|
|
root = await mkdtemp(path.join(os.tmpdir(), 'detail-storage-test-'))
|
|
process.env.DETAIL_GENERATOR_STORAGE_DIR = path.join(root, 'private')
|
|
process.env.UPLOAD_DIR = path.join(root, 'uploads')
|
|
})
|
|
afterEach(async () => {
|
|
// Recursive removal is restricted to this test's freshly created temporary root.
|
|
if (path.dirname(root) !== path.resolve(os.tmpdir()) || !path.basename(root).startsWith('detail-storage-test-')) throw new Error('Unsafe test cleanup')
|
|
await rm(root, { recursive: true, force: true })
|
|
if (oldStorage === undefined) delete process.env.DETAIL_GENERATOR_STORAGE_DIR; else process.env.DETAIL_GENERATOR_STORAGE_DIR = oldStorage
|
|
if (oldUploads === undefined) delete process.env.UPLOAD_DIR; else process.env.UPLOAD_DIR = oldUploads
|
|
})
|
|
it('stores files outside generic uploads and reads the exact bytes', async () => {
|
|
const key = await saveDetailFile('project-1', Buffer.from('private bytes'), 'png')
|
|
expect(key).toMatch(/^project-1\/[a-f0-9-]{36}\.png$/)
|
|
expect((await readDetailFile(key)).toString()).toBe('private bytes')
|
|
await deleteDetailFile(key)
|
|
await expect(readDetailFile(key)).rejects.toThrow()
|
|
})
|
|
it('rejects encoded, absolute and parent traversal keys', async () => {
|
|
for (const key of ['../private.png', '/etc/passwd', 'p/%2e%2e/secret.png', 'p\\secret.png', 'http://host/image.png']) await expect(readDetailFile(key)).rejects.toThrow('无效')
|
|
await expect(saveDetailFile('../project', Buffer.from('x'), 'png')).rejects.toThrow('无效')
|
|
})
|
|
it('rejects private roots inside either public serving directory', () => {
|
|
process.env.DETAIL_GENERATOR_STORAGE_DIR = path.join(root, 'uploads', 'secret')
|
|
expect(getDetailStorageRoot).toThrow('之外')
|
|
process.env.DETAIL_GENERATOR_STORAGE_DIR = path.join(process.cwd(), 'public', 'secret')
|
|
expect(getDetailStorageRoot).toThrow('之外')
|
|
})
|
|
})
|