// Web link previews: reading a page's title and description, and never fetching private addresses. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const { parsePreview, fetchPreview, allowed } = require('../electron/linkpreview.cjs'); const page = (head) => `${head}

hi

`; const reply = (body, { status = 200, type = 'text/html; charset=utf-8', headers = {} } = {}) => ({ ok: status >= 200 && status < 300, status, headers: new Headers({ 'content-type': type, ...headers }), body: new Response(body).body }); const PUBLIC = async () => [{ address: '93.184.216.34' }]; test('parsePreview prefers Open Graph, falls back to and the description meta', () => { assert.deepEqual( parsePreview(page('<title>Plain')), { title: 'The & Title', description: 'OG "desc"', site: 'Site' } ); assert.deepEqual(parsePreview(page('\n Just a\n title ')), { title: 'Just a title', description: 'D', site: '' }); assert.deepEqual(parsePreview(page("")), { title: 'single', description: 'bare', site: '' }); assert.deepEqual(parsePreview('no markup at all'), { title: '', description: '', site: '' }); }); test('parsePreview decodes numeric entities and caps lengths', () => { assert.equal(parsePreview(page('café 😀 &unknown;')).title, 'café 😀 &unknown;'); const long = parsePreview(page(``)).description; assert.equal(long.length, 400); assert.ok(long.endsWith('…')); }); test('only public http(s) addresses are allowed', async () => { const ok = async (u) => !!(await allowed(u, PUBLIC)); assert.equal(await ok('https://example.com/a?b=1'), true); for (const u of ['file:///etc/passwd', 'ftp://example.com', 'javascript:alert(1)', 'http://localhost:3000', 'http://app.localhost', 'http://127.0.0.1', 'http://10.1.2.3', 'http://192.168.0.1', 'http://172.20.0.1', 'http://169.254.169.254/latest', 'http://100.64.0.1', 'http://[::1]/', 'http://[fd00::1]/', 'http://[::ffff:127.0.0.1]/', 'http://[::ffff:192.168.1.5]/', 'http://[::127.0.0.1]/', 'http://[::ffff:10.0.0.1]/', 'http://2130706433/', 'http://0x7f.1/', 'http://user:pw@example.com', 'not a url']) { assert.equal(await ok(u), false, u); } // a name that resolves to a private address assert.equal(!!(await allowed('https://evil.example', async () => [{ address: '10.0.0.5' }])), false); assert.equal(!!(await allowed('https://evil.example', async () => [{ address: '93.184.216.34' }, { address: '127.0.0.1' }])), false); assert.equal(!!(await allowed('https://nx.example', async () => { throw new Error('ENOTFOUND'); })), false); }); test('fetchPreview reads the page, follows redirects, and checks every hop', async () => { const seen = []; const fetch = async (url) => { seen.push(url); if (url === 'https://a.example/') return reply('', { status: 301, headers: { location: '/next' } }); if (url === 'https://a.example/next') return reply(page('Landed')); return reply('', { status: 404 }); }; assert.deepEqual(await fetchPreview('https://a.example/', { fetch, lookup: PUBLIC }), { title: 'Landed', description: 'here', site: '' }); assert.deepEqual(seen, ['https://a.example/', 'https://a.example/next']); // a public page that bounces to an address on the local network is not followed const bounce = async (url) => (url === 'https://b.example/' ? reply('', { status: 302, headers: { location: 'http://192.168.1.1/admin' } }) : reply(page('Router'))); const calls = []; assert.equal(await fetchPreview('https://b.example/', { fetch: async (u) => { calls.push(u); return bounce(u); }, lookup: PUBLIC }), null); assert.deepEqual(calls, ['https://b.example/']); }); test('fetchPreview gives null for non-HTML, errors, empty pages and loops', async () => { const lookup = PUBLIC; assert.equal(await fetchPreview('https://c.example/', { fetch: async () => reply('{"a":1}', { type: 'application/json' }), lookup }), null); assert.equal(await fetchPreview('https://c.example/', { fetch: async () => { throw new Error('offline'); }, lookup }), null); assert.equal(await fetchPreview('https://c.example/', { fetch: async () => reply(page('')), lookup }), null); assert.equal(await fetchPreview('https://c.example/', { fetch: async () => reply('', { status: 302, headers: { location: '/' } }), lookup }), null); assert.equal(await fetchPreview('http://127.0.0.1/', { fetch: async () => { throw new Error('must not be called'); }, lookup }), null); }); test('fetchPreview honours the page encoding', async () => { const latin = Buffer.from(page('Café'), 'latin1'); const r = await fetchPreview('https://d.example/', { fetch: async () => reply(latin, { type: 'text/html' }), lookup: PUBLIC }); assert.equal(r.title, 'Café'); });