feat: cloakbrowser v0.1.0 — stealth Chromium wrapper for Playwright

Drop-in Playwright replacement with source-level fingerprint patches.
Auto-downloads patched Chromium 142 from GitHub Releases on first use.
17/17 stealth tests passing (reCAPTCHA 0.9, Cloudflare Turnstile, BrowserScan).
This commit is contained in:
CloakHQ
2026-02-22 10:30:25 +01:00
commit 840dc8f88d
20 changed files with 1277 additions and 0 deletions
View File
+81
View File
@@ -0,0 +1,81 @@
"""Basic launch tests for cloakbrowser."""
import pytest
from cloakbrowser import launch, launch_async, binary_info, CHROMIUM_VERSION
def test_binary_info():
"""binary_info() returns expected structure."""
info = binary_info()
assert "version" in info
assert "platform" in info
assert "binary_path" in info
assert "installed" in info
assert info["version"] == CHROMIUM_VERSION
def test_launch_and_close():
"""Can launch browser and close it."""
browser = launch(headless=True)
assert browser.is_connected()
browser.close()
def test_launch_new_page():
"""Can create a page and navigate."""
browser = launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
assert "Example Domain" in page.title()
browser.close()
def test_launch_with_extra_args():
"""Can pass extra Chrome args."""
browser = launch(headless=True, args=["--disable-gpu"])
page = browser.new_page()
page.goto("https://example.com")
assert page.title()
browser.close()
def test_webdriver_flag():
"""navigator.webdriver should be false (patched)."""
browser = launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
webdriver = page.evaluate("navigator.webdriver")
assert webdriver is False, f"navigator.webdriver should be false, got {webdriver}"
browser.close()
def test_chrome_object_exists():
"""window.chrome should exist (Playwright leaks undefined)."""
browser = launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
chrome_exists = page.evaluate("typeof window.chrome")
assert chrome_exists == "object", f"window.chrome should be 'object', got '{chrome_exists}'"
browser.close()
def test_plugins_count():
"""navigator.plugins should have entries (Playwright has 0)."""
browser = launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
plugins = page.evaluate("navigator.plugins.length")
assert plugins > 0, f"Expected plugins > 0, got {plugins}"
browser.close()
@pytest.mark.asyncio
async def test_launch_async():
"""Async launch works."""
browser = await launch_async(headless=True)
assert browser.is_connected()
page = await browser.new_page()
await page.goto("https://example.com")
title = await page.title()
assert "Example Domain" in title
await browser.close()
+108
View File
@@ -0,0 +1,108 @@
"""Stealth detection tests for cloakbrowser.
These tests verify that the stealth Chromium binary passes common
bot detection checks. They require network access.
"""
import pytest
from cloakbrowser import launch
@pytest.fixture(scope="module")
def browser():
"""Shared browser instance for stealth tests."""
b = launch(headless=True)
yield b
b.close()
@pytest.fixture
def page(browser):
"""Fresh page for each test."""
p = browser.new_page()
yield p
p.close()
class TestWebDriverDetection:
"""Tests for WebDriver/automation detection signals."""
def test_navigator_webdriver_false(self, page):
"""navigator.webdriver must be false."""
page.goto("https://example.com")
assert page.evaluate("navigator.webdriver") is False
def test_no_headless_chrome_ua(self, page):
"""User agent must not contain 'HeadlessChrome'."""
page.goto("https://example.com")
ua = page.evaluate("navigator.userAgent")
assert "HeadlessChrome" not in ua
assert "Chrome/" in ua
def test_window_chrome_exists(self, page):
"""window.chrome must be an object (not undefined)."""
page.goto("https://example.com")
assert page.evaluate("typeof window.chrome") == "object"
def test_plugins_present(self, page):
"""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}"
def test_languages_present(self, page):
"""navigator.languages must be populated."""
page.goto("https://example.com")
langs = page.evaluate("navigator.languages")
assert len(langs) >= 1
def test_cdp_not_detected(self, page):
"""Chrome DevTools Protocol should not be detectable."""
page.goto("https://example.com")
# Common CDP detection: check for Runtime.evaluate artifacts
has_cdp = page.evaluate("""
() => {
try {
// Check common CDP leak: window.cdc_
const keys = Object.keys(window);
return keys.some(k => k.startsWith('cdc_') || k.startsWith('__webdriver'));
} catch(e) {
return false;
}
}
""")
assert has_cdp is False
class TestBotDetectionSites:
"""Live tests against bot detection services.
These require network access and may be slow.
Mark with pytest -m slow to skip in CI.
"""
@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
@pytest.mark.slow
def test_browserscan(self, page):
"""BrowserScan bot detection should show NORMAL."""
page.goto("https://www.browserscan.net/bot-detection", timeout=30000)
page.wait_for_timeout(5000)
title = page.title()
assert title # Page loaded
@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()