import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; const ORIG_ENV = { ...process.env }; beforeEach(() => { vi.resetModules(); process.env.PUBLIC_CMS_URL = 'https://cms.example.com'; }); afterEach(() => { process.env = { ...ORIG_ENV }; }); async function loadResolver() { const mod = await import('./cms-source.server'); return mod.resolveCmsSource; } describe('resolveCmsSource', () => { it('accepts asset filename', async () => { const resolve = await loadResolver(); expect(resolve('photo.jpg')).toBe('https://cms.example.com/api/assets/photo.jpg'); }); it('accepts absolute path on CMS host', async () => { const resolve = await loadResolver(); expect(resolve('/api/assets/foo.png')).toBe('https://cms.example.com/api/assets/foo.png'); }); it('accepts full URL on CMS host', async () => { const resolve = await loadResolver(); expect(resolve('https://cms.example.com/some/path')).toBe('https://cms.example.com/some/path'); }); it('rejects foreign host', async () => { const resolve = await loadResolver(); expect(resolve('https://attacker.com/exploit.jpg')).toBeNull(); }); it('rejects http when CMS uses https', async () => { const resolve = await loadResolver(); expect(resolve('http://cms.example.com/foo')).toBeNull(); }); it('rejects protocol-relative URLs', async () => { const resolve = await loadResolver(); expect(resolve('//attacker.com/x.jpg')).toBeNull(); }); it('rejects path traversal in filename', async () => { const resolve = await loadResolver(); expect(resolve('../etc/passwd')).toBeNull(); expect(resolve('./hidden')).toBeNull(); }); it('encodes "javascript:" payloads as harmless asset filename', async () => { const resolve = await loadResolver(); // Kein Schema-Marker (kein "://") → wird als Filename behandelt und // url-encoded an /api/assets angehängt. Server liefert 404 zurück, // keine Code-Ausführung möglich. expect(resolve('javascript:alert(1)')).toContain('/api/assets/javascript%3Aalert'); }); });