From a3e0994a313cbd718fe20ff05a7b36493f4d32c6 Mon Sep 17 00:00:00 2001 From: CloakHQ Date: Fri, 3 Jul 2026 00:01:46 +0200 Subject: [PATCH] =?UTF-8?q?release:=20v0.4.7=20=E2=80=94=20update=20cloakt?= =?UTF-8?q?est?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 + README.md | 20 +-- cloakbrowser/_version.py | 2 +- dotnet/src/CloakBrowser/CloakBrowser.csproj | 2 +- dotnet/src/CloakBrowser/CloakVersion.cs | 2 +- examples/stealth_test.py | 156 ++++++++++---------- js/package-lock.json | 4 +- js/package.json | 2 +- 8 files changed, 101 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8574d1..56a02cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Changes are tagged: **[wrapper]** for Python/JS wrapper, **[binary]** for Chromi ## [Unreleased] +## [0.4.7] — 2026-07-02 + +- **[docker]** Update the public `cloaktest` suite to use free-tier-stable bot-detection checks while Pro continues to verify reCAPTCHA v3 at 0.9. The Docker smoke test now runs Sannysoft, Incolumitas, Rebrowser, deviceandbrowserinfo, BrowserScan, and CreepJS lies/noise=false; FingerprintJS and reCAPTCHA v3 are no longer hard-pass checks for the free v146 image. + ## [0.4.6] — 2026-07-02 - **[wrapper]** The resolved Pro license key is now passed to the browser process at launch (via `CLOAKBROWSER_LICENSE_KEY`) so the Pro binary can authenticate itself, including when you supply a custom `env`. Fixes launch failures on the newest Pro binaries. Python, JS, and .NET. diff --git a/README.md b/README.md index 9bbea79..1c45b41 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@ Same API, same code — just swap the import. 3 lines of code, 30 second - **58 source-level C++ patches** — canvas, WebGL, audio, fonts, GPU, screen, WebRTC, network timing, automation signals, CDP input behavior - **`humanize=True`** — human-like mouse curves, keyboard timing, and scroll patterns. One flag, behavioral detection passes -- **0.9 reCAPTCHA v3 score** — human-level, server-verified +- **Pro: 0.9 reCAPTCHA v3 score** — human-level, server-verified - **Passes Cloudflare Turnstile**, FingerprintJS, BrowserScan — tested against 30+ detection sites -- **Auto-updating binary** — background update checks, always on the latest stealth build +- **Auto-downloads the right binary** — free or Pro based on your license - **`pip install cloakbrowser`** or **`npm install cloakbrowser`** — binary auto-downloads, zero config -- **Free and open source** — no subscriptions, no usage limits +- **Open-source wrappers** — free v146 binary, Pro for latest builds **Try it now** — no install needed: @@ -150,7 +150,7 @@ page.goto("https://example.com") --- -## Latest: v0.4.6 — 59 source-level stealth patches, now on every platform (Chromium 148.0.7778.215.3) +## Latest: v0.4.7 — 59 source-level stealth patches, now on every platform (Chromium 148.0.7778.215.3) - **CloakBrowser Pro** — the latest binary (Chromium 148.0.7778.215.3, 59 source-level patches) is available to Pro subscribers, now on **all platforms including macOS** (Apple Silicon + Intel); v146 stays free forever. Set a `license_key` (`licenseKey` in JS) or the `CLOAKBROWSER_LICENSE_KEY` env var and the wrapper fetches the latest build automatically. See [CloakBrowser Pro](#cloakbrowser-pro) - **.NET 8 / C# client** — CloakBrowser now ships as a NuGet package (`CloakBrowser`), mirroring the Python and JS wrappers. @@ -206,15 +206,15 @@ Pro plans → **[cloakbrowser.dev](https://cloakbrowser.dev)** ## Test Results -All tests verified against live detection services. Last tested: Jun 2026 (Chromium 148). +All tests verified against live detection services. Results below are for the latest Pro/current build unless noted. Last tested: Jul 2026 (Chromium 148). | Detection Service | Stock Playwright | CloakBrowser | Notes | |---|---|---|---| -| **reCAPTCHA v3** | 0.1 (bot) | **0.9** (human) | Server-side verified | +| **reCAPTCHA v3** | 0.1 (bot) | **0.9** (human) | Pro/current build; server-side verified | | **Cloudflare Turnstile** (non-interactive) | FAIL | **PASS** | Auto-resolve | | **Cloudflare Turnstile** (managed) | FAIL | **PASS** | Single click | | **ShieldSquare** | BLOCKED | **PASS** | Production site | -| **FingerprintJS** bot detection | DETECTED | **PASS** | demo.fingerprint.com | +| **FingerprintJS** bot detection | DETECTED | **PASS** | Pro/current build; demo.fingerprint.com | | **BrowserScan** bot detection | DETECTED | **NORMAL** (4/4) | browserscan.net | | **bot.incolumitas.com** | 13 fails | **1 fail** | WEBDRIVER spec only | | **deviceandbrowserinfo.com** | 6 true flags | **0 true flags** | `isBot: false` | @@ -230,7 +230,7 @@ All tests verified against live detection services. Last tested: Jun 2026 (Chrom

reCAPTCHA v3 — Score 0.9 -
reCAPTCHA v3 score 0.9 — server-side verified (human-level) +
Pro/latest build: reCAPTCHA v3 score 0.9 — server-side verified (human-level)

@@ -245,7 +245,7 @@ All tests verified against live detection services. Last tested: Jun 2026 (Chrom

FingerprintJS — Passed -
FingerprintJS web-scraping demo — data served, not blocked +
Pro/latest build: FingerprintJS web-scraping demo — data served, not blocked

@@ -257,7 +257,7 @@ All tests verified against live detection services. Last tested: Jun 2026 (Chrom | Feature | Playwright | playwright-stealth | undetected-chromedriver | Camoufox | CloakBrowser | |---|---|---|---|---|---| -| reCAPTCHA v3 score | 0.1 | 0.3-0.5 | 0.3-0.7 | 0.7-0.9 | **0.9** | +| reCAPTCHA v3 score (Pro/current) | 0.1 | 0.3-0.5 | 0.3-0.7 | 0.7-0.9 | **0.9** | | Cloudflare Turnstile | Fail | Sometimes | Sometimes | Pass | **Pass** | | Patch level | None | JS injection | Config patches | C++ (Firefox) | **C++ (Chromium)** | | Survives Chrome updates | N/A | Breaks often | Breaks often | Yes | **Yes** | diff --git a/cloakbrowser/_version.py b/cloakbrowser/_version.py index 3dd3d2d..a34b2f6 100644 --- a/cloakbrowser/_version.py +++ b/cloakbrowser/_version.py @@ -1 +1 @@ -__version__ = "0.4.6" +__version__ = "0.4.7" diff --git a/dotnet/src/CloakBrowser/CloakBrowser.csproj b/dotnet/src/CloakBrowser/CloakBrowser.csproj index 7532aa6..5eeafcb 100644 --- a/dotnet/src/CloakBrowser/CloakBrowser.csproj +++ b/dotnet/src/CloakBrowser/CloakBrowser.csproj @@ -10,7 +10,7 @@ CloakBrowser - 0.4.6 + 0.4.7 CloakHQ Stealth Chromium that passes every bot detection test. Drop-in Playwright (.NET) replacement with source-level fingerprint patches. MIT diff --git a/dotnet/src/CloakBrowser/CloakVersion.cs b/dotnet/src/CloakBrowser/CloakVersion.cs index a6383d5..fcb4a35 100644 --- a/dotnet/src/CloakBrowser/CloakVersion.cs +++ b/dotnet/src/CloakBrowser/CloakVersion.cs @@ -4,5 +4,5 @@ namespace CloakBrowser; public static class CloakVersion { ///

The CloakBrowser .NET wrapper version. - public const string Version = "0.4.6"; + public const string Version = "0.4.7"; } diff --git a/examples/stealth_test.py b/examples/stealth_test.py index 6950ae8..bf9d26e 100644 --- a/examples/stealth_test.py +++ b/examples/stealth_test.py @@ -10,7 +10,6 @@ Usage: python examples/stealth_test.py --proxy http://10.50.96.5:8888 """ -import json import sys import time @@ -54,8 +53,8 @@ def test_bot_incolumitas(page): """bot.incolumitas.com — comprehensive 30+ check bot detection.""" page.goto("https://bot.incolumitas.com", wait_until="networkidle", timeout=30000) - # Poll until test count stabilizes (site runs tests progressively) last_total = 0 + results = {"passed": 0, "failed": 0, "failedTests": [], "total": 0} for _ in range(15): time.sleep(2) results = page.evaluate("""() => { @@ -63,12 +62,7 @@ def test_bot_incolumitas(page): 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, - total: okMatches.length + failMatches.length - }; + return {passed: okMatches.length, failed: failMatches.length, failedTests, total: okMatches.length + failMatches.length}; }""") if results["total"] >= 30 and results["total"] == last_total: break @@ -77,6 +71,22 @@ def test_bot_incolumitas(page): return results +def test_rebrowser(page): + """bot-detector.rebrowser.net — automation signal detector.""" + page.goto("https://bot-detector.rebrowser.net/", wait_until="networkidle", timeout=30000) + time.sleep(10) + + return page.evaluate("""() => { + const el = document.getElementById('detections-json'); + if (!el) return {failing: [], totalFails: 0, passed: 0, notTriggered: 0, error: 'no detections-json element'}; + const tests = JSON.parse(el.value); + const failing = tests.filter(t => t.rating === 1).map(t => t.type); + const passed = tests.filter(t => t.rating === -1).length; + const notTriggered = tests.filter(t => t.rating === 0).length; + return {failing, totalFails: failing.length, passed, notTriggered, total: tests.length}; + }""") + + def test_browserscan(page): """browserscan.net/bot-detection — WebDriver, UA, CDP, Navigator checks.""" page.goto("https://www.browserscan.net/bot-detection", wait_until="networkidle", timeout=30000) @@ -123,47 +133,17 @@ def test_deviceandbrowserinfo(page): return results -def test_fingerprintjs(page): - """demo.fingerprint.com/web-scraping — industry-standard bot detection.""" - page.goto("https://demo.fingerprint.com/web-scraping", wait_until="domcontentloaded", timeout=30000) - time.sleep(8) +def test_creepjs_noise_off(page): + """CreepJS — lie detection with fingerprint noise disabled.""" + page.goto("https://abrahamjuliot.github.io/creepjs/", wait_until="domcontentloaded", timeout=30000) + time.sleep(15) - # Click search to trigger bot detection — bots get blocked, humans see flights - try: - page.click("button:has-text('Search')", timeout=5000) - time.sleep(5) - except Exception: - pass - - results = page.evaluate("""() => { - const text = document.body.innerText; - // Bots see error messages; humans see flight prices - 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}; + return page.evaluate("""() => { + const fp = window.Fingerprint; + if (!fp) return {error: 'Fingerprint not ready', totalLies: null}; + const lies = fp.lies || {}; + return {totalLies: lies.totalLies || 0}; }""") - return results - - -def test_recaptcha(page): - """recaptcha-demo.appspot.com — Google's official reCAPTCHA v3 score.""" - page.goto( - "https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php", - wait_until="domcontentloaded", - timeout=30000, - ) - # Wait for score to appear (polls up to 30s) - for _ in range(15): - time.sleep(2) - score = page.evaluate("""() => { - const text = document.body.innerText; - const match = text.match(/"score":\\s*(\\d+\\.\\d+)/); - return match ? parseFloat(match[1]) : null; - }""") - if score is not None: - break - - return {"score": score} TESTS = [ @@ -180,11 +160,33 @@ TESTS = [ "url": "https://bot.incolumitas.com", "runner": test_bot_incolumitas, "verdict": lambda r: f"{r['passed']}/{r['total']} passed" - + (" — ALL GREEN" if r.get("failed", 0) == 0 - else f" (FAILED: {', '.join(r.get('failedTests', []))} — known false positives)" - if set(r.get("failedTests", [])) <= {"WEBDRIVER", "connectionRTT"} - else f" (FAILED: {', '.join(r.get('failedTests', []))})"), - "pass": lambda r: set(r.get("failedTests", [])) <= {"WEBDRIVER", "connectionRTT"}, # known false positives + + ( + " — ALL GREEN" + if r.get("failed", 0) == 0 + else f" (FAILED: {', '.join(r.get('failedTests', []))} — known false positives)" + if set(r.get("failedTests", [])) <= {"WEBDRIVER", "connectionRTT"} + else f" (FAILED: {', '.join(r.get('failedTests', []))})" + ), + "pass": lambda r: set(r.get("failedTests", [])) <= {"WEBDRIVER", "connectionRTT"}, + }, + { + "name": "Rebrowser Bot Detector", + "url": "https://bot-detector.rebrowser.net/", + "runner": test_rebrowser, + "verdict": lambda r: ( + f"🟢{r.get('passed', 0)} ⚪{r.get('notTriggered', 0)} — ALL CLEAN" + if r.get("totalFails", 1) == 0 + else f"FAIL: {', '.join(r.get('failing', []))}" + ), + "pass": lambda r: r.get("totalFails", 1) == 0, + }, + { + "name": "deviceandbrowserinfo.com", + "url": "https://deviceandbrowserinfo.com/are_you_a_bot", + "runner": test_deviceandbrowserinfo, + "verdict": lambda r: f"isBot: {r.get('isBot', 'unknown')}" + + (f", trueFlags: {sum(1 for v in r.get('checks', {}).values() if v)}" if r.get("checks") else ""), + "pass": lambda r: not r.get("isBot", True) and not any(r.get("checks", {}).values()), }, { "name": "BrowserScan", @@ -194,26 +196,12 @@ TESTS = [ "pass": lambda r: r.get("abnormal", 1) == 0, }, { - "name": "deviceandbrowserinfo.com", - "url": "https://deviceandbrowserinfo.com/are_you_a_bot", - "runner": test_deviceandbrowserinfo, - "verdict": lambda r: f"isBot: {r.get('isBot', 'unknown')}" - + (f" checks: {json.dumps(r.get('checks', {}))}" if r.get("checks") else ""), - "pass": lambda r: not r.get("isBot", True), - }, - { - "name": "FingerprintJS", - "url": "https://demo.fingerprint.com/web-scraping", - "runner": test_fingerprintjs, - "verdict": lambda r: "PASSED (flights shown)" if r.get("passed") else "BLOCKED" if r.get("isBlocked") else "NO FLIGHTS", - "pass": lambda r: r.get("passed", False), - }, - { - "name": "reCAPTCHA v3 (Google)", - "url": "https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php", - "runner": test_recaptcha, - "verdict": lambda r: f"Score: {r.get('score', 'N/A')}", - "pass": lambda r: (r.get("score") or 0) >= 0.7, + "name": "CreepJS lies (noise=false)", + "url": "https://abrahamjuliot.github.io/creepjs/", + "runner": test_creepjs_noise_off, + "verdict": lambda r: f"lies: {r.get('totalLies', 'N/A')}", + "pass": lambda r: r.get("totalLies") == 0, + "args": ["--fingerprint-noise=false"], }, ] @@ -229,6 +217,7 @@ def main(): print("Launching stealth browser...", flush=True) browser = launch(headless=not HEADED, proxy=PROXY, geoip=True) + shared_browser_closed = False page = browser.new_page() # Show browser fingerprint details @@ -281,8 +270,19 @@ def main(): print(f"--- {name} ---") print(f"URL: {test['url']}") + test_browser = browser + test_context = None + test_page = page try: - result = test["runner"](page) + if test.get("args"): + browser.close() + shared_browser_closed = True + test_browser = launch(headless=not HEADED, proxy=PROXY, geoip=True, args=test["args"]) + + test_context = test_browser.new_context(viewport={"width": 1920, "height": 1080}) + test_page = test_context.new_page() + + result = test["runner"](test_page) passed = test["pass"](result) verdict = test["verdict"](result) status = "PASS" if passed else "FAIL" @@ -292,16 +292,22 @@ def main(): if SCREENSHOTS: filename = f"stealth_test_{name.replace('.', '_').replace(' ', '_').replace('/', '_')}.png" - page.screenshot(path=filename) + test_page.screenshot(path=filename) print(f"Screenshot: {filename}") except Exception as e: results_summary.append((name, "ERROR", str(e))) print(f"Error: {e}") + finally: + if test_context is not None: + test_context.close() + if test_browser is not browser: + test_browser.close() print() - browser.close() + if not shared_browser_closed: + browser.close() # Summary table print("=" * 60) diff --git a/js/package-lock.json b/js/package-lock.json index a737f62..9a08198 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "cloakbrowser", - "version": "0.4.6", + "version": "0.4.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cloakbrowser", - "version": "0.4.6", + "version": "0.4.7", "license": "MIT", "dependencies": { "tar": "^7.0.0" diff --git a/js/package.json b/js/package.json index 68a2adc..81744c3 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "cloakbrowser", - "version": "0.4.6", + "version": "0.4.7", "description": "Stealth Chromium that passes every bot detection test. Drop-in Playwright/Puppeteer replacement with source-level fingerprint patches.", "type": "module", "main": "dist/index.js",