Files
maigret/tests/test_error_detection.py
SoxojandGitHub 4b54004eae Fix six false positives reported for username soxoj (#3032)
Every one of them is a site that answers the same way for any username, so the check said "claimed" for everyone.

Two of them are proof-of-work interstitials that come back with a 2xx, and those are worth a global marker rather than a per-site patch: joyreactor.cc now runs Anubis (`/.within.website/x/`, HTTP 200 on every path) and fixya.com serves an HTTP 202 with `window.POW_CHALLENGE_DATA`. Both are vendor products used far beyond these two sites, so they go into COMMON_ERRORS and the sites report bot protection instead of a hit.

The rest are per-site rot. championat.com answers every `/user/*` with the same 686-byte SberID auth stub, profiles are behind login now, disabled. forum.heroesworld.ru redirects to the forum index, which never contains the vBulletin absence marker, the forum has moved to `heroesworld.ru/user/{username}/` where a missing user is a clean 404. Codédex was a status_code check and flapped to 200 on a missing profile once in about fifteen requests, so it now matches the og:title of a real profile.

OP.GG search falls back to other regions when the requested one has no hit: `?q=soxoj&region=ru` returns an Oceania summoner, and the shared engine marker `href="/lol/summoners/` matched it, so all seventeen region entries claimed the account. The marker moves from the engine to each site with its region baked in. Name matching is already exact on op.gg's side, `q=blaz` returns only summoners named exactly Blaz, so the region was the whole leak.
2026-08-28 15:53:24 +02:00

62 lines
1.6 KiB
Python

"""
Unit tests for error page detection helpers.
"""
from maigret.error_detection import detect_error_page
from maigret.errors import CheckError
def test_site_specific_error():
err = detect_error_page(
"this page is blocked",
200,
{"blocked": "Blocked by site"},
ignore_403=False,
)
assert isinstance(err, CheckError)
assert err.type == "Site-specific"
def test_http_403():
err = detect_error_page("x", 403, {}, ignore_403=False)
assert err.type == "Access denied"
def test_http_500():
err = detect_error_page("x", 500, {}, ignore_403=False)
assert err.type == "Server"
def test_no_error():
assert detect_error_page("ok", 200, {}, ignore_403=False) is None
def test_http_429_is_rate_limit():
err = detect_error_page("x", 429, {}, ignore_403=False)
assert err.type == "Rate limited"
def test_ignore_linkedin_999_status():
# 999 stays a pass-through on purpose, see detect_error_page.
assert detect_error_page("", 999, {}, ignore_403=False) is None
def test_pow_challenge_pages_are_bot_protection():
# Both serve a 2xx on every path, so without a marker every username
# would read as claimed.
anubis = detect_error_page(
'<link rel="stylesheet" href="/.within.website/x/xess/xess.min.css">',
200,
{},
ignore_403=False,
)
pow_js = detect_error_page(
"window.POW_CHALLENGE_DATA={challenge_nonce:'2bafb0f5'};", 202, {}, ignore_403=False
)
assert anubis.type == "Bot protection"
assert pow_js.type == "Bot protection"