mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
fix(stalker): drop the unanchored auth-failure sweep in the session service
Anchoring the body detector closed one door and left another open. The session service still stringified the whole response and matched an UNANCHORED `authorization failed`, so a short page from something in FRONT of the portal retired the token, retried, and threw `Authorization failed after retry` — whose own message then matched the repair trigger's wide phrase set and re-probed a portal that had refused nothing. The shared detector already covers every real shape, including the `js.error`/`js.msg` envelopes the sweep was also catching, so removing it costs no coverage. Regression goes through `makeAuthenticatedRequest` rather than the primitive, since that is where the chain actually ran. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6cb195197d
commit
59d4060afb
2 files changed
+33
-24
No files matched your search
@@ -259,6 +259,28 @@ describe('StalkerSessionService identity-tagged token cache', () => {
|
||||
}
|
||||
);
|
||||
|
||||
it('does not treat a proxy page mentioning the phrase as an auth failure', async () => {
|
||||
// End to end through makeAuthenticatedRequest, not just the
|
||||
// primitive: this is the path that used to stringify the whole
|
||||
// response and match an unanchored `authorization failed`. A short
|
||||
// page from something in FRONT of the portal would retire the token,
|
||||
// retry, and throw a message that itself matches the repair trigger —
|
||||
// re-probing a portal that never refused anything.
|
||||
service.setCachedToken('portal-1', 'LIVE', playlistA);
|
||||
const body = 'The request Authorization failed. Try again later.';
|
||||
sendIpcEvent.mockResolvedValue(body);
|
||||
|
||||
await expect(
|
||||
service.makeAuthenticatedRequest(playlistA, {
|
||||
action: 'get_genres',
|
||||
})
|
||||
).resolves.toBe(body);
|
||||
|
||||
// No retry, no retirement, nothing for the repair layer to act on.
|
||||
expect(sendIpcEvent).toHaveBeenCalledTimes(1);
|
||||
expect(service.getCachedToken('portal-1')).toBe('LIVE');
|
||||
});
|
||||
|
||||
it('retires a failed token even on the no-retry path (watchdog pings)', async () => {
|
||||
service.setCachedToken('portal-1', 'DEAD', playlistA);
|
||||
sendIpcEvent.mockResolvedValue('Authorization failed.');
|
||||
|
||||
@@ -801,30 +801,17 @@ export class StalkerSessionService {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert response to string for pattern matching
|
||||
const responseStr = JSON.stringify(responseOrError).toLowerCase();
|
||||
|
||||
// Check for "Authorization failed. XX" pattern (like "Authorization failed. 75")
|
||||
if (/authorization\s*failed\.?\s*\d*/i.test(responseStr)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for common auth failure indicators
|
||||
const jsData = response?.['js'] as Record<string, unknown>;
|
||||
const errorMessage =
|
||||
(response?.['message'] as string)?.toLowerCase?.() ||
|
||||
jsData?.['error']?.toString?.().toLowerCase?.() ||
|
||||
jsData?.['msg']?.toString?.().toLowerCase?.() ||
|
||||
'';
|
||||
|
||||
return (
|
||||
errorMessage.includes('authorization') ||
|
||||
errorMessage.includes('unauthorized') ||
|
||||
errorMessage.includes('auth failed') ||
|
||||
errorMessage.includes('invalid token') ||
|
||||
response?.['status'] === 401 ||
|
||||
jsData?.['error'] === 'Authorization failed'
|
||||
);
|
||||
// Everything above is structural. What used to follow was a
|
||||
// `JSON.stringify(responseOrError)` sweep with an UNANCHORED
|
||||
// `authorization failed` pattern — the same false positive the body
|
||||
// detector was just anchored against, reachable through a different
|
||||
// door: a short proxy/WAF page containing the phrase retired the
|
||||
// token, retried, and threw `Authorization failed after retry`,
|
||||
// whose own message then matched the repair trigger and re-probed a
|
||||
// portal that never refused anything. The shared detector already
|
||||
// covers every real shape, including the `js.error`/`js.msg`
|
||||
// envelopes, so the sweep only added the false positive.
|
||||
return response?.['status'] === 401 || response?.['status'] === 403;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user