fix: strengthen SSRF guard in fetchUrl with DNS resolution check
Add exported isBlockedAddress(ip) helper covering loopback, 0.0.0.0, private v4 (10/8, 172.16-31, 192.168/16), link-local (169.254/16, fe80::/10), and IPv6 ULA (fc00::/7). In fetchUrl, after the existing literal-hostname fast-reject, resolve the hostname via dns.lookup (all:true) when using the real fetcher and block if any resolved address isBlockedAddress. Injected fetcher (tests) skips DNS. Drop unused contentType from return value. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import AdmZip from 'adm-zip';
|
||||
import { processFile, unpackZip, fetchUrl, MAX_FILE } from '../../lib/icons/ingest.js';
|
||||
import { processFile, unpackZip, fetchUrl, isBlockedAddress, MAX_FILE } from '../../lib/icons/ingest.js';
|
||||
|
||||
const PNG = Buffer.from([0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a, 0,0,0,0]);
|
||||
|
||||
@@ -52,6 +52,18 @@ describe('unpackZip', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBlockedAddress', () => {
|
||||
it('blocks loopback IPv4', () => { expect(isBlockedAddress('127.0.0.1')).toBe(true); });
|
||||
it('blocks private 10/8', () => { expect(isBlockedAddress('10.1.2.3')).toBe(true); });
|
||||
it('blocks private 192.168/16', () => { expect(isBlockedAddress('192.168.0.1')).toBe(true); });
|
||||
it('blocks link-local 169.254/16', () => { expect(isBlockedAddress('169.254.1.1')).toBe(true); });
|
||||
it('blocks 0.0.0.0', () => { expect(isBlockedAddress('0.0.0.0')).toBe(true); });
|
||||
it('blocks IPv6 loopback ::1', () => { expect(isBlockedAddress('::1')).toBe(true); });
|
||||
it('blocks IPv6 ULA fc00::1', () => { expect(isBlockedAddress('fc00::1')).toBe(true); });
|
||||
it('allows public 8.8.8.8', () => { expect(isBlockedAddress('8.8.8.8')).toBe(false); });
|
||||
it('allows public 1.1.1.1', () => { expect(isBlockedAddress('1.1.1.1')).toBe(false); });
|
||||
});
|
||||
|
||||
describe('fetchUrl', () => {
|
||||
it('rejects non-http schemes', async () => {
|
||||
await expect(fetchUrl('file:///etc/passwd')).rejects.toThrow();
|
||||
|
||||
Reference in New Issue
Block a user