mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 04:26:16 -08:00
fix(playlist): DOM-safety caps far beyond real handouts, port-aware portal ambiguity
- The MAC and candidate caps are raised to 64/96: they exist to keep a pathological paste (a log full of MAC-shaped hex) from rendering thousands of cards, not to bound real handouts — scanner dumps top out around a couple dozen accounts, so no real multi-account message is truncated. - The multi-MAC portal ambiguity guard compares URL origins instead of hostnames, so two panels on one DNS name but different ports no longer cross-pair; URL normalization drops default ports, keeping the Real/Panel ':80'-vs-bare pairs of scanner dumps unambiguous. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
This commit is contained in:
3 files changed
+36
-11
No files matched your search
@@ -699,6 +699,23 @@ describe('detectProviderImportCandidates', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('treats same-host portals on different ports as different panels', () => {
|
||||
const candidates = detectProviderImportCandidates(
|
||||
[
|
||||
'http://panel.example.com:8080/c/',
|
||||
'MAC: 00:1A:79:AA:AA:AA',
|
||||
'http://panel.example.com:9090/c/',
|
||||
'MAC: 00:1A:79:BB:BB:BB',
|
||||
].join('\n')
|
||||
);
|
||||
|
||||
const stalker = only(candidates, 'stalker');
|
||||
expect(stalker).toHaveLength(2);
|
||||
for (const candidate of stalker) {
|
||||
expect(candidate.portalUrl).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('treats several MACs with generic URLs on different hosts as ambiguous too', () => {
|
||||
// Without portal-shaped URLs the picker falls back to a generic
|
||||
// URL — the ambiguity guard must cover that pool as well, or two
|
||||
@@ -721,14 +738,15 @@ describe('detectProviderImportCandidates', () => {
|
||||
|
||||
it('surfaces every MAC of a long multi-account list', () => {
|
||||
const macs = Array.from(
|
||||
{ length: 6 },
|
||||
(_, index) => `00:1A:79:0${index}:0${index}:0${index}`
|
||||
{ length: 20 },
|
||||
(_, index) =>
|
||||
`00:1A:79:${String(index).padStart(2, '0')}:11:22`
|
||||
);
|
||||
const candidates = detectProviderImportCandidates(
|
||||
['http://long.example.com/c/', ...macs].join('\n')
|
||||
);
|
||||
|
||||
expect(only(candidates, 'stalker')).toHaveLength(6);
|
||||
expect(only(candidates, 'stalker')).toHaveLength(20);
|
||||
});
|
||||
|
||||
it('gives labeled credentials to the stalker candidate, not a competing xtream guess', () => {
|
||||
|
||||
@@ -69,9 +69,10 @@ export interface ProviderImportCandidate {
|
||||
|
||||
const GET_PHP_PATH_PATTERN = /\/get\.php$/i;
|
||||
const EXTM3U_HEADER_PATTERN = /^\s*#EXTM3U/i;
|
||||
// Guard against pathological pastes, not against real handouts: multi-account
|
||||
// MAC lists with a dozen entries are legitimate and must all surface.
|
||||
const MAX_CANDIDATES = 16;
|
||||
// DOM-safety guard against pathological pastes, not a business limit: sized
|
||||
// above the scanners' combined caps (64 MACs + 16 URLs), so every candidate
|
||||
// a real multi-account handout produces survives the final slice.
|
||||
const MAX_CANDIDATES = 96;
|
||||
|
||||
/**
|
||||
* Scans a pasted provider message and returns ranked import candidates.
|
||||
@@ -132,9 +133,13 @@ export function detectProviderImportCandidates(
|
||||
: labeledHostUrl(labeled) !== undefined
|
||||
? []
|
||||
: urls.filter((url) => url.role === 'generic');
|
||||
// Compared by ORIGIN, not hostname: two panels on one DNS name but
|
||||
// different ports are different panels, while URL normalization drops a
|
||||
// default port, so the Real/Panel `:80`-vs-bare pairs of scanner dumps
|
||||
// still compare equal.
|
||||
const portalAmbiguous =
|
||||
macs.length > 1 &&
|
||||
new Set(portalPool.map((url) => url.parsed.hostname)).size > 1;
|
||||
new Set(portalPool.map((url) => url.parsed.origin)).size > 1;
|
||||
const portal = portalAmbiguous
|
||||
? null
|
||||
: pickStalkerPortalUrl(urls, labeled);
|
||||
|
||||
@@ -52,11 +52,13 @@ const HOSTISH_PATTERN = /^[a-z0-9][a-z0-9.-]*(?::\d{2,5})?$/i;
|
||||
|
||||
const WRAPPING_CHARS = /^["'`«<([]+|["'`»>)\]]+$/g;
|
||||
|
||||
// Both caps guard against pathological pastes only, and stay aligned with
|
||||
// the assembler's candidate cap so a real multi-link or multi-account
|
||||
// handout is never truncated by the scanner before assembly even sees it.
|
||||
// Both caps are DOM-safety guards against pathological pastes (a log file
|
||||
// full of MAC-shaped hex would otherwise render thousands of cards), sized
|
||||
// far beyond any real handout — reseller and scanner dumps top out around a
|
||||
// couple dozen accounts. The assembler's candidate cap leaves headroom for
|
||||
// both scanners together, so neither truncates real input before assembly.
|
||||
const MAX_URLS = 16;
|
||||
const MAX_MACS = 12;
|
||||
const MAX_MACS = 64;
|
||||
|
||||
interface FieldMatcher {
|
||||
field: keyof LabeledFields;
|
||||
|
||||
Reference in new issue
Block a user