test: unit and end-to-end suites
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
27fafa0bcd
commit
9b00e21c44
49 files changed
+6694
-1
No files matched your search
@@ -0,0 +1,79 @@
|
||||
// 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) => `<!doctype html><html><head>${head}</head><body><p>hi</p></body></html>`;
|
||||
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 <title> and the description meta', () => {
|
||||
assert.deepEqual(
|
||||
parsePreview(page('<title>Plain</title><meta property="og:title" content="The & Title"><meta name="description" content="Desc"><meta property="og:description" content="OG "desc""><meta property="og:site_name" content="Site">')),
|
||||
{ title: 'The & Title', description: 'OG "desc"', site: 'Site' }
|
||||
);
|
||||
assert.deepEqual(parsePreview(page('<title>\n Just a\n title </title><meta name="description" content="D">')), { title: 'Just a title', description: 'D', site: '' });
|
||||
assert.deepEqual(parsePreview(page("<meta content='single' name='twitter:title'><meta content=bare name=description>")), { 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('<title>café 😀 &unknown;</title>')).title, 'café 😀 &unknown;');
|
||||
const long = parsePreview(page(`<meta name="description" content="${'x'.repeat(1000)}">`)).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('<title>Landed</title><meta name="description" content="here">'));
|
||||
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('<title>Router</title>')));
|
||||
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('<meta charset="iso-8859-1"><title>Café</title>'), 'latin1');
|
||||
const r = await fetchPreview('https://d.example/', { fetch: async () => reply(latin, { type: 'text/html' }), lookup: PUBLIC });
|
||||
assert.equal(r.title, 'Café');
|
||||
});
|
||||
Reference in new issue
Block a user