import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => { const agentInstances: Array<{ options: Record; close: ReturnType; }> = []; return { lookup: vi.fn(), fetch: vi.fn(), agentInstances, }; }); vi.mock('node:dns', () => ({ promises: { lookup: mocks.lookup }, })); vi.mock('undici', () => ({ Agent: class MockAgent { readonly close = vi.fn().mockResolvedValue(undefined); constructor(readonly options: Record) { mocks.agentInstances.push(this); } }, fetch: mocks.fetch, })); const originalAllowLocalNetworks = process.env.ALLOW_LOCAL_NETWORKS; describe('public URL pinned fetch', () => { beforeEach(() => { vi.resetModules(); mocks.lookup.mockReset(); mocks.fetch.mockReset(); mocks.agentInstances.length = 0; delete process.env.ALLOW_LOCAL_NETWORKS; }); afterEach(() => { if (originalAllowLocalNetworks === undefined) { delete process.env.ALLOW_LOCAL_NETWORKS; } else { process.env.ALLOW_LOCAL_NETWORKS = originalAllowLocalNetworks; } }); it('accepts only ordinary global-unicast IP literals', async () => { const { resolvePublicTarget } = await import('@/lib/server/public-url-fetch'); await expect(resolvePublicTarget('https://8.8.8.8/media.png')).resolves.toMatchObject({ hostname: '8.8.8.8', addresses: [{ address: '8.8.8.8', family: 4 }], }); await expect( resolvePublicTarget('https://[2606:4700:4700::1111]/media.png'), ).resolves.toMatchObject({ hostname: '2606:4700:4700::1111', addresses: [{ address: '2606:4700:4700::1111', family: 6 }], }); for (const target of [ 'http://127.0.0.1/', 'http://100.100.100.200/latest/meta-data/', 'http://198.18.0.1/', 'http://203.0.113.10/', 'http://224.0.0.1/', 'http://[::ffff:127.0.0.1]/', 'http://[2001:db8::1]/', 'http://[2002:0808:0808::]/', 'http://[2001:4860:0:1::5efe:10.0.0.1]/', ]) { await expect(resolvePublicTarget(target)).rejects.toMatchObject({ code: 'BLOCKED_TARGET', }); } expect(mocks.lookup).not.toHaveBeenCalled(); }); it('rejects a hostname if any DNS answer is not global-unicast', async () => { mocks.lookup.mockResolvedValue([ { address: '93.184.216.34', family: 4 }, { address: '100.100.100.200', family: 4 }, ]); const { resolvePublicTarget } = await import('@/lib/server/public-url-fetch'); await expect(resolvePublicTarget('https://mixed.example/media')).rejects.toMatchObject({ code: 'BLOCKED_TARGET', }); }); it('never honors ALLOW_LOCAL_NETWORKS for the public media fetch policy', async () => { process.env.ALLOW_LOCAL_NETWORKS = 'true'; mocks.lookup.mockResolvedValue([{ address: '192.168.1.20', family: 4 }]); const { resolvePublicTarget } = await import('@/lib/server/public-url-fetch'); await expect(resolvePublicTarget('http://10.0.0.1/file')).rejects.toMatchObject({ code: 'BLOCKED_TARGET', }); await expect(resolvePublicTarget('https://internal.example/file')).rejects.toMatchObject({ code: 'BLOCKED_TARGET', }); }); it('pins the connector to the first verified DNS result set without a second lookup', async () => { mocks.lookup .mockResolvedValueOnce([ { address: '93.184.216.34', family: 4 }, { address: '2606:2800:220:1:248:1893:25c8:1946', family: 6 }, ]) // A validate-then-fetch implementation would consume this rebinding answer. .mockResolvedValueOnce([{ address: '127.0.0.1', family: 4 }]); mocks.fetch.mockResolvedValue(new Response('media', { status: 200 })); const { fetchPinnedPublicUrl } = await import('@/lib/server/public-url-fetch'); const result = await fetchPinnedPublicUrl('https://rebind.example/media.png'); expect(mocks.lookup).toHaveBeenCalledTimes(1); expect(mocks.fetch).toHaveBeenCalledOnce(); expect(String(mocks.fetch.mock.calls[0][0])).toBe('https://rebind.example/media.png'); const agentOptions = mocks.agentInstances[0].options as { connect: { lookup: ( hostname: string, options: { all: boolean }, callback: (error: Error | null, addresses: unknown) => void, ) => void; }; }; const pinnedAddresses = await new Promise((resolve, reject) => { agentOptions.connect.lookup('rebind.example', { all: true }, (error, addresses) => { if (error) reject(error); else resolve(addresses); }); }); expect(pinnedAddresses).toEqual([ { address: '93.184.216.34', family: 4 }, { address: '2606:2800:220:1:248:1893:25c8:1946', family: 6 }, ]); expect(mocks.lookup).toHaveBeenCalledTimes(1); await result.dispose(); expect(mocks.agentInstances[0].close).toHaveBeenCalledOnce(); }); it('fails a pinned lookup if a connector asks for a different hostname', async () => { mocks.lookup.mockResolvedValue([{ address: '93.184.216.34', family: 4 }]); const { createPinnedLookup, resolvePublicTarget } = await import('@/lib/server/public-url-fetch'); const target = await resolvePublicTarget('https://safe.example/media'); const pinnedLookup = createPinnedLookup(target); const error = await new Promise((resolve) => { pinnedLookup('changed.example', { all: true }, (lookupError) => resolve(lookupError)); }); expect(error).toMatchObject({ code: 'ENOTFOUND' }); }); it('closes the dispatcher and returns a generic typed error when transport fails', async () => { mocks.lookup.mockResolvedValue([{ address: '93.184.216.34', family: 4 }]); mocks.fetch.mockRejectedValue(new Error('connect ECONNREFUSED 10.0.0.8:443')); const { fetchPinnedPublicUrl } = await import('@/lib/server/public-url-fetch'); await expect(fetchPinnedPublicUrl('https://safe.example/media')).rejects.toMatchObject({ code: 'UPSTREAM_FAILURE', message: 'Unable to fetch upstream media', }); expect(mocks.agentInstances[0].close).toHaveBeenCalledOnce(); }); it('passes trusted authentication headers without allowing Host to be overridden', async () => { mocks.lookup.mockResolvedValue([{ address: '93.184.216.34', family: 4 }]); mocks.fetch.mockResolvedValue(new Response('ok')); const { fetchPinnedPublicUrl } = await import('@/lib/server/public-url-fetch'); const result = await fetchPinnedPublicUrl('https://safe.example/models', { headers: { Authorization: 'Bearer secret' }, }); expect(mocks.fetch).toHaveBeenCalledWith( new URL('https://safe.example/models'), expect.objectContaining({ headers: { Authorization: 'Bearer secret' }, redirect: 'manual', }), ); await result.dispose(); await expect( fetchPinnedPublicUrl('https://safe.example/models', { headers: { Host: 'internal.example' }, }), ).rejects.toMatchObject({ code: 'INVALID_URL' }); await expect( fetchPinnedPublicUrl('https://safe.example/models', { headers: { 'Content-Length': '1' }, }), ).rejects.toMatchObject({ code: 'INVALID_URL' }); expect(mocks.fetch).toHaveBeenCalledTimes(1); }); it('supports a bounded PUT through the same pinned dispatcher', async () => { mocks.lookup.mockResolvedValue([{ address: '93.184.216.34', family: 4 }]); mocks.fetch.mockResolvedValue(new Response('', { status: 200 })); const { fetchPinnedPublicUrl } = await import('@/lib/server/public-url-fetch'); const body = Buffer.from('document bytes'); const result = await fetchPinnedPublicUrl('https://upload.example/presigned?signature=secret', { method: 'PUT', body, maxRequestBytes: body.byteLength, maxResponseBytes: 1024, headersTimeoutMs: 180_000, bodyTimeoutMs: 60_000, }); expect(mocks.lookup).toHaveBeenCalledTimes(1); expect(mocks.fetch).toHaveBeenCalledWith( new URL('https://upload.example/presigned?signature=secret'), expect.objectContaining({ method: 'PUT', body, redirect: 'manual', }), ); expect(mocks.agentInstances[0].options).toMatchObject({ headersTimeout: 180_000, bodyTimeout: 60_000, maxResponseSize: 1024, }); await result.dispose(); expect(mocks.agentInstances[0].close).toHaveBeenCalledOnce(); }); it('rejects an oversized PUT before DNS or transport work', async () => { const { fetchPinnedPublicUrl } = await import('@/lib/server/public-url-fetch'); await expect( fetchPinnedPublicUrl('https://upload.example/presigned', { method: 'PUT', body: Buffer.from('too large'), maxRequestBytes: 1, }), ).rejects.toMatchObject({ code: 'REQUEST_TOO_LARGE' }); expect(mocks.lookup).not.toHaveBeenCalled(); expect(mocks.fetch).not.toHaveBeenCalled(); expect(mocks.agentInstances).toHaveLength(0); }); });