WebGL renderer names a high-end GPU but the hardware capabilities are below its floor (spoofed string)

br.webgl_renderer_caps_mismatch  ·  convicts

browserlayer
coherencecategory
1evaders caught

What it catches

G18 — the renderer-string-vs-GPU-CAPABILITY coherence tell, aimed at the source-level anti-detect FORK frontier (CloakBrowser/Wayfern/BotBrowser etc.) that the string-based WebGL checks miss. Kitsune already reads the UNMASKED_RENDERER string and runs seven string checks on it (webgl_software/getparameter_tampered/os_vs_ua/worker_vs_main/renderer_artifact/not_angle/webgl2_missing), but never read the GPU's actual CAPABILITIES. A source-level fork patches the renderer STRING in BOTH realms — defeating br.webgl_worker_vs_main (which only catches a main-realm getParameter spoof that the Worker exposes) — yet cannot change what the physical silicon can do, so MAX_TEXTURE_SIZE and friends stay consistent with the real hardware. The collector now captures the capability fingerprint (webgl_caps + webgl_max_texture, main realm) and emits webgl_renderer_caps_mismatch when the renderer string names a recent HIGH-END discrete GPU (RTX / Radeon RX 6000+ / Apple M-series / Intel Arc) yet MAX_TEXTURE_SIZE < 16384 — below the floor EVERY such GPU exposes. GROUNDED on real data: headless Chrome's SwiftShader backend reports MAX_TEXTURE_SIZE=8192 (captured live), while all recent high-end GPUs report 16384 or 32768 (universal since ~2013; ANGLE on D3D11/Vulkan/Metal exposes the native max). FP-SAFE by the high-end-string scoping: a real RTX/Apple-M/Arc is always >=16384 → never fires; an honest software renderer NAMES itself (SwiftShader/llvmpipe → caught by webgl_software) so its string never matches the high-end pattern → never fires; a real low-end/mobile GPU at 8192 has a low-end renderer string, not 'RTX 4090', so it is out of scope. The only thing that trips it is a spoofed high-end renderer string over a backend (software or lesser GPU) whose caps betray the lie. CONVICTING (coherence): the claimed GPU identity contradicts the hardware it runs on. RED: the renderer-spoof evader patches UNMASKED_RENDERER to a high-end string over the SwiftShader (8192) backend → fires; every real-browser capture (honest renderer ↔ caps) does not. browserforge calibration carries the renderer string but not the live caps probe, so it cannot raise the legit flag rate. Lead G18; see docs/research-radar.md. Increment 1 (caps capture + display + this tell); the broader renderer↔caps profile match across more GPU tiers is a follow-up needing real-GPU caps profiles.

Signals it reads

browser.webgl_renderer_caps_mismatch

How it fires

present

Evaders it caught 1

Bypassed by 10

Frontier evaders that reach the detector uncaught (scored only suspicious, defeating every convicting tell) — this check is not one that stops them. The red-team frontier this detection still has to convict.

← all detections