Files
FileHost/server/utils/access-isolation.test.ts
2026-10-27 11:03:36 +08:00

67 lines
2.2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { validateImageKey } from './image-key'
/**
* Mirrors assertImageOwnership rules (access.ts) for documentation + regression.
*/
function canOperateImage(
actor: { type: 'user', id: number, role: 'user' | 'admin' }
| { type: 'user-token', id: number }
| { type: 'global-token' },
ownerId: number | null,
primaryAdminId = 1
): boolean {
if (actor.type === 'global-token') {
return ownerId !== null && ownerId === primaryAdminId
}
if (actor.type === 'user-token') {
return ownerId === actor.id
}
return ownerId !== null && ownerId === actor.id
}
describe('image ownership rules', () => {
it('regular user can only operate own images', () => {
const user1 = { type: 'user' as const, id: 1, role: 'user' as const }
expect(canOperateImage(user1, 1)).toBe(true)
expect(canOperateImage(user1, 2)).toBe(false)
expect(canOperateImage(user1, null)).toBe(false)
})
it('admin can only operate own images', () => {
const admin = { type: 'user' as const, id: 1, role: 'admin' as const }
expect(canOperateImage(admin, 1)).toBe(true)
expect(canOperateImage(admin, 2)).toBe(false)
expect(canOperateImage(admin, null)).toBe(false)
})
it('user API token is scoped to owner', () => {
const token = { type: 'user-token' as const, id: 2 }
expect(canOperateImage(token, 2)).toBe(true)
expect(canOperateImage(token, 1)).toBe(false)
expect(canOperateImage(token, null)).toBe(false)
})
it('global API token is scoped to the primary admin owner', () => {
const token = { type: 'global-token' as const }
expect(canOperateImage(token, 1)).toBe(true)
expect(canOperateImage(token, 2)).toBe(false)
expect(canOperateImage(token, null)).toBe(false)
})
})
describe('path traversal hardening', () => {
it('rejects keys that could escape data dir', () => {
const attacks = [
'images/2026/08/../secret.webp',
'images/../../../etc/passwd',
'images//2026/08/a.webp',
'/images/2026/08/a.webp',
'images/2026/08/%2e%2e/other.webp'
]
for (const key of attacks) {
expect(validateImageKey(key), key).toBe(false)
}
})
})