From ac8945ca4b456350169f5d8822a94c931e9617f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 07:54:20 +0200 Subject: [PATCH] --fingerprint dropped deviceScaleFactor, so the identity contradicted itself `playwright_context_kwargs` mapped the user agent, the locale, the timezone and the screen onto the browser context and ignored `deviceScaleFactor`, which the fingerprint API returns beside them. Measured 2026-09-11 against the live API and a live browser: a fingerprint stating `deviceScaleFactor: 1.25` produced a browser reporting `window.devicePixelRatio === 1`. That is the mismatch this flag exists to prevent, on an axis any fingerprinter reads for free and for nothing: the paid identity said one thing and the browser said another, on every run, silently. Same shape as the three defects the family notes already record for this file -- a key the API returns that nothing applies -- and found the same way, by comparing a live browser against the fingerprint instead of by reading the code. Playwright takes it as its own context option, so the fix is to pass it. Verified in a live browser with this repo's own client: 1.25 requested, 1.25 reported, with the user agent, timezone and locale still matching. The check is pinned in the offline suite so it stays fixed. Found while auditing a new sibling repo against the family notes. All five repos in this family had it. Co-Authored-By: Claude Opus 5 (1M context) --- fingerprint_client.py | 14 ++++++++++++++ smoke_test.py | 10 +++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/fingerprint_client.py b/fingerprint_client.py index 29c2ebc..11e295c 100644 --- a/fingerprint_client.py +++ b/fingerprint_client.py @@ -241,6 +241,20 @@ def playwright_context_kwargs(fp: dict) -> dict: kwargs["viewport"] = {"width": outer_w, "height": max(400, outer_h)} kwargs["screen"] = {"width": width, "height": height} + # The device pixel ratio, which Playwright takes as its own context + # option and which was previously dropped on the floor. Measured + # 2026-09-11 against the live API and a live browser: a fingerprint + # stating `deviceScaleFactor: 1.25` produced a browser reporting + # `window.devicePixelRatio === 1`, so the identity contradicted itself + # on an axis any fingerprinter reads for free -- and a contradiction is + # exactly what this flag exists to avoid. Same shape as the three + # defects the family notes already list for this file: a key the API + # returns that nothing applies. Found by comparing a live browser + # against the fingerprint rather than by reading the code. + scale = (fp.get("screen") or {}).get("deviceScaleFactor") + if isinstance(scale, (int, float)) and not isinstance(scale, bool) and scale > 0: + kwargs["device_scale_factor"] = float(scale) + intl = fp.get("intl") or {} # The fingerprint's OWN locale. This used to be built as # f"en-{country}", which produced "en-DE" for a German fingerprint — an diff --git a/smoke_test.py b/smoke_test.py index db22703..8763802 100644 --- a/smoke_test.py +++ b/smoke_test.py @@ -1407,7 +1407,8 @@ def wait_for_timeout(self, ms): pass from fingerprint_client import playwright_context_kwargs, playwright_init_script FP = {"id": "fp_test", "country": "us", - "screen": {"width": 1920, "height": 1080}, + "screen": {"width": 1920, "height": 1080, + "deviceScaleFactor": 1}, "userAgent": {"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/145.0.0.0"}, "navigator": {"platform": "Win32", "hardwareConcurrency": 8, "deviceMemory": 8}, "webgl": {"vendor": 'Google Inc. "quoted"', "renderer": "ANGLE (RTX 3060)"}} @@ -1421,6 +1422,13 @@ def wait_for_timeout(self, ms): pass and kw["screen"]["width"] == 1920) js = playwright_init_script(FP) + # The device pixel ratio, which Playwright takes as its own option + # and which was dropped on the floor until a live browser was + # compared against the fingerprint: one stating 1.25 produced a + # browser reporting `devicePixelRatio === 1`, an identity + # contradicting itself on an axis a fingerprinter reads for free. + ok &= check("fingerprint: the device scale factor is carried", + kw.get("device_scale_factor") == 1) ok &= check("fingerprint: values are JSON-encoded, so a quote in an API string " "cannot break out of the script", '\\"quoted\\"' in js and "Google Inc. \"quoted\"" not in js)