mirror of
https://github.com/daijro/camoufox.git
synced 2026-10-04 16:00:21 +00:00
A preset records only its GPU's name. The WebGL parameters, extensions and shader precision behind it have to come from somewhere, and for these 23 nothing Camoufox has describes the GPU: fpgen has never seen Firefox report it on that OS. So each launch paired the name with another device's parameters, a mismatch any WebGL fingerprinter can see. They were: - Windows on ARM (Adreno 650); - Direct3D 10-level GPUs (vs_4_0/vs_4_1); - "Generic Renderer"; - 945GM and GTX 480 on macOS; - nouveau/Mesa buckets on Linux; - one Linux preset pairing NVIDIA's proprietary vendor string with the nouveau renderer name. scripts/clean-fingerprint-data.py now applies the rule, via a shared fingerprints.firefox_gpus(), and test_shipped_data asserts it. 374 presets remain, and every OS keeps its presets. ROADMAP.md lists capturing WebGL data for these GPUs, which would bring them back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
115 lines
4.5 KiB
Python
115 lines
4.5 KiB
Python
"""The data files that ship must not contain an identity we would refuse to use.
|
|
|
|
`camoufox.coherence` filters at draw time, so an impossible row can never reach
|
|
a page either way. This is the second filter, on the data itself: a row that
|
|
survives here is one a future refresh could ship, a consumer reading the JSON
|
|
directly would get, and a reviewer would take as real. Both filters run the same
|
|
rules -- `scripts/clean-fingerprint-data.py --write` is what makes these pass.
|
|
|
|
Dropped on 2026-09-17: 38 of 435 presets (26 with a GPU their OS cannot report,
|
|
7 pairing Apple Silicon with a core count Apple never shipped, 4 with a colour
|
|
depth their GPU contradicts, 3 with a phone viewport, 1 claiming 40 touch
|
|
points), and 2 impossible macOS weights in webgl_data.db.
|
|
"""
|
|
|
|
import json
|
|
import sqlite3
|
|
import sys
|
|
from os.path import dirname, join
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, join(dirname(__file__), ".."))
|
|
|
|
from camoufox import coherence # noqa: E402
|
|
from camoufox.fingerprints import from_preset # noqa: E402
|
|
from camoufox.webgl.sample import DB_PATH # noqa: E402
|
|
|
|
DATA = Path(__file__).parent.parent / "camoufox"
|
|
PRESET_FILES = ("fingerprint-presets.json", "fingerprint-presets-v150.json")
|
|
OS_KEY = {"macos": "mac", "windows": "win", "linux": "lin"}
|
|
|
|
|
|
@pytest.mark.parametrize("filename", PRESET_FILES)
|
|
def test_every_bundled_preset_is_coherent_as_stored(filename):
|
|
presets = json.loads((DATA / filename).read_text())["presets"]
|
|
for os_name, entries in presets.items():
|
|
assert entries, f"{filename}/{os_name} has no presets left"
|
|
for index, preset in enumerate(entries):
|
|
config = from_preset(preset, "152")
|
|
violations = coherence.validate(config, OS_KEY[os_name])
|
|
assert violations == [], (
|
|
f"{filename} {os_name}[{index}]: "
|
|
+ "; ".join(v.detail for v in violations)
|
|
+ " -- run scripts/clean-fingerprint-data.py --write"
|
|
)
|
|
|
|
|
|
def test_no_gpu_is_offered_to_an_os_that_cannot_report_it():
|
|
connection = sqlite3.connect(DB_PATH)
|
|
try:
|
|
rows = connection.execute(
|
|
"SELECT vendor, renderer, win, mac, lin FROM webgl_fingerprints"
|
|
).fetchall()
|
|
finally:
|
|
connection.close()
|
|
assert rows, "webgl_data.db is empty"
|
|
for vendor, renderer, *weights in rows:
|
|
for os_key, weight in zip(("win", "mac", "lin"), weights):
|
|
if weight and weight > 0:
|
|
assert coherence.gpu_fits_os(renderer, os_key), (
|
|
f"{renderer!r} is offered to {os_key} at {weight}"
|
|
)
|
|
|
|
|
|
def test_each_os_still_has_gpus_to_draw_from():
|
|
"""The filter must not empty a pool -- a single GPU per OS is its own tell."""
|
|
connection = sqlite3.connect(DB_PATH)
|
|
try:
|
|
for os_key in ("win", "mac", "lin"):
|
|
count = connection.execute(
|
|
f"SELECT COUNT(*) FROM webgl_fingerprints WHERE {os_key} > 0" # nosec
|
|
).fetchone()[0]
|
|
assert count >= 2, f"{os_key} has {count} GPU(s) left"
|
|
finally:
|
|
connection.close()
|
|
|
|
|
|
def test_every_gpu_row_can_be_drawn_on_some_os():
|
|
"""A row no OS can draw is dead weight a reader would take as real data."""
|
|
connection = sqlite3.connect(DB_PATH)
|
|
try:
|
|
rows = connection.execute(
|
|
"SELECT vendor, renderer, win, mac, lin FROM webgl_fingerprints"
|
|
).fetchall()
|
|
finally:
|
|
connection.close()
|
|
undrawable = [
|
|
(vendor, renderer)
|
|
for vendor, renderer, *weights in rows
|
|
if not any(
|
|
weight > 0 and coherence.gpu_fits_os(renderer, os_key)
|
|
for os_key, weight in zip(("win", "mac", "lin"), weights)
|
|
)
|
|
]
|
|
assert undrawable == []
|
|
|
|
|
|
@pytest.mark.parametrize("filename", PRESET_FILES)
|
|
def test_every_preset_gpu_has_webgl_data(filename):
|
|
"""A preset records only its GPU's name; the WebGL parameters behind it come
|
|
from fpgen. A GPU fpgen has never seen Firefox report on that OS has none, so
|
|
launching it would pair the name with another device's parameters."""
|
|
from camoufox.fingerprints import firefox_gpus
|
|
|
|
presets = json.loads((DATA / filename).read_text())["presets"]
|
|
for os_name, entries in presets.items():
|
|
known = firefox_gpus(os_name)
|
|
for index, preset in enumerate(entries):
|
|
gpu = (preset["webgl"]["unmaskedVendor"], preset["webgl"]["unmaskedRenderer"])
|
|
assert gpu in known, (
|
|
f"{filename} {os_name}[{index}]: {gpu[1]!r} has no WebGL data"
|
|
" -- run scripts/clean-fingerprint-data.py --write"
|
|
)
|