support.lutron.com returned "Protected by imperva bot detection and could not
be bypassed" through the normal get() path, with a browser solver attached and
every log line reporting success:
Imperva embedder solve (20 cookies, solved=True, passthrough=False)
Browser solved challenge_type=imperva (cookie_count=15)
Challenge detected (body): imperva interstitial <- the replay, still blocked
The solve was earning cookies for a different WAF tenant. Imperva sites are
provisioned per host: support.lutron.com is site 2477703 (visid_incap_2477703)
while www.lutron.com is site 1555970 (nlbi_1555970) and never challenges at
all. imperva_embedder()'s "non-www subdomain must be an API host" heuristic
sent the browser to that sibling, where cookies appear on any page load whether
or not anything was challenged, so solve_imperva_embedder() reported success
having proven nothing. The reese84 token for 2477703 was never minted.
That heuristic exists for api2.realtor.ca, where it is correct: a top-level
navigation to an API host earns Imperva's interactive edet=15 block, so there
is nothing to solve at the URL and a sibling origin is the only way in. The
mistake was assuming every non-www subdomain is such a host. Normal content
hosts -- support., help., docs., shop. -- are the common case.
The challenge body already distinguishes them, and the request loop already
has it. Measured on both live sites:
support.lutron.com api2.realtor.ca
status 200 403
body ~6.2KB interstitial ~960B edet=15 block
reese sensor hooks present absent
solvable by navigating the URL yes no
Imperva serving the sensor interstitial is the invitation to run it on that
host, so imperva_embedder() now takes the body and returns None -- solve in
place -- when the hooks are present, keeping the sibling guess for the hookless
block it was built for. The marker set moved to is_imperva_interstitial() in
_challenge.py so detection and this decision cannot drift apart.
The predicate is deliberately narrower than detection, which also treats any
tiny (<5KB) hookless body as IMPERVA. Detection asks "is this a challenge",
where a false positive costs one solve attempt; the predicate asks "will
navigating this URL produce a sensor I can run", where a false positive aims
the solve at a page with nothing on it.
Live: support.lutron.com returns 200 and the real 110,875-byte article via
session.get(), sync and async, solved in ~3s on the challenged URL itself.
api2.realtor.ca is unaffected, which is the regression that mattered. Its body
is hookless cold and stays hookless under escalation (8 rapid requests, edet=15
every time), so the predicate is False and the derivation is byte-for-byte what
it was; both real bodies are pinned in tests. www.realtor.ca 200/283KB,
amadeus, hkbea and anz.com.au all still pass on TLS alone. An explicit
solve_origin= still overrides the derivation, as before.