mirror of
https://github.com/daijro/camoufox.git
synced 2026-10-04 16:00:21 +00:00
fix: remove the glyph-spacing seed from the browser and the launcher
anti-font-fingerprinting.patch added a seeded amount to every glyph advance, so that text widths differed per context. No real machine produces those widths: the same font on the same OS measures the same everywhere. So the noise was itself a fingerprint, measured in #779 at +1 px per ~100 glyphs plus fractional deltas on every measureText. #779 defaulted the seed to 0 and kept it as an opt-in, but an opt-in whose only effect is to become detectable is not worth carrying. Removed: - The browser side: - FontSpacingSeedManager and window.setFontSpacingSeed; - the HarfBuzz hook; - the plumbing that existed only to carry the context id down to the shaper: the userContextId on gfxTextRun, gfxShapedWord and the word-cache key, and the extra MakeTextRun argument in nsTextFrame, nsFontMetrics, MathML and canvas. The font group keeps its userContextId, which font-list-spoofing.patch uses to apply the per-context font list. Text is now shaped exactly as stock Firefox shapes it. - The fonts:spacing_seed key. The launcher had been sending 0 on every launch, plus a setFontSpacingSeed(0) call in every context's init script. - tests/patches/config-overrides.py, which tested only the spacing override. A pythonlib test now covers config_overrides with another key. timezone-spoofing, webrtc-ip-spoofing and window-setter-seal change only in context lines and the setter seal list. Every patch applies cleanly to a fresh tree, and the result builds. The settled decision is recorded as no-glyph-spacing-noise, with an automated check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
86a20c73b7
commit
676fb3f7c1
@@ -1645,9 +1645,6 @@ def from_preset(preset: Dict, ff_version: Optional[str] = None, salt: Optional[i
|
||||
config['webGl:renderer'] = webgl['unmaskedRenderer']
|
||||
|
||||
# Generate a unique audio seed per launch (1 to 2^32-1, excluding 0 which is a no-op in C++)
|
||||
# fonts:spacing_seed stays 0 (off): glyph-advance perturbation produces text
|
||||
# widths no real machine emits (see launch_options in utils.py).
|
||||
config['fonts:spacing_seed'] = 0
|
||||
config['audio:seed'] = randint(1, 4_294_967_295) # nosec
|
||||
|
||||
if preset.get('timezone'):
|
||||
@@ -1697,7 +1694,6 @@ def _build_init_script(values: Dict[str, Any]) -> str:
|
||||
lines = ['(function(v) {', ' var w = window;']
|
||||
|
||||
setters = [
|
||||
('fontSpacingSeed', 'setFontSpacingSeed', '{val}'),
|
||||
('audioFingerprintSeed', 'setAudioFingerprintSeed', '{val}'),
|
||||
('navigatorPlatform', 'setNavigatorPlatform', '{val}'),
|
||||
('navigatorOscpu', 'setNavigatorOscpu', '{val}'),
|
||||
@@ -1798,8 +1794,7 @@ def generate_context_fingerprint(
|
||||
normalize_locale() and injected into config. Also sets
|
||||
context_options['locale'] for Playwright.
|
||||
config_overrides: Dict of CAMOU_CONFIG keys to override after config
|
||||
is built but before init_script is rendered. Useful for disabling
|
||||
perturbation (e.g. {'fonts:spacing_seed': 0}).
|
||||
is built but before init_script is rendered (e.g. {'audio:seed': 7}).
|
||||
"""
|
||||
if preset is not None:
|
||||
# Use real fingerprint preset
|
||||
@@ -1816,7 +1811,6 @@ def generate_context_fingerprint(
|
||||
_salt = identity_salt()
|
||||
|
||||
# Add seeds (the generator doesn't produce these)
|
||||
config.setdefault('fonts:spacing_seed', 0) # perturbation off; see utils.launch_options
|
||||
config.setdefault('audio:seed', randint(1, 4_294_967_295)) # nosec
|
||||
|
||||
# Determine target OS from platform for font/voice generation
|
||||
@@ -1912,7 +1906,6 @@ def generate_context_fingerprint(
|
||||
|
||||
# Build the values dict for the init script (works for both paths)
|
||||
init_values: Dict[str, Any] = {
|
||||
'fontSpacingSeed': config.get('fonts:spacing_seed'),
|
||||
'audioFingerprintSeed': config.get('audio:seed'),
|
||||
'navigatorPlatform': nav.get('platform'),
|
||||
'navigatorOscpu': config.get('navigator.oscpu'),
|
||||
|
||||
@@ -1267,18 +1267,12 @@ def launch_options(
|
||||
if not _user_set_accept_encoding:
|
||||
config.pop('headers.Accept-Encoding', None)
|
||||
|
||||
# Set random seeds for fingerprint noise (per launch)
|
||||
# Glyph-advance perturbation is OFF by default (seed 0): it moves every
|
||||
# measured text width off the value the same font produces on a real
|
||||
# machine (measured 2026-09-14: +1 px per ~100 glyphs, fractional deltas
|
||||
# on every measureText), which is a fingerprint no stock Firefox emits.
|
||||
# Pass fonts:spacing_seed explicitly to opt back in.
|
||||
set_into(config, 'fonts:spacing_seed', 0)
|
||||
# The audio noise seed follows the identity: a returning "same device" must
|
||||
# reproduce its audio hash (#442/#765). Never 0 (0 disables the noise). A
|
||||
# preset draws its own random seed; it is replaced here too so a pinned
|
||||
# preset reproduces it, but a seed the caller set is kept. There is no
|
||||
# canvas seed: the browser adds no canvas noise (#528).
|
||||
# canvas seed: the browser adds no canvas noise (#528), and no glyph-spacing
|
||||
# noise either (ci/tribal-rules.yml: no-glyph-spacing-noise).
|
||||
if not _user_set_audio_seed:
|
||||
_ident = identity_seed(config, _identity_salt)
|
||||
config['audio:seed'] = ((_ident * 2654435761 + 97) & 0xFFFFFFFF) or 1
|
||||
|
||||
@@ -178,3 +178,19 @@ def test_fingerprint_preset_off_never_draws_a_preset(off):
|
||||
checked with `is not None`, so False drew a random bundled preset."""
|
||||
with mock.patch.object(utils, "get_random_preset", side_effect=AssertionError("preset drawn")):
|
||||
launch(fingerprint_preset=off)
|
||||
|
||||
|
||||
def test_no_glyph_spacing_seed_is_generated():
|
||||
"""Glyph-spacing noise moved every measured text width off what the same
|
||||
font gives on a real machine, so it was itself a fingerprint; the feature
|
||||
is gone from the browser, and the launcher sends nothing for it."""
|
||||
assert "fonts:spacing_seed" not in launch()
|
||||
context = fp.generate_context_fingerprint(os="linux")
|
||||
assert "fonts:spacing_seed" not in context["config"]
|
||||
assert "setFontSpacingSeed" not in context["init_script"]
|
||||
|
||||
|
||||
def test_config_overrides_reach_the_config_and_the_init_script():
|
||||
context = fp.generate_context_fingerprint(os="linux", config_overrides={"audio:seed": 7})
|
||||
assert context["config"]["audio:seed"] == 7
|
||||
assert "setAudioFingerprintSeed(7)" in context["init_script"]
|
||||
|
||||
Reference in New Issue
Block a user