Skip to content

feat(plugins): add stealth plugin (WebRTC block, locale/TZ/geo overrides) - #3109

Open
dawsman wants to merge 1 commit into
jo-inc:masterfrom
dawsman:feat/stealth-plugin
Open

feat(plugins): add stealth plugin (WebRTC block, locale/TZ/geo overrides)#3109
dawsman wants to merge 1 commit into
jo-inc:masterfrom
dawsman:feat/stealth-plugin

Conversation

@dawsman

@dawsman dawsman commented May 13, 2026

Copy link
Copy Markdown
Contributor

What

Adds a new plugins/stealth/ that closes the two most common fingerprint leaks visible on creepjs / sannysoft / browserleaks when running without a residential proxy:

  1. WebRTC IP leak — disables media.peerconnection.enabled plus tightens ICE so even if a downstream consumer flips WebRTC back on, host candidates can't reveal the host IP.
  2. Hardcoded LA / en-US / SF context defaultssession:creating is already where these live in server.js (~line 781); the plugin lets you point locale / timezone / geolocation anywhere via config or env.

Both knobs are no-ops when their respective config keys are unset, so enabling the plugin with empty config gets you a WebRTC-blocked browser and nothing else changes.

Default is "enabled": false in camofox.config.json — opt-in, no behavior change on upgrade.

Verified

CreepJS headless score on a default camofox instance drops from 13% → 0% after enabling stealth + a UK config. WebRTC host / srflx candidates become empty (blocked) on browserleaks/webrtc. Timezone and locale on creepjs match the configured values.

Tests

plugins/stealth/plugin.test.js — 9 tests covering:

  • WebRTC block on/off, env override
  • Accept-Language set / passthrough
  • locale / timezone / geolocation override
  • env precedence over plugin config
  • passthrough when nothing configured
  • partial config (one knob set, others passthrough)

All 35 plugin tests across plugins/** pass.

Limitation (called out in README + AGENTS)

The browser:launching hook fires after camoufox-js's launchOptions() has already baked the os choice into Playwright launch args, so this plugin can't change navigator.platform, the UA family, or WebGL renderer — those depend on core picking a different os value upstream of this hook. Documenting so a future contributor doesn't waste time trying.

🤖 Generated with Claude Code

…des)

Closes the two most common fingerprint leaks visible on creepjs /
sannysoft / browserleaks when running without a residential proxy:

1. WebRTC IP leak — disables media.peerconnection.enabled plus tightens
   ICE so even if a downstream consumer flips WebRTC back on, host
   candidates can't reveal the host IP.
2. The hardcoded LA/en-US/SF context defaults (server.js ~line 781) —
   the plugin lets you point locale / timezoneId / geolocation anywhere
   via plugin config or env vars (CAMOFOX_STEALTH_LOCALE,
   CAMOFOX_STEALTH_TZ, CAMOFOX_STEALTH_GEO_LAT/LON,
   CAMOFOX_STEALTH_BLOCK_WEBRTC).

Both knobs are no-ops when their respective config keys are unset, so
enabling stealth with empty config gets you a WebRTC-blocked browser
and nothing else changes.

Default is `"enabled": false` in camofox.config.json — opt-in, no
behavior change for existing users on upgrade.

CreepJS headless score on a default install drops 13% -> 0% after
enabling stealth + a UK config. WebRTC host/srflx candidates become
empty on browserleaks/webrtc. Timezone/locale on creepjs match the
configured values.

Limitation: the browser:launching hook fires after camoufox-js's
launchOptions() has already baked the `os` choice into Playwright
launch args, so this plugin can't change navigator.platform, the UA
family, or WebGL renderer — those depend on core picking a different
`os` value upstream of this hook.

Tests: plugins/stealth/plugin.test.js — 9 tests covering WebRTC on/off,
env-over-config precedence, partial config, and passthrough. All 35
plugin tests across plugins/** still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@skyfallsin

Copy link
Copy Markdown
Contributor

Thanks for this — we reviewed it again during the v1.11.0 pass but didn’t include it as-is.

The feature is interesting, especially the locale/TZ/geo consistency piece. The part we’re still thinking through is the API shape and fingerprinting tradeoff around WebRTC: a global “block WebRTC” mode can itself become a fingerprint, so we’re leaning toward stealth behavior being explicit per session/request when the plugin is enabled rather than a server-wide on/off switch.

Do you have practical experience with which of these settings help vs hurt in real anti-bot environments? In particular:

  • is WebRTC blocking actually a net positive in your use cases, or do you prefer IP/locale/TZ consistency without blocking?
  • should this be configured once per server, once per session, or per tab/request?
  • are there sites where WebRTC blocking clearly made detection worse?

If you have field experience here, would love your take before we settle the design.

@LckySndays

Copy link
Copy Markdown

I have been looking to find a way to change the timezone, any update or works in progress for this PR?

@skyfallsin

Copy link
Copy Markdown
Contributor

Thanks for the stealth-plugin implementation and the verification work. The feature can be useful for no-proxy, fixed-location deployments, but this version needs a rebase and a safer configuration design.

Please address these points:

  • do not apply a fixed locale, timezone, and geolocation when a proxy is configured unless they are explicitly aligned with that proxy; proxy-IP and browser-location mismatches are detectable;
  • move configuration reads into the centralized lib/config.js path and forward supported values consistently;
  • validate locale, IANA timezone, and latitude/longitude bounds before creating a browser context;
  • define an approved session/request-scoped API rather than applying one global location profile to every user;
  • document the WebRTC fingerprint tradeoff and add regression coverage for proxy mode and invalid configuration;
  • rebase onto current master and resolve the config conflict.

Please keep the plugin opt-in and preserve the existing proxy-derived fingerprint behavior by default.

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.

3 participants