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
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
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
**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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Parent Epic: #68
Problem
All 12 stealth init scripts in
stealth.gowere written for Chromium but are injected unconditionally into every browser engine, including Firefox. On Firefox:window.chromeproperty injection is meaningless (Firefox doesn't have it natively)HeadlessChromeUA stripping targets a string Firefox never containsnavigator.webdriveroverride works but the approach differs between enginesnavigator.languagesinconsistencies, missingmozInnerScreenX,document.fontsenumeration differences) are completely unaddressedThis 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
Categorize existing scripts into three groups:
navigator.webdriver, permissions API)window.chrome, Chrome plugins,HeadlessChromeUA fixGate injection by browser engine:
Add Firefox-specific stealth scripts:
navigator.languages(match Accept-Language header)navigator.hardwareConcurrency(Firefox headless sometimes reports 2)navigator.platformvaluesdocument.fonts.check()to return typical font availabilitymozInnerScreenX/mozInnerScreenYto non-zero valuesFiles to Modify
stealth.go— split script slices, add Firefox scriptsbrowser_init.go— conditional injection based onopt.Browserstealth_test.go— tests for each script categoryReferences
Starting implementation. Plan:
Script categorization:
navigator.webdriveroverride,outerWidth/outerHeightfix,navigator.permissions.queryoverride,Notificationconstructor stubnavigator.plugins(Chrome PDFs),navigator.mimeTypes,window.chromestub,chrome.app/csi/loadTimes, WebGL spoof (ANGLE strings),navigator.connection, CDP cleanup (cdc_*),HeadlessChromeUA stripnavigator.webdriverhardening (getOwnPropertyDescriptor), WebGL spoof (Mesa/Intel strings),mozInnerScreenX/Yspoof,navigator.hardwareConcurrencynormalization, Firefox PDF.js plugin listFiles to modify:
stealth.go,browser_init.go,stealth_test.goBranch:
fix/69-firefox-stealth-scriptsImplementation complete. PR: #72
What was done:
stealthInitScriptsintostealthCommonScripts(4),stealthChromiumScripts(8), andstealthFirefoxScripts(5 new)browser_init.gonow selects common + engine-specific scripts viaswitch opt.BrowsergetOwnPropertyDescriptorhardening, Mesa/Intel WebGL spoof,mozInnerScreenX/Yfix,hardwareConcurrencynormalization, PDF.js plugin list