Stealth's WebGL vendor spoof contradicts navigator.gpu adapter.info (cross-interface GPU mismatch)
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 hardwareEverything 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
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 createdPossible fixes
- Spoof
navigator.gpu'sadapter.info(vendor/architecture/description) to match whatever the WebGL evasion claims; or - derive the WebGL spoof from the real adapter instead of a fixed Intel string; or
- make
webgl.vendoropt-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