From 758cbe4c43649ceabcff78aee60e6c02ee40789f Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Wed, 7 Jan 2026 11:31:40 +0100 Subject: [PATCH] feat: implement Xtream proxy handler with M3U8 URL rewriting, SSRF protection, and content type handling. --- bin/api/proxy_handler.dart | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/bin/api/proxy_handler.dart b/bin/api/proxy_handler.dart index 295f625..98566ae 100644 --- a/bin/api/proxy_handler.dart +++ b/bin/api/proxy_handler.dart @@ -88,21 +88,31 @@ class ProxyHandler { final targetUrl = Uri.parse(fullUrl); - // SSRF Protection via Domain Allowlist - final playlist = await _getPlaylist(request); - if (playlist == null) { - return Response.forbidden('No active playlist configuration found to validate request'); - } - - // Normalize domains for comparison - final targetHost = targetUrl.host.toLowerCase(); - final allowedHost = Uri.parse(playlist.dns).host.toLowerCase(); + // SSRF Protection - but allow images/static assets from any host + // Xtream providers often use separate CDN servers for picons/images + final isStaticAsset = targetUrl.path.endsWith('.png') || + targetUrl.path.endsWith('.jpg') || + targetUrl.path.endsWith('.jpeg') || + targetUrl.path.endsWith('.gif') || + targetUrl.path.endsWith('.webp') || + targetUrl.path.endsWith('.ico') || + targetUrl.path.contains('/picons/') || + targetUrl.path.contains('/logos/'); - bool isAllowed = targetHost == allowedHost; - - if (!isAllowed) { - print('[Proxy] Blocked SSRF attempt to $targetHost (Allowed: $allowedHost)'); - return Response.forbidden('Access to this domain is forbidden by policy'); + if (!isStaticAsset) { + // For API calls, enforce domain allowlist + final playlist = await _getPlaylist(request); + if (playlist == null) { + return Response.forbidden('No active playlist configuration found to validate request'); + } + + final targetHost = targetUrl.host.toLowerCase(); + final allowedHost = Uri.parse(playlist.dns).host.toLowerCase(); + + if (targetHost != allowedHost) { + print('[Proxy] Blocked SSRF attempt to $targetHost (Allowed: $allowedHost)'); + return Response.forbidden('Access to this domain is forbidden by policy'); + } } final proxyHeaders = {