mirror of
https://github.com/daijro/camoufox.git
synced 2026-09-09 00:00:39 +00:00
Firefox registers the host's speech-dispatcher / SAPI / NSSpeech voices
unless something stops it, and nsSynthVoiceRegistry only stopped it when the
explicit `voices:blockIfNotDefined` flag was set. Nothing set that flag, so
the host was suppressed only as a side effect of a non-empty spoofed list --
and the Python layer built that list inside a bare `except Exception: pass`.
Any path that left the list empty or unset therefore fell through to the host
backend. On a stock Linux box that exposes 14805 espeak-ng voices to the page
under a fingerprint claiming macOS or Windows, which both leaks the real host
OS and contradicts the rest of the profile. Reproduced on 152.0.4-beta.29:
config voices exposed
generation raises 14805 (all host speechd)
{"voices": []} 14805 (all host speechd)
valid list 115 (correct)
Three changes, so the failure is closed at both layers:
* nsSynthVoiceRegistry::AddVoice now also blocks when MaskConfig carries a
`voices` array at all -- including an empty one, or one whose entries were
all rejected as malformed. An empty spoofed list must mean "no voices",
never "all of the host's". With no `voices` key the browser still behaves
like stock Firefox, so a bare binary is unaffected.
* launch_options pins `voices:blockIfNotDefined` (via set_into, so an
explicit caller value still wins) and degrades a generation failure to an
empty list rather than leaving the key unset. It also passes the spoofed
navigator.language through, so the default voice matches the locale.
* validate_voices rejects the shapes MaskConfig::MVoices() silently drops --
bare "Name:lang:type" strings and half-filled objects -- before launch
instead of letting them degrade into a host-voice leak.
Both failure paths now expose 0 voices; the normal path still exposes 115.