From ee552dddef37de4ece673408e1dae8544550e09b Mon Sep 17 00:00:00 2001 From: calesthio Date: Sun, 15 Mar 2026 17:27:07 -0700 Subject: [PATCH] fix: restrict ticker article links to http(s) --- dashboard/inject.mjs | 14 ++++++++++++-- dashboard/public/jarvis.html | 6 +++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/dashboard/inject.mjs b/dashboard/inject.mjs index 331171c..3f7bd4f 100644 --- a/dashboard/inject.mjs +++ b/dashboard/inject.mjs @@ -58,6 +58,16 @@ function geoTagText(text) { return null; } +function sanitizeExternalUrl(raw) { + if (!raw) return undefined; + try { + const url = new URL(raw); + return url.protocol === 'http:' || url.protocol === 'https:' ? url.toString() : undefined; + } catch { + return undefined; + } +} + // === RSS Fetching === async function fetchRSS(url, source) { try { @@ -69,7 +79,7 @@ async function fetchRSS(url, source) { while ((match = itemRegex.exec(xml)) !== null) { const block = match[1]; const title = (block.match(/(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/title>/)?.[1] || '').trim(); - const link = (block.match(/<link>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/link>/)?.[1] || '').trim(); + const link = sanitizeExternalUrl((block.match(/<link>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/link>/)?.[1] || '').trim()); const pubDate = block.match(/<pubDate>(.*?)<\/pubDate>/)?.[1] || ''; if (title && title !== source) items.push({ title, date: pubDate, source, url: link || undefined }); } @@ -421,7 +431,7 @@ function buildNewsFeed(rssNews, gdeltData, tgUrgent, tgTop) { const geo = geoTagText(a.title); feed.push({ headline: a.title.substring(0, 100), source: 'GDELT', type: 'gdelt', - timestamp: new Date().toISOString(), region: geo?.region || 'Global', urgent: false, url: a.url + timestamp: new Date().toISOString(), region: geo?.region || 'Global', urgent: false, url: sanitizeExternalUrl(a.url) }); } } diff --git a/dashboard/public/jarvis.html b/dashboard/public/jarvis.html index ff2bf47..6af58f0 100644 --- a/dashboard/public/jarvis.html +++ b/dashboard/public/jarvis.html @@ -958,6 +958,7 @@ function renderRight(){ // === HELPERS === function getAge(d){const ms=Date.now()-new Date(d).getTime();const h=Math.floor(ms/3600000);if(h<1)return 'just now';if(h<24)return h+'h ago';return Math.floor(h/24)+'d ago'} function cleanText(t){return t.replace(/'/g,"'").replace(/!/g,"!").replace(/&/g,"&").replace(/<[^>]+>/g,'')} +function safeExternalUrl(raw){try{const u=new URL(raw,location.href);return u.protocol==='http:'||u.protocol==='https:'?u.toString():null}catch{return null}} // === BOOT SEQUENCE === function runBoot(){ @@ -1055,7 +1056,10 @@ function init(){ // Open article links from ticker cards document.addEventListener('click',e=>{ const card=e.target.closest('.tk-card[data-url]'); - if(card) window.open(card.dataset.url,'_blank','noopener'); + if(card){ + const url=safeExternalUrl(card.dataset.url); + if(url) window.open(url,'_blank','noopener'); + } }); }