test: add real bot detection assertions to stealth tests

Replace hollow page-load checks with actual verdict parsing for 6
detection sites: sannysoft, incolumitas, browserscan, deviceandbrowserinfo,
fingerprintjs, and recaptcha v3. Add proxy support via CLOAKBROWSER_TEST_PROXY
env var. All 12 stealth tests pass.
This commit is contained in:
CloakHQ
2026-02-27 09:20:56 +01:00
parent 8ffaf86abe
commit f480958ba7
2 changed files with 130 additions and 18 deletions
+128 -18
View File
@@ -4,14 +4,18 @@ These tests verify that the stealth Chromium binary passes common
bot detection checks. They require network access.
"""
import os
import pytest
from cloakbrowser import launch
PROXY = os.environ.get("CLOAKBROWSER_TEST_PROXY")
@pytest.fixture(scope="module")
def browser():
"""Shared browser instance for stealth tests."""
b = launch(headless=True)
b = launch(headless=True, proxy=PROXY)
yield b
b.close()
@@ -48,7 +52,7 @@ class TestWebDriverDetection:
"""Must have browser plugins (real Chrome has 5)."""
page.goto("https://example.com")
count = page.evaluate("navigator.plugins.length")
assert count >= 1, f"Expected plugins, got {count}"
assert count >= 5, f"Expected 5+ plugins (real Chrome), got {count}"
def test_languages_present(self, page):
"""navigator.languages must be populated."""
@@ -81,28 +85,134 @@ class TestBotDetectionSites:
Mark with pytest -m slow to skip in CI.
"""
@pytest.mark.slow
def test_bot_sannysoft(self, page):
"""bot.sannysoft.com — all checks should pass (0 failures)."""
page.goto("https://bot.sannysoft.com", wait_until="networkidle", timeout=30000)
page.wait_for_timeout(3000)
results = page.evaluate("""() => {
const rows = document.querySelectorAll('table tr');
const failed = [];
let total = 0;
rows.forEach(r => {
const cells = r.querySelectorAll('td');
if (cells.length >= 2) {
total++;
const cls = cells[1].className || '';
if (cls.includes('failed')) {
failed.push(cells[0].innerText.trim());
}
}
});
return {total, failed};
}""")
failed = results["failed"]
assert len(failed) == 0, f"Sannysoft failures: {', '.join(failed)}"
@pytest.mark.slow
def test_bot_incolumitas(self, page):
"""bot.incolumitas.com should detect minimal flags."""
page.goto("https://bot.incolumitas.com", timeout=30000)
page.wait_for_timeout(5000)
# Check that we're not immediately flagged
title = page.title()
assert title # Page loaded successfully
"""bot.incolumitas.com — max 1 failure (WEBDRIVER false positive expected)."""
page.goto("https://bot.incolumitas.com", wait_until="networkidle", timeout=30000)
page.wait_for_timeout(12000)
# Known acceptable failures (not browser fingerprint issues):
# - WEBDRIVER: spec-level false positive across all builds
# - connectionRTT: detects datacenter/proxy network latency, not browser
KNOWN_ACCEPTABLE = {"WEBDRIVER", "connectionRTT"}
results = page.evaluate("""() => {
const text = document.body.innerText;
const okMatches = text.match(/"\\w+":\\s*"OK"/g) || [];
const failMatches = text.match(/"\\w+":\\s*"FAIL"/g) || [];
const failedTests = failMatches.map(m => m.match(/"(\\w+)"/)[1]);
return {passed: okMatches.length, failed: failMatches.length, failedTests};
}""")
failed_names = results["failedTests"]
real_failures = [f for f in failed_names if f not in KNOWN_ACCEPTABLE]
assert len(real_failures) == 0, f"Incolumitas unexpected failures: {', '.join(real_failures)}"
@pytest.mark.slow
def test_browserscan(self, page):
"""BrowserScan bot detection should show NORMAL."""
page.goto("https://www.browserscan.net/bot-detection", timeout=30000)
"""BrowserScan bot detection — 0 abnormal checks."""
page.goto("https://www.browserscan.net/bot-detection", wait_until="networkidle", timeout=30000)
page.wait_for_timeout(5000)
title = page.title()
assert title # Page loaded
results = page.evaluate("""() => {
const text = document.body.innerText;
const normalMatches = text.match(/Normal/g);
const abnormalMatches = text.match(/Abnormal/g);
return {
normal: normalMatches ? normalMatches.length : 0,
abnormal: abnormalMatches ? abnormalMatches.length : 0
};
}""")
assert results["abnormal"] == 0, \
f"BrowserScan: {results['abnormal']} abnormal, {results['normal']} normal"
@pytest.mark.slow
def test_device_and_browser_info(self, page):
"""deviceandbrowserinfo.com should report isBot: false."""
page.goto("https://deviceandbrowserinfo.com/are_you_a_bot", timeout=30000)
page.wait_for_timeout(5000)
content = page.content()
# The page shows bot detection results
assert "deviceandbrowserinfo" in page.url.lower()
"""deviceandbrowserinfo.com — isBot must be false."""
page.goto("https://deviceandbrowserinfo.com/are_you_a_bot", wait_until="domcontentloaded", timeout=30000)
page.wait_for_timeout(8000)
results = page.evaluate("""() => {
const text = document.body.innerText;
const botMatch = text.match(/"isBot":\\s*(true|false)/);
const isBot = botMatch ? botMatch[1] === 'true' : null;
const checks = {};
['isBot', 'hasBotUserAgent', 'hasWebdriverTrue', 'isHeadlessChrome',
'isAutomatedWithCDP', 'hasSuspiciousWeakSignals', 'isPlaywright',
'hasInconsistentChromeObject'].forEach(p => {
const match = text.match(new RegExp('"' + p + '":\\s*(true|false)'));
if (match) checks[p] = match[1] === 'true';
});
return {isBot, checks};
}""")
assert results["isBot"] is False, f"Detected as bot! Checks: {results['checks']}"
@pytest.mark.slow
def test_fingerprintjs(self, page):
"""FingerprintJS — must not be blocked, should see flight data."""
page.goto("https://demo.fingerprint.com/web-scraping", wait_until="domcontentloaded", timeout=30000)
page.wait_for_timeout(8000)
try:
page.click("button:has-text('Search')", timeout=5000)
page.wait_for_timeout(5000)
except Exception:
pass
results = page.evaluate("""() => {
const text = document.body.innerText;
const hasFlights = text.includes('Price per adult') || text.includes('$');
const isBlocked = text.includes('request was blocked') || text.includes('bot visit detected');
return {passed: hasFlights && !isBlocked, isBlocked, hasFlights};
}""")
assert not results["isBlocked"], "FingerprintJS blocked us as a bot"
assert results["passed"], "FingerprintJS: no flight data shown"
@pytest.mark.slow
def test_recaptcha_v3(self, page):
"""reCAPTCHA v3 — score must be >= 0.7."""
page.goto(
"https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php",
wait_until="domcontentloaded",
timeout=60000,
)
page.wait_for_timeout(8000)
results = page.evaluate("""() => {
const text = document.body.innerText;
const scoreMatch = text.match(/"score":\\s*(\\d+\\.\\d+)/);
return {score: scoreMatch ? parseFloat(scoreMatch[1]) : null};
}""")
score = results["score"]
assert score is not None, "Could not extract reCAPTCHA score"
assert score >= 0.7, f"reCAPTCHA score too low: {score}"