Add Firefox-specific stealth and split browser-conditional init scripts #69

Closed
opened 2026-02-24 01:13:21 +00:00 by Claude · 2 comments
Collaborator

Parent Epic: #68

Problem

All 12 stealth init scripts in stealth.go were written for Chromium but are injected unconditionally into every browser engine, including Firefox. On Firefox:

  • window.chrome property injection is meaningless (Firefox doesn't have it natively)
  • Chrome PDF plugin spoofing adds suspicious plugins that Firefox shouldn't have
  • HeadlessChrome UA stripping targets a string Firefox never contains
  • navigator.webdriver override works but the approach differs between engines
  • Firefox-specific headless detection vectors (e.g. navigator.languages inconsistencies, missing mozInnerScreenX, document.fonts enumeration differences) are completely unaddressed

This means Firefox sessions are more fingerprintable than no stealth at all — the Chromium-specific spoofing makes the browser look like it's trying to fake being Chrome while running Firefox.

Proposed Solution

  1. Categorize existing scripts into three groups:

    • Common — works on all engines (e.g. navigator.webdriver, permissions API)
    • Chromium-onlywindow.chrome, Chrome plugins, HeadlessChrome UA fix
    • Firefox-only — new scripts for Firefox-specific vectors
  2. Gate injection by browser engine:

    scripts := stealthCommonScripts
    if opt.Browser == BrowserChromium {
        scripts = append(scripts, stealthChromiumScripts...)
    } else if opt.Browser == BrowserFirefox {
        scripts = append(scripts, stealthFirefoxScripts...)
    }
    
  3. Add Firefox-specific stealth scripts:

    • Consistent navigator.languages (match Accept-Language header)
    • Consistent navigator.hardwareConcurrency (Firefox headless sometimes reports 2)
    • Consistent navigator.platform values
    • Override document.fonts.check() to return typical font availability
    • Spoof mozInnerScreenX/mozInnerScreenY to non-zero values

Files to Modify

  • stealth.go — split script slices, add Firefox scripts
  • browser_init.go — conditional injection based on opt.Browser
  • stealth_test.go — tests for each script category

References

  • #58, #59 — original stealth implementation
  • #68 — parent epic
**Parent Epic:** #68 ## Problem All 12 stealth init scripts in `stealth.go` were written for Chromium but are injected unconditionally into every browser engine, including Firefox. On Firefox: - `window.chrome` property injection is meaningless (Firefox doesn't have it natively) - Chrome PDF plugin spoofing adds suspicious plugins that Firefox shouldn't have - `HeadlessChrome` UA stripping targets a string Firefox never contains - `navigator.webdriver` override works but the approach differs between engines - Firefox-specific headless detection vectors (e.g. `navigator.languages` inconsistencies, missing `mozInnerScreenX`, `document.fonts` enumeration differences) are completely unaddressed This means Firefox sessions are **more** fingerprintable than no stealth at all — the Chromium-specific spoofing makes the browser look like it's trying to fake being Chrome while running Firefox. ## Proposed Solution 1. **Categorize existing scripts** into three groups: - **Common** — works on all engines (e.g. `navigator.webdriver`, permissions API) - **Chromium-only** — `window.chrome`, Chrome plugins, `HeadlessChrome` UA fix - **Firefox-only** — new scripts for Firefox-specific vectors 2. **Gate injection by browser engine:** ```go scripts := stealthCommonScripts if opt.Browser == BrowserChromium { scripts = append(scripts, stealthChromiumScripts...) } else if opt.Browser == BrowserFirefox { scripts = append(scripts, stealthFirefoxScripts...) } ``` 3. **Add Firefox-specific stealth scripts:** - Consistent `navigator.languages` (match Accept-Language header) - Consistent `navigator.hardwareConcurrency` (Firefox headless sometimes reports 2) - Consistent `navigator.platform` values - Override `document.fonts.check()` to return typical font availability - Spoof `mozInnerScreenX`/`mozInnerScreenY` to non-zero values ## Files to Modify - `stealth.go` — split script slices, add Firefox scripts - `browser_init.go` — conditional injection based on `opt.Browser` - `stealth_test.go` — tests for each script category ## References - #58, #59 — original stealth implementation - #68 — parent epic
Claude added the enhancementpriority/hightype/task labels 2026-02-24 01:13:48 +00:00
Author
Collaborator

Starting implementation. Plan:

Script categorization:

  • Common (all engines, 4 scripts): navigator.webdriver override, outerWidth/outerHeight fix, navigator.permissions.query override, Notification constructor stub
  • Chromium-only (8 scripts): navigator.plugins (Chrome PDFs), navigator.mimeTypes, window.chrome stub, chrome.app/csi/loadTimes, WebGL spoof (ANGLE strings), navigator.connection, CDP cleanup (cdc_*), HeadlessChrome UA strip
  • Firefox-only (5 new scripts): Firefox navigator.webdriver hardening (getOwnPropertyDescriptor), WebGL spoof (Mesa/Intel strings), mozInnerScreenX/Y spoof, navigator.hardwareConcurrency normalization, Firefox PDF.js plugin list

Files to modify: stealth.go, browser_init.go, stealth_test.go

Branch: fix/69-firefox-stealth-scripts

Starting implementation. Plan: **Script categorization:** - **Common (all engines, 4 scripts):** `navigator.webdriver` override, `outerWidth/outerHeight` fix, `navigator.permissions.query` override, `Notification` constructor stub - **Chromium-only (8 scripts):** `navigator.plugins` (Chrome PDFs), `navigator.mimeTypes`, `window.chrome` stub, `chrome.app/csi/loadTimes`, WebGL spoof (ANGLE strings), `navigator.connection`, CDP cleanup (`cdc_*`), `HeadlessChrome` UA strip - **Firefox-only (5 new scripts):** Firefox `navigator.webdriver` hardening (`getOwnPropertyDescriptor`), WebGL spoof (Mesa/Intel strings), `mozInnerScreenX/Y` spoof, `navigator.hardwareConcurrency` normalization, Firefox PDF.js plugin list **Files to modify:** `stealth.go`, `browser_init.go`, `stealth_test.go` Branch: `fix/69-firefox-stealth-scripts`
Author
Collaborator

Implementation complete. PR: #72

What was done:

  • Split stealthInitScripts into stealthCommonScripts (4), stealthChromiumScripts (8), and stealthFirefoxScripts (5 new)
  • browser_init.go now selects common + engine-specific scripts via switch opt.Browser
  • Added 5 Firefox stealth scripts: webdriver getOwnPropertyDescriptor hardening, Mesa/Intel WebGL spoof, mozInnerScreenX/Y fix, hardwareConcurrency normalization, PDF.js plugin list
  • Tests cover per-category counts, content checks, no-overlap, and cross-contamination guards
  • All builds, tests, and vet pass cleanly
Implementation complete. PR: #72 **What was done:** - Split `stealthInitScripts` into `stealthCommonScripts` (4), `stealthChromiumScripts` (8), and `stealthFirefoxScripts` (5 new) - `browser_init.go` now selects common + engine-specific scripts via `switch opt.Browser` - Added 5 Firefox stealth scripts: webdriver `getOwnPropertyDescriptor` hardening, Mesa/Intel WebGL spoof, `mozInnerScreenX/Y` fix, `hardwareConcurrency` normalization, PDF.js plugin list - Tests cover per-category counts, content checks, no-overlap, and cross-contamination guards - All builds, tests, and vet pass cleanly
steve closed this issue 2026-02-24 01:23:25 +00:00
Sign in to join this conversation.