Stealth's WebGL vendor spoof contradicts navigator.gpu adapter.info (cross-interface GPU mismatch)

Author: liarjsdevCreated Jul 29, 2026Updated Jul 29, 2026

Summary

puppeteer-extra-plugin-stealth rewrites WEBGL_debug_renderer_info to an Intel GPU but leaves navigator.gpu.requestAdapter().info reporting the machine's real adapter. On a non-Intel host the two graphics interfaces now disagree — a state a real browser cannot be in, and a cheap check for anything that reads both.

Environment

  • puppeteer-extra 3.3.6, puppeteer-extra-plugin-stealth 2.11.2, puppeteer 25.4.0 (bundled Chrome for Testing 151)
  • Windows 11, host GPU: AMD Radeon 780M (RDNA-3)

What I measured

Unpatched Chrome on the same machine, same page:

WebGL unmasked : Google Inc. (AMD) / ANGLE (AMD, AMD Radeon 780M Graphics (0x00001900) Direct3D11 vs_5_0 ps_5_0, D3D11)

With the stealth plugin (headed and headless, identical result):

WebGL  : Intel Inc. / Intel Iris OpenGL Engine     <- plugin default
WebGPU : navigator.gpu adapter.info.vendor = "amd", architecture = "rdna-3"   <- real hardware

Everything else the plugin covers holds up: navigator.webdriver is gone, the HeadlessChrome UA token is masked even in headless, navigator.languages looks normal, canvas/DOMRect are stable, native functions still report [native code]. Scoring the page against a consistency checker, the stealth profile lands at 66/100 and the entire deduction is this one mismatch; an unpatched Chrome driven over a plain debug port scores 88/100 on the same machine (the missing 12 there is an environment artifact — my egress IP resolves to a different timezone than the OS).

So on any non-Intel machine the WebGL evasion currently makes the profile more detectable than leaving WebGL alone, as soon as the detector also reads WebGPU.

Repro

bash
npx liarjs --cdp http://127.0.0.1:9222     # attach to the stealth-launched browser
# or just open https://liarjs.dev in the page the plugin created

Possible fixes

  1. Spoof navigator.gpu's adapter.info (vendor/architecture/description) to match whatever the WebGL evasion claims; or
  2. derive the WebGL spoof from the real adapter instead of a fixed Intel string; or
  3. make webgl.vendor opt-in and document the WebGPU side effect.

Happy to send a PR for whichever direction you prefer.

Disclosure: the numbers come from liarjs, an MIT-licensed consistency checker I maintain. Single machine, single run per configuration — if you can't reproduce on an Intel host, that's expected: the mismatch only shows when the real GPU isn't Intel.

Source: berstend/puppeteer-extra