import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { safeFetch, SafeFetchError } from '../../lib/ingest/safe_fetch.js'; beforeEach(() => { delete process.env.VOID_INGEST_ALLOW_PRIVATE; }); afterEach(() => { vi.restoreAllMocks(); }); describe('safeFetch', () => { it('rejects file:// scheme', async () => { await expect(safeFetch('file:///etc/passwd')).rejects.toThrow(SafeFetchError); }); it('rejects literal loopback IP', async () => { await expect(safeFetch('http://127.0.0.1/x')).rejects.toThrow(/blocked/); }); it('rejects literal RFC1918 IP', async () => { await expect(safeFetch('http://192.168.1.1/x')).rejects.toThrow(/blocked/); }); it('rejects literal CGNAT IP', async () => { await expect(safeFetch('http://100.64.0.1/x')).rejects.toThrow(/blocked/); }); it('rejects AWS metadata literal IP', async () => { await expect(safeFetch('http://169.254.169.254/latest/meta-data/')).rejects.toThrow(/blocked/); }); it('allows literal IPs when VOID_INGEST_ALLOW_PRIVATE=true (test fixtures)', async () => { process.env.VOID_INGEST_ALLOW_PRIVATE = 'true'; global.fetch = vi.fn(async () => new Response('ok', { status: 200 })); const res = await safeFetch('http://127.0.0.1:65535/'); expect(res.status).toBe(200); }); });