| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 |
- 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',
- );
- });
|