Skip to content

feat: stealth-browser detection (F1) (#11)#27

Merged
cport1 merged 1 commit into
mainfrom
feat/stealth-detection
Jul 19, 2026
Merged

feat: stealth-browser detection (F1) (#11)#27
cport1 merged 1 commit into
mainfrom
feat/stealth-detection

Conversation

@cport1

@cport1 cport1 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Adds node 0.4.0's headline capability the plugin was missing: stealth-browser detection. The existing scanner has solid coverage (webdriver, headless, WebRTC, canvas/WebGL) but no lie detection — which is exactly the class of scraper (puppeteer-extra-stealth, botasaurus) purpose-built to defeat those signals.

How it works

A real, unmodified browser never rewrites its own native functions. Stealth tooling patches them to spoof away tells — but the patch is itself detectable: a patched function's toString() no longer returns "[native code]".

detectLies() probes for this, split by false-positive risk:

  • strong (near-zero FP) — patched Function.prototype.toString (stealth plugins do this to hide their other patches) and a modified navigator.webdriver getter. These are automation signatures.
  • weak (real privacy extensions can trigger)permissions.query, HTMLCanvasElement.toDataURL, WebGLRenderingContext.getParameter, mediaDevices.enumerateDevices, Notification.requestPermission.

Scoring (aligned with node, tuned for zero FP)

  • Scanner (0–100): 1 strong tell = 70, 2 = 82 (blocks alone); a lone weak tell = 0; multiple weak cap at 40 (never auto-blocks).
  • WebDecoy_Behavioral_Scorer: new stealth category applied as a max() override (not a re-weight), so a confirmed tell is decisive — mirroring node weighting stealth highest (0.30) — while leaving the existing PoW-verify model unchanged when no lie data is present. Curve matches node: 0.7 + 0.12·(strong−1), capped 0.97.

The DoD's hard requirement: zero new false positives

Tested explicitly. Stock Chrome/Safari/Firefox patch nothing → score 0. A privacy-extension user who trips a single weak tell → score 0, never blocked. This is the cautionary lesson from the 1.3.8 no_plugins removal and node dropping its FP-prone Playwright heuristics — encoded as assertions here.

Tests

tests/StealthScorerTest.php (6 tests): the strong/weak curve, decisive max() override, and FP-safety for no-lies / single-weak / multiple-weak. Full PHP suite 41 assertions, green. The scanner's JS scoring math was cross-checked with a standalone harness. (detectLies() itself is a DOM probe, exercised in a real browser rather than headlessly.)

Closes #11. Part of #16.

Adds the node 0.4.0 headline capability the plugin was missing: detecting
scrapers that patch native browser functions to hide automation
(puppeteer-extra-stealth, botasaurus) — precisely the class that evades the
scanner's existing fingerprint checks.

- webdecoy-scanner.js detectLies(): probes for patched native functions via
  the [native code] toString tell. Split by false-positive risk:
    strong (near-zero FP): patched Function.prototype.toString, modified
      navigator.webdriver getter — automation signatures.
    weak (privacy extensions can trigger): permissions.query, toDataURL,
      WebGLRenderingContext.getParameter, mediaDevices.enumerateDevices,
      Notification.requestPermission.
  Scored on the 0-100 scale: 1 strong=70, 2 strong=82 (blocks alone); a lone
  weak tell scores 0; multiple weak cap at 40 (never auto-block). Lie flags
  are forwarded for attribution.
- WebDecoy_Behavioral_Scorer: new stealth category applied as a max()
  override (not a re-weight) so it's decisive when present but preserves the
  existing PoW-verify model otherwise. Mirrors node's curve (0.7 base, +0.12
  per additional strong tell, cap 0.97).

FP-safety is explicit in tests: stock Chrome/Safari/Firefox and single-weak
(privacy-extension) sessions never reach a blocking score. 41 assertions
green; JS scoring math cross-checked.

Closes #11. Part of #16.
Co-authored-by: Claude <noreply@anthropic.com>
@cport1
cport1 merged commit 16bacc3 into main Jul 19, 2026
3 checks passed
@cport1
cport1 deleted the feat/stealth-detection branch July 19, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stealth-browser detection (F1) — converge browser payload on @webdecoy/client

1 participant