import test from 'node:test'; import assert from 'node:assert/strict'; import { hostMatchesAllowlist, isPrivateOrReservedAddress, validateRemoteAudioUrl, } from '../src/url-security.mjs'; test('音频域名必须匹配显式白名单', () => { assert.equal(hostMatchesAllowlist('file.yuban.co', ['file.yuban.co']), true); assert.equal(hostMatchesAllowlist('a.media.example.com', ['*.media.example.com']), true); assert.equal(hostMatchesAllowlist('media.example.com', ['*.media.example.com']), false); assert.equal(hostMatchesAllowlist('attacker.example', ['file.yuban.co']), false); }); test('阻止私网、回环和保留地址', () => { for (const address of ['127.0.0.1', '10.0.0.1', '172.16.0.1', '192.168.1.1', '::1', 'fd00::1']) { assert.equal(isPrivateOrReservedAddress(address), true, address); } assert.equal(isPrivateOrReservedAddress('1.1.1.1'), false); assert.equal(isPrivateOrReservedAddress('2606:4700:4700::1111'), false); }); test('允许白名单 HTTPS 公网地址', async () => { const url = await validateRemoteAudioUrl( 'https://file.yuban.co/audio/test.mp3', ['file.yuban.co'], async () => [{ address: '1.1.1.1', family: 4 }], ); assert.equal(url.hostname, 'file.yuban.co'); }); test('拒绝非 HTTPS 和私网解析', async () => { await assert.rejects( validateRemoteAudioUrl( 'http://file.yuban.co/test.mp3', ['file.yuban.co'], async () => [{ address: '1.1.1.1', family: 4 }], ), error => error.code === 'INVALID_AUDIO_URL', ); await assert.rejects( validateRemoteAudioUrl( 'https://file.yuban.co/test.mp3', ['file.yuban.co'], async () => [{ address: '127.0.0.1', family: 4 }], ), error => error.code === 'AUDIO_ADDRESS_NOT_ALLOWED', ); });