Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ GEN_SERVER_DIR := src/generated/server
DOCS_API_DIR := docs/api

# Go install settings
GO_PROXY := GOPROXY=direct
GO_PRIVATE := GOPRIVATE=github.com/SebastienMelki
GO_INSTALL := $(GO_PROXY) $(GO_PRIVATE) go install
GO_INSTALL := $(GO_PRIVATE) go install

# Required tool versions
BUF_VERSION := v1.64.0
Expand Down
2 changes: 1 addition & 1 deletion api/[domain]/v1/[rpc].ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default async function handler(request: Request): Promise<Response> {
try {
corsHeaders = getCorsHeaders(request);
} catch {
corsHeaders = { 'Access-Control-Allow-Origin': '*' };
corsHeaders = { 'Access-Control-Allow-Origin': 'https://worldmonitor.app' };
}

// OPTIONS preflight
Expand Down
8 changes: 7 additions & 1 deletion api/download.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,16 @@ export default async function handler(req) {
return Response.redirect(RELEASES_PAGE, 302);
}

// Validate redirect URL is a GitHub download
const downloadUrl = String(asset.browser_download_url || '');
if (!downloadUrl.startsWith('https://github.com/')) {
return Response.redirect(RELEASES_PAGE, 302);
}

return new Response(null, {
status: 302,
headers: {
'Location': asset.browser_download_url,
'Location': downloadUrl,
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=60',
},
});
Expand Down
183 changes: 4 additions & 179 deletions api/rss-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,185 +15,10 @@ async function fetchWithTimeout(url, options, timeoutMs = 15000) {
}
}

// Allowed RSS feed domains for security
const ALLOWED_DOMAINS = [
'feeds.bbci.co.uk',
'www.theguardian.com',
'feeds.npr.org',
'news.google.com',
'www.aljazeera.com',
'rss.cnn.com',
'hnrss.org',
'feeds.arstechnica.com',
'www.theverge.com',
'www.cnbc.com',
'feeds.marketwatch.com',
'www.defenseone.com',
'breakingdefense.com',
'www.bellingcat.com',
'techcrunch.com',
'huggingface.co',
'www.technologyreview.com',
'rss.arxiv.org',
'export.arxiv.org',
'www.federalreserve.gov',
'www.sec.gov',
'www.whitehouse.gov',
'www.state.gov',
'www.defense.gov',
'home.treasury.gov',
'www.justice.gov',
'tools.cdc.gov',
'www.fema.gov',
'www.dhs.gov',
'www.thedrive.com',
'krebsonsecurity.com',
'finance.yahoo.com',
'thediplomat.com',
'venturebeat.com',
'foreignpolicy.com',
'www.ft.com',
'openai.com',
'www.reutersagency.com',
'feeds.reuters.com',
'rsshub.app',
'asia.nikkei.com',
'www.cfr.org',
'www.csis.org',
'www.politico.com',
'www.brookings.edu',
'layoffs.fyi',
'www.defensenews.com',
'www.militarytimes.com',
'taskandpurpose.com',
'news.usni.org',
'www.oryxspioenkop.com',
'www.gov.uk',
'www.foreignaffairs.com',
'www.atlanticcouncil.org',
// Tech variant domains
'www.zdnet.com',
'www.techmeme.com',
'www.darkreading.com',
'www.schneier.com',
'rss.politico.com',
'www.anandtech.com',
'www.tomshardware.com',
'www.semianalysis.com',
'feed.infoq.com',
'thenewstack.io',
'devops.com',
'dev.to',
'lobste.rs',
'changelog.com',
'seekingalpha.com',
'news.crunchbase.com',
'www.saastr.com',
'feeds.feedburner.com',
// Additional tech variant domains
'www.producthunt.com',
'www.axios.com',
'github.blog',
'githubnext.com',
'mshibanami.github.io',
'www.engadget.com',
'news.mit.edu',
'dev.events',
// VC blogs
'www.ycombinator.com',
'a16z.com',
'review.firstround.com',
'www.sequoiacap.com',
'www.nfx.com',
'www.aaronsw.com',
'bothsidesofthetable.com',
'www.lennysnewsletter.com',
'stratechery.com',
// Regional startup news
'www.eu-startups.com',
'tech.eu',
'sifted.eu',
'www.techinasia.com',
'kr-asia.com',
'techcabal.com',
'disrupt-africa.com',
'lavca.org',
'contxto.com',
'inc42.com',
'yourstory.com',
// Funding & VC
'pitchbook.com',
'www.cbinsights.com',
// Accelerators
'www.techstars.com',
// Middle East & Regional News
'english.alarabiya.net',
'www.arabnews.com',
'www.timesofisrael.com',
'www.haaretz.com',
'www.scmp.com',
'kyivindependent.com',
'www.themoscowtimes.com',
'feeds.24.com',
'feeds.capi24.com', // News24 redirect destination
// International News Sources
'www.france24.com',
'www.euronews.com',
'www.lemonde.fr',
'rss.dw.com',
'www.africanews.com',
'www.lasillavacia.com',
'www.channelnewsasia.com',
'www.thehindu.com',
// International Organizations
'news.un.org',
'www.iaea.org',
'www.who.int',
'www.cisa.gov',
'www.crisisgroup.org',
// Think Tanks & Research (Added 2026-01-29)
'rusi.org',
'warontherocks.com',
'www.aei.org',
'responsiblestatecraft.org',
'www.fpri.org',
'jamestown.org',
'www.chathamhouse.org',
'ecfr.eu',
'www.gmfus.org',
'www.wilsoncenter.org',
'www.lowyinstitute.org',
'www.mei.edu',
'www.stimson.org',
'www.cnas.org',
'carnegieendowment.org',
'www.rand.org',
'fas.org',
'www.armscontrol.org',
'www.nti.org',
'thebulletin.org',
'www.iss.europa.eu',
// Economic & Food Security
'www.fao.org',
'worldbank.org',
'www.imf.org',
// Regional locale feeds (tr, pl, ru, th, vi)
'www.hurriyet.com.tr',
'tvn24.pl',
'www.polsatnews.pl',
'www.rp.pl',
'meduza.io',
'novayagazeta.eu',
'www.bangkokpost.com',
'vnexpress.net',
'www.abc.net.au',
// Additional
'news.ycombinator.com',
// Finance variant
'seekingalpha.com',
'www.coindesk.com',
'cointelegraph.com',
];
// Allowed RSS feed domains — single source of truth: data/rss-allowed-domains.json
// KEEP IN SYNC with src-tauri/sidecar/local-api-server.mjs (reads the same JSON at build time)
import allowedDomains from '../data/rss-allowed-domains.json';
const ALLOWED_DOMAINS = allowedDomains;

export default async function handler(req) {
const corsHeaders = getCorsHeaders(req, 'GET, OPTIONS');
Expand Down
2 changes: 1 addition & 1 deletion api/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default async function handler() {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=60',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Origin': 'https://worldmonitor.app',
},
});
} catch {
Expand Down
9 changes: 5 additions & 4 deletions api/youtube/embed.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ function sanitizeVideoId(value) {
const ALLOWED_ORIGINS = [
/^https:\/\/(.*\.)?worldmonitor\.app$/,
/^https:\/\/worldmonitor-[a-z0-9-]+-elie-habib-projects\.vercel\.app$/,
/^https:\/\/worldmonitor-[a-z0-9-]+\.vercel\.app$/,
/^https?:\/\/localhost(:\d+)?$/,
/^https?:\/\/127\.0\.0\.1(:\d+)?$/,
/^tauri:\/\/localhost$/,
Expand Down Expand Up @@ -88,12 +87,12 @@ export default async function handler(request) {
playerVars:{autoplay:${autoplay},mute:${mute},playsinline:1,rel:0,controls:1,modestbranding:1,enablejsapi:1,origin:${JSON.stringify(origin)},widget_referrer:${JSON.stringify(origin)}},
events:{
onReady:function(){
window.parent.postMessage({type:'yt-ready'},'*');
window.parent.postMessage({type:'yt-ready'},${JSON.stringify(origin)});
if(${autoplay}===1){player.playVideo()}
},
onError:function(e){window.parent.postMessage({type:'yt-error',code:e.data},'*')},
onError:function(e){window.parent.postMessage({type:'yt-error',code:e.data},${JSON.stringify(origin)})},
onStateChange:function(e){
window.parent.postMessage({type:'yt-state',state:e.data},'*');
window.parent.postMessage({type:'yt-state',state:e.data},${JSON.stringify(origin)});
if(e.data===1||e.data===3){hideOverlay();started=true}
}
}
Expand All @@ -103,7 +102,9 @@ export default async function handler(request) {
if(player&&player.playVideo){player.playVideo();player.unMute();hideOverlay()}
});
setTimeout(function(){if(!started)overlay.classList.remove('hidden')},3000);
var allowedOrigin=${JSON.stringify(origin)};
window.addEventListener('message',function(e){
if(e.origin!==allowedOrigin)return;
if(!player||!player.getPlayerState)return;
var m=e.data;if(!m||!m.type)return;
switch(m.type){
Expand Down
8 changes: 8 additions & 0 deletions api/youtube/live.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export default async function handler(request) {
});
}

// Validate channel parameter to prevent path traversal
if (!/^@?[A-Za-z0-9_.\-]{1,100}$/.test(channel)) {
return new Response(JSON.stringify({ error: 'Invalid channel parameter' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}

try {
// Try to fetch the channel's live page
const channelHandle = channel.startsWith('@') ? channel : `@${channel}`;
Expand Down
Loading