import { describe, expect, it } from 'vitest' import { checkTarget, isSafeTargetSegment, uploadKey, } from '../../skills/data-portal/template/functions/api/_lib/target' /** A D1 fake whose `first()` answers from a set of published folder rows. It * models the real query's constraint rather than its happy path: a row is a * folder only when isDir = 1, so a fake that ignored the filter would let a * file path pass as an upload target. */ function fakeDb(folders: string[]) { return { prepare(sql: string) { return { bind(_accountId: string, relPath: string) { return { async first() { if (!sql.includes('isDir = 1')) throw new Error('query must filter isDir') return folders.includes(relPath) ? { relPath } : null }, } }, } }, } as never } /** Any query at all is a failure for the cases that must refuse before the * index is read. Refusing early is not an optimisation here: it is what keeps * an out-of-grant probe from costing a database round trip. */ const noQueryDb = { prepare() { throw new Error('must not query') }, } as never describe('isSafeTargetSegment', () => { it('rejects traversal, separators, empties and NUL', () => { for (const bad of ['', '.', '..', 'a/b', 'a\\b', 'a\0b']) { expect(isSafeTargetSegment(bad)).toBe(false) } }) it('accepts an ordinary folder name', () => { expect(isSafeTargetSegment('jobs')).toBe(true) }) }) describe('uploadKey', () => { it('collapses to owner/filename when untargeted', () => { expect(uploadKey('alice', '', 'a.pdf')).toBe('alice/a.pdf') }) it('carries the target so two folders never collide', () => { expect(uploadKey('alice', 'jobs', 'a.pdf')).toBe('alice/jobs/a.pdf') expect(uploadKey('alice', 'jobs/2026', 'a.pdf')).toBe('alice/jobs/2026/a.pdf') }) }) describe('checkTarget', () => { it('admits the empty target without touching the index', async () => { expect(await checkTarget(noQueryDb, 'acct', [], '')).toEqual({ ok: true }) }) it('admits a granted folder that the index publishes', async () => { expect(await checkTarget(fakeDb(['jobs']), 'acct', [], 'jobs')).toEqual({ ok: true }) }) it('admits a nested path under a granted top-level folder', async () => { expect(await checkTarget(fakeDb(['jobs/2026']), 'acct', ['jobs'], 'jobs/2026')).toEqual({ ok: true, }) }) it('refuses a folder outside the grant before reading the index', async () => { expect(await checkTarget(noQueryDb, 'acct', ['output'], 'jobs')).toEqual({ ok: false, reason: 'grant', }) }) it('refuses a folder the index does not publish', async () => { expect(await checkTarget(fakeDb(['jobs']), 'acct', [], 'output')).toEqual({ ok: false, reason: 'index', }) }) it('refuses a traversal segment before any lookup', async () => { expect(await checkTarget(noQueryDb, 'acct', [], 'jobs/../secrets')).toEqual({ ok: false, reason: 'segment', }) }) it('refuses a trailing separator, which is an empty segment', async () => { expect(await checkTarget(noQueryDb, 'acct', [], 'jobs/')).toEqual({ ok: false, reason: 'segment', }) }) })