import test from 'node:test'; import assert from 'node:assert/strict'; import {createCompanyNewsReader,parseCompanyNews,publicNewsAddress} from '../apps/server/company-news.mjs'; const policy={version:1,startPage:{newsEnabled:true,feedUrl:'https://news.example/feed'}}; const rss=Buffer.from('<![CDATA[<b>Company news</b> & update]]>https://intranet.example/article2026-10-13Bad URLjavascript:alert(1)'); test('RSS and Atom produce bounded plain text and safe links',()=>{ assert.deepEqual(parseCompanyNews(rss),[{title:'Company news & update',url:'https://intranet.example/article',publishedAt:'2026-10-13T00:00:00.000Z'}]); assert.equal(parseCompanyNews(Buffer.from('Announcement'))[0].title,'Announcement'); assert.throws(()=>parseCompanyNews(Buffer.from(']>'))); assert.throws(()=>parseCompanyNews(Buffer.from(''))); assert.throws(()=>parseCompanyNews(Buffer.alloc(524289))); }); test('news disabled does not resolve or request anything',async()=>{ const read=createCompanyNewsReader({lookup:()=>{throw Error('Must not resolve');}}); assert.deepEqual(await read({version:1,startPage:{newsEnabled:false}}),[]); }); test('public DNS is pinned and feed is cached without article requests',async()=>{ let calls=0;const read=createCompanyNewsReader({lookup:async()=>[{address:'8.8.8.8',family:4}],request:async(url,options)=>{calls++;assert.equal(options.address,'8.8.8.8');assert.equal(url.href,policy.startPage.feedUrl);return rss;}}); assert.equal((await read(policy)).length,1);await read(policy);assert.equal(calls,1); }); test('SSRF rejects private, loopback, mapped IPv6, mixed DNS and numeric URLs',async()=>{ for(const address of ['10.0.0.8','127.0.0.1','::1','::ffff:127.0.0.1','169.254.169.254']){ assert.equal(publicNewsAddress(address),false); const read=createCompanyNewsReader({lookup:async()=>[{address,family:address.includes(':')?6:4}],request:()=>{throw Error('Must not fetch');}});await assert.rejects(read(policy)); } const mixed=createCompanyNewsReader({lookup:async()=>[{address:'8.8.8.8',family:4},{address:'10.0.0.8',family:4}]});await assert.rejects(mixed(policy)); const read=createCompanyNewsReader();for(const feedUrl of ['http://news.example/feed','https://user:pass@news.example/feed','https://127.0.0.1/feed'])await assert.rejects(read({...policy,startPage:{...policy.startPage,feedUrl}})); }); test('exact corporate allowlist permits private DNS but never loopback',async()=>{ const read=createCompanyNewsReader({allowedHosts:'news.example',lookup:async()=>[{address:'10.0.0.8',family:4}],request:async()=>rss});assert.equal((await read(policy)).length,1); const loop=createCompanyNewsReader({allowedHosts:'news.example',lookup:async()=>[{address:'127.0.0.1',family:4}]});await assert.rejects(loop(policy)); const suffix=createCompanyNewsReader({allowedHosts:'example',lookup:async()=>[{address:'10.0.0.8',family:4}]});await assert.rejects(suffix(policy)); }); test('failed feeds are not cached',async()=>{ let calls=0;const read=createCompanyNewsReader({lookup:async()=>[{address:'8.8.8.8',family:4}],request:async()=>{calls++;throw Error('Offline');}});await assert.rejects(read(policy));await assert.rejects(read(policy));assert.equal(calls,2); });