Skip to content

fix(fonts): keep the browser chrome out of the spoofed font list (#695) - #703

Open
pratyush618 wants to merge 4 commits into
daijro:mainfrom
pratyush618:fix/chrome-ui-fonts-695
Open

fix(fonts): keep the browser chrome out of the spoofed font list (#695)#703
pratyush618 wants to merge 4 commits into
daijro:mainfrom
pratyush618:fix/chrome-ui-fonts-695

Conversation

@pratyush618

Copy link
Copy Markdown
Contributor

Fixes #695.

Root cause

Two independent bugs, both "spoofing leaks into the browser's own chrome UI".

1. Tofu titlebar buttons

The boxes in the report are U+E921 / U+E922 / U+E8BB — Firefox's Windows titlebar glyphs:

/* browser/themes/windows/browser.css */
.titlebar-button { font:"Segoe Fluent Icons", "Segoe MDL2 Assets"; }
.titlebar-min::before   { content: "\e921" }
.titlebar-max::before   { content: "\e922" }
.titlebar-close::before { content: "\e8bb" }

font-hijacker.patch fed the spoofed fonts list into Firefox's font.system.whitelist pref. That pref drives gfxPlatformFontList::ApplyWhitelist(), which removes every non-listed family from the font list — and with the shared font list (default on all three platforms) that pruning runs in the parent process, i.e. the process that paints the chrome.

Neither icon font is in any generated mask, and Segoe UI only survives when the spoofed OS is Windows. Linux hosts are insulated by the bundled FONTCONFIG_FILE, which is why this only shows up on Windows and macOS builds.

2. Serif toolbar

font-system-fonts-css2.patch hooked nsLayoutUtils::ComputeSystemFont for every document whenever navigator.platform is spoofed. Chrome CSS is font: message-box, so the browser UI was handed "Segoe UI" on Linux / "-apple-system" on Windows — families that aren't installed → fallback to the default serif font. system-ui-font-spoofing.patch had the same problem for the system-ui generic.

Fix

File Change
additions/camoucfg/MaskConfig.hpp FontAllowlist() / HasFontAllowlist() / IsFontAllowed(), parsed once per process — the gfx paths consult this on every family resolution
patches/font-hijacker.patch Stop writing font.system.whitelist. Filter at lookup time on FontVisibilityProvider::IsChrome(), across all three content-reachable paths
patches/font-system-fonts-css2.patch Chrome docs exempt from the spoof branch
patches/system-ui-font-spoofing.patch Chrome docs exempt

The three content-reachable font paths now covered by the lookup-time filter:

  • FindAndAddFamiliesLockedfont-family / CSS generic resolution, i.e. what document.fonts.check() and text-metric probing go through.
  • GlobalFontFallback — per-character system fallback. The cmap path stays forced (as the whitelist did), so the platform fallback APIs can't pick a family behind our back.
  • gfxUserFontSet src: local() — upstream already refuses local() sources while a whitelist is active; that behaviour is kept for content under the mask, so a page can't probe for hidden families.

font-system-fonts-css2.patch also drops its GTK nsLookAndFeel::PerThemeData::GetFont and cocoa gfxMacPlatformFontList::LookupSystemFont hooks. With the ComputeSystemFont guard in place they are reachable only when the spoof branch is not taken — which is exactly when navigator.platform is unspoofed (where they were no-ops) or when the document is chrome (where they broke the UI).

Behaviour for web content is unchanged

  • Pages still resolve only families from the mask.
  • src: local() probing stays blocked.
  • getComputedStyle().fontFamily for the CSS2 keywords and system-ui returns the same spoofed values (CreepJS attribution unchanged).
  • IsChrome() is Firefox's own notion of privileged (Document::IsInChromeDocShell()), the same predicate FontVisibilityProvider::ComputeFontVisibility() uses for its "chrome gets User level" rule — so about: pages are still treated as content, as upstream does.

Testing

  • The full patch chain (anti-font-fingerprintingfont-hijackerfont-list-spoofingfont-system-fonts-css2system-ui-font-spoofing) applies to pristine Firefox 152.0.4 with zero rejects. Verified in particular that font-hijacker's new FindAndAddFamiliesLocked hunk and font-list-spoofing's per-user-context hunk coexist in that function.
  • The new MaskConfig helpers compile under -std=c++20 -Wall -Wextra and were unit-tested against a real CAMOU_CONFIG payload.
  • build-tester's cssFingerprint.systemFonts and fontAvailability checks run in content, so they are unaffected by the chrome exemption.

I do not have a full build in this environment — the C++ is written and reviewed against the real FF152 sources but has not been compiled in-tree. Happy to iterate if CI turns up anything.

Two adjacent issues found, deliberately left alone

  • Segoe Fluent Icons / Segoe MDL2 Assets are non-essential in _ESSENTIAL_FONTS_WINDOWS (pythonlib/camoufox/fingerprints.py), so a Windows profile randomly claims not to have fonts that ship on every real Windows install — a weak tell. Would have masked this bug too, but it's a separate change.
  • GetSpoofedSystemFontForRFP's fall-through for a Linux spoof is #if defined(XP_WIN) / XP_MACOSX etc., so a Linux fingerprint on a Windows host reports Segoe UI rather than sans-serif.

The gfx font lookup paths need to consult the spoofed "fonts" list on
every family resolution, so it cannot be re-parsed from CAMOU_CONFIG per
call.
font.system.whitelist makes ApplyWhitelist() delete non-listed families
from the process-wide font list, and with the shared font list that
pruning happens in the parent process -- the one that paints the browser
chrome. On Windows the titlebar buttons lost Segoe Fluent Icons and drew
tofu boxes for U+E921/E922/E8BB.

Filter on FontVisibilityProvider::IsChrome() instead, in the three
content-reachable paths: FindAndAddFamiliesLocked, GlobalFontFallback and
gfxUserFontSet's src: local().

Fixes daijro#695
Chrome CSS is `font: message-box`, so the browser UI was handed "Segoe UI"
on Linux and "-apple-system" on Windows -- families that aren't installed,
leaving the toolbar in the default serif font.

The GTK and cocoa LookAndFeel hooks are dropped: with the ComputeSystemFont
guard they are only reachable when navigator.platform is unspoofed (no-op)
or the document is chrome (harmful).

Refs daijro#695
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fonts are missing after upgrade?

1 participant