mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(xtream): send player-style User-Agent to avoid WAF challenge blocking connections (#1170)
Some Xtream panels sit behind Cloudflare/WAF rules that challenge generic browser-looking User-Agents while allowlisting known IPTV player clients. The previous hardcoded, truncated browser-style User-Agent in xtream.events.ts (XTREAM_REQUEST and XTREAM_PROBE_URL) was being served a Cloudflare challenge page (HTTP 403 HTML) instead of the real API response, so "Test Connection" always failed with "Could not connect to the portal" even though the same portal worked fine via curl (with a player-style UA) and Safari. Switching to a shared VLC-style User-Agent constant across all three request sites resolves it, verified against a live panel. Co-authored-by: Sergio Herencias Redondo <sherencr@MacBook-Pro-de-diverzy.local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
3 files changed
+63
-6
No files matched your search
@@ -86,6 +86,45 @@ describe('XtreamEvents session cancellation', () => {
|
||||
expect(requestedUrl.searchParams.get('username')).toBe('user');
|
||||
});
|
||||
|
||||
it('sends a player-style User-Agent instead of a truncated browser string', async () => {
|
||||
// Regression: some Xtream panels sit behind a WAF (e.g. Cloudflare)
|
||||
// that challenges generic/incomplete browser User-Agents but
|
||||
// allowlists known IPTV player clients. The previous hardcoded
|
||||
// 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
// string was rejected by such panels even though curl/Safari worked.
|
||||
const requestHandler = registeredHandlers.get('XTREAM_REQUEST');
|
||||
expect(requestHandler).toBeDefined();
|
||||
|
||||
axiosMock.mockResolvedValue({
|
||||
status: 200,
|
||||
data: { user_info: { auth: 1, status: 'Active' } },
|
||||
headers: {},
|
||||
});
|
||||
|
||||
await requestHandler?.(
|
||||
{},
|
||||
{
|
||||
url: 'https://example.com',
|
||||
params: {
|
||||
action: 'get_account_info',
|
||||
password: 'pass',
|
||||
username: 'user',
|
||||
},
|
||||
suppressErrorLog: true,
|
||||
}
|
||||
);
|
||||
|
||||
const requestConfig = axiosMock.mock.calls[0][0] as {
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
const userAgent = requestConfig.headers?.['User-Agent'];
|
||||
expect(userAgent).toBeDefined();
|
||||
expect(userAgent).not.toBe(
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
);
|
||||
expect(userAgent).not.toMatch(/^Mozilla\/5\.0 /);
|
||||
});
|
||||
|
||||
it('follows validated redirects for range GET media probes', async () => {
|
||||
const probeHandler = registeredHandlers.get('XTREAM_PROBE_URL');
|
||||
const destroyProbeBody = jest.fn();
|
||||
@@ -129,6 +168,9 @@ describe('XtreamEvents session cancellation', () => {
|
||||
expect(firstRequest.headers).toEqual(
|
||||
expect.objectContaining({ Range: 'bytes=0-4095' })
|
||||
);
|
||||
expect(firstRequest.headers?.['User-Agent']).not.toBe(
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
);
|
||||
expect(firstRequest.maxRedirects).toBe(0);
|
||||
expect(firstRequest.responseType).toBe('stream');
|
||||
expect(destroyProbeBody).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -14,6 +14,11 @@ import { emitPortalDebugEvent } from './portal-debug.events';
|
||||
import { UnsafeUrlError } from './url-safety';
|
||||
import { requestWithValidatedRedirects } from '../util/validated-axios';
|
||||
|
||||
// Some Xtream panels sit behind Cloudflare (or similar WAFs) configured to
|
||||
// challenge generic browser-looking User-Agents while allowlisting known
|
||||
// IPTV player clients. A VLC-style User-Agent reliably passes those checks.
|
||||
const XTREAM_CLIENT_USER_AGENT = 'VLC/3.0.18 LibVLC/3.0.18';
|
||||
|
||||
export default class XtreamEvents {
|
||||
static bootstrapXtreamEvents(): Electron.IpcMain {
|
||||
return ipcMain;
|
||||
@@ -119,8 +124,7 @@ ipcMain.handle(
|
||||
method: 'GET',
|
||||
url: apiUrl.toString(),
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
'User-Agent': XTREAM_CLIENT_USER_AGENT,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 30000, // 30 seconds timeout for Xtream API
|
||||
@@ -194,8 +198,7 @@ ipcMain.handle(
|
||||
method: 'GET',
|
||||
url: apiUrl,
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
'User-Agent': XTREAM_CLIENT_USER_AGENT,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 30000,
|
||||
@@ -304,8 +307,7 @@ ipcMain.handle(
|
||||
method,
|
||||
url: payload.url,
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
'User-Agent': XTREAM_CLIENT_USER_AGENT,
|
||||
...(method === 'GET' ? { Range: 'bytes=0-4095' } : {}),
|
||||
},
|
||||
timeout: 10000,
|
||||
|
||||
@@ -62,6 +62,19 @@ targets. Those targets are validated when registered and revalidated before the
|
||||
`/xtream` proxy request, including protocol, URL credentials, DNS resolution,
|
||||
and private-network checks.
|
||||
|
||||
## User-Agent
|
||||
|
||||
Electron's `XTREAM_REQUEST` and `XTREAM_PROBE_URL` handlers
|
||||
(`apps/electron-backend/src/app/events/xtream.events.ts`) send a shared
|
||||
`XTREAM_CLIENT_USER_AGENT` constant on every outgoing request. Some Xtream
|
||||
panels sit behind a WAF (e.g. Cloudflare) configured to challenge
|
||||
generic/incomplete browser-looking User-Agents while allowlisting known IPTV
|
||||
player clients; a player-style User-Agent (currently a VLC signature) avoids
|
||||
that challenge page, whereas a browser-looking but non-browser TLS/HTTP client
|
||||
(axios/curl with a Chrome or empty User-Agent) can be blocked even though a
|
||||
real browser or a VLC-style client passes. Keep all three request sites using
|
||||
the shared constant instead of inlining the string again.
|
||||
|
||||
## Playback URL Formats
|
||||
|
||||
When account info includes `user_info.allowed_output_formats`, the current
|
||||
|
||||
Reference in new issue
Block a user