diff --git a/README.md b/README.md index cd4199b..0ed705c 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ Camoufox will automatically add the following default fonts associated your spoo **Notes**: - **navigator.webdriver** is set to false at all times. +- `navigator.language` & `navigator.languages` will fall back to the `locale:language`/`locale:region` values if not set. - When spoofing Chrome fingerprints, the following may leak: - navigator.userAgentData missing. - navigator.deviceMemory missing. @@ -264,21 +265,26 @@ Camoufox can override the following network headers:
-Geolocation +Geolocation & Intl -| Property | Status | Description | -| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| geolocation:latitude | ✅ | Latitude to use. Requires `geolocation:longitude` to be set as well. | -| geolocation:longitude | ✅ | Longitude to use. Requires `geolocation:longitude` to be set as well. | -| geolocation:accuracy | ✅ | Accuracy in meters. This will be calculated automatically using the decminal percision of `geolocation:latitude` & `geolocation:longitude` if not set. | -| timezone | ✅ | Set a custom TZ timezone (e.g. "America/Chicago"). This will also change `Date()` to return the local time. | +| Property | Status | Description | Required Keys | +| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | +| geolocation:latitude | ✅ | Latitude to use. | `geolocation:longitude` | +| geolocation:longitude | ✅ | Longitude to use. | `geolocation:latitude` | +| geolocation:accuracy | ✅ | Accuracy in meters. This will be calculated automatically using the decminal percision of `geolocation:latitude` & `geolocation:longitude` if not set. | | +| timezone | ✅ | Set a custom TZ timezone (e.g. "America/Chicago"). This will also change `Date()` to return the local time. | | +| locale:language | ✅ | Spoof the Intl API, headers, and system language (e.g. "en") | `locale:region` | +| locale:region | ✅ | Spoof the Intl API, headers, and system region (e.g. "US"). | `locale:language` | +| locale:script | ✅ | Set a custom script (e.g. "Latn"). Will be set automatically if not specified. | | + +The **Required Keys** are keys that must also be set for the property to work. **Notes:** -- Setting `geolocation:latitude` & `geolocation:longitude` will automatically accept Location permission prompts. +- Location permission prompts will be accepted automatically if `geolocation:latitude` and `geolocation:longitude` are set. - `timezone` **must** be set to a valid TZ identifier. See [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for a list of valid timezones. -- To change your locale, set the `intl.accept_languages` preference. +- `locale:language` & `locale:region` **must** be set to valid locale values. See [here](https://simplelocalize.io/data/locales/) for a list of valid locale-region values.
@@ -406,7 +412,7 @@ Miscellaneous (battery status, etc) - Support for spoofing both inner and outer window viewport sizes - Network headers (Accept-Languages and User-Agent) are spoofed to match the navigator properties - WebRTC IP spoofing at the protocol level -- Geolocation & timezone spoofing +- Geolocation, timezone, and locale spoofing - etc. #### Anti font fingerprinting diff --git a/patches/chromeutil.patch b/patches/chromeutil.patch index 5641f99..b689e49 100644 --- a/patches/chromeutil.patch +++ b/patches/chromeutil.patch @@ -1,5 +1,5 @@ diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp -index 6833d2227f..d3d2ef088d 100644 +index 6833d2227f..0b2ea99615 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -5,6 +5,7 @@ @@ -10,13 +10,12 @@ index 6833d2227f..d3d2ef088d 100644 #include "JSOracleParent.h" #include "js/CallAndConstruct.h" // JS::Call -@@ -2068,6 +2069,25 @@ bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) { +@@ -2068,6 +2069,24 @@ bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) { return nsNativeTheme::IsDarkBackground(f); } +/* static */ -+void ChromeUtils::CamouDebug(GlobalObject& aGlobal, -+ const nsAString& aVarName) { ++void ChromeUtils::CamouDebug(GlobalObject& aGlobal, const nsAString& aVarName) { + if (auto value = MaskConfig::GetBool("debug"); + value.has_value() && !value.value()) { + return; @@ -36,13 +35,13 @@ index 6833d2227f..d3d2ef088d 100644 double ChromeUtils::DateNow(GlobalObject&) { return JS_Now() / 1000.0; } /* static */ -@@ -2094,6 +2114,28 @@ void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal, +@@ -2094,6 +2113,39 @@ void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal, } } +/* static */ +int32_t ChromeUtils::CamouGetInt(GlobalObject& aGlobal, -+ const nsAString& aVarName) { ++ const nsAString& aVarName) { + NS_ConvertUTF16toUTF8 utf8VarName(aVarName); + if (auto value = MaskConfig::GetInt32(utf8VarName.get())) { + return value.value(); @@ -52,8 +51,8 @@ index 6833d2227f..d3d2ef088d 100644 + +/* static */ +double ChromeUtils::CamouGetDouble(GlobalObject& aGlobal, -+ const nsAString& aVarName, -+ double aDefaultValue) { ++ const nsAString& aVarName, ++ double aDefaultValue) { + NS_ConvertUTF16toUTF8 utf8VarName(aVarName); + if (auto value = MaskConfig::GetDouble(utf8VarName.get())) { + return value.value(); @@ -61,12 +60,23 @@ index 6833d2227f..d3d2ef088d 100644 + return aDefaultValue; +} + ++/* static */ ++void ChromeUtils::CamouGetString(GlobalObject& aGlobal, ++ const nsAString& aVarName, ++ nsAString& aRetVal) { ++ NS_ConvertUTF16toUTF8 utf8VarName(aVarName); ++ if (auto value = MaskConfig::GetString(utf8VarName.get())) { ++ aRetVal.Assign(NS_ConvertUTF8toUTF16(value.value())); ++ } else { ++ aRetVal.Truncate(); ++ } ++} + /* static */ bool ChromeUtils::ShouldResistFingerprinting( GlobalObject& aGlobal, JSRFPTarget aTarget, diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h -index 0150c59670..d297ab5788 100644 +index 0150c59670..e3d203ed1a 100644 --- a/dom/base/ChromeUtils.h +++ b/dom/base/ChromeUtils.h @@ -301,6 +301,10 @@ class ChromeUtils { @@ -80,7 +90,7 @@ index 0150c59670..d297ab5788 100644 static double DateNow(GlobalObject&); static void EnsureJSOracleStarted(GlobalObject&); -@@ -310,6 +314,11 @@ class ChromeUtils { +@@ -310,6 +314,14 @@ class ChromeUtils { static void GetAllPossibleUtilityActorNames(GlobalObject& aGlobal, nsTArray& aNames); @@ -88,12 +98,15 @@ index 0150c59670..d297ab5788 100644 + + static double CamouGetDouble(GlobalObject& aGlobal, const nsAString& aVarName, + double aDefaultValue); ++ ++ static void CamouGetString(GlobalObject& aGlobal, const nsAString& aVarName, ++ nsAString& aRetVal); + static bool ShouldResistFingerprinting( GlobalObject& aGlobal, JSRFPTarget aTarget, const Nullable& aOverriddenFingerprintingSettings); diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl -index bf196f039d..994b5803df 100644 +index bf196f039d..04e0cdcabd 100644 --- a/dom/chrome-webidl/ChromeUtils.webidl +++ b/dom/chrome-webidl/ChromeUtils.webidl @@ -746,6 +746,13 @@ partial namespace ChromeUtils { @@ -110,7 +123,7 @@ index bf196f039d..994b5803df 100644 /** * Starts the JSOracle process for ORB JavaScript validation, if it hasn't started already. */ -@@ -757,6 +764,16 @@ partial namespace ChromeUtils { +@@ -757,6 +764,21 @@ partial namespace ChromeUtils { [ChromeOnly] readonly attribute unsigned long aliveUtilityProcesses; @@ -123,6 +136,11 @@ index bf196f039d..994b5803df 100644 + * Get a double value from Camoufox MaskConfig. + */ + double camouGetDouble(DOMString varName, double defaultValue); ++ ++ /** ++ * Get a string value from Camoufox MaskConfig. ++ */ ++ DOMString camouGetString(DOMString varName); + /** * Get a list of all possible Utility process Actor Names ; mostly useful to diff --git a/patches/locale-spoofing.patch b/patches/locale-spoofing.patch new file mode 100644 index 0000000..02523e9 --- /dev/null +++ b/patches/locale-spoofing.patch @@ -0,0 +1,186 @@ +diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js +index 16f4dd7d42..63c9c39741 100644 +--- a/browser/base/content/browser-init.js ++++ b/browser/base/content/browser-init.js +@@ -109,6 +109,17 @@ var gBrowserInit = { + window.TabBarVisibility.update(); + TabsInTitlebar.init(); + ++ let camouLocale; ++ let language = ChromeUtils.camouGetString("locale:language"); ++ let region = ChromeUtils.camouGetString("locale:region"); ++ if (language && region) { ++ camouLocale = language + "-" + region + ", " + language; ++ } else { ++ camouLocale = ChromeUtils.camouGetString("navigator.language"); ++ } ++ if (camouLocale) ++ Services.prefs.setCharPref("intl.accept_languages", camouLocale); ++ + new LightweightThemeConsumer(document); + + if ( +diff --git a/intl/components/src/Locale.cpp b/intl/components/src/Locale.cpp +index 9a043518cf..a8d3733212 100644 +--- a/intl/components/src/Locale.cpp ++++ b/intl/components/src/Locale.cpp +@@ -24,11 +24,56 @@ + + #include "unicode/uloc.h" + #include "unicode/utypes.h" ++#include "MaskConfig.hpp" + + namespace mozilla::intl { + + using namespace intl::LanguageTagLimits; + ++ ++// Helper methods for header file ++ ++const char* Locale::GetCamouLanguage() { ++ if (auto lang = MaskConfig::GetString("locale:language")) ++ return lang.value().c_str(); ++ return nullptr; ++} ++ ++const char* Locale::GetCamouRegion() { ++ if (auto region = MaskConfig::GetString("locale:region")) ++ return region.value().c_str(); ++ return nullptr; ++} ++ ++const char* Locale::GetCamouScript() { ++ if (auto script = MaskConfig::GetString("locale:script")) ++ return script.value().c_str(); ++ return nullptr; ++} ++ ++// Publicly exposed methods ++ ++const char* Locale::GetCamouLocale() { ++ static std::string locale; ++ ++ if (auto lang = MaskConfig::GetString("locale:language")) ++ if (auto region = MaskConfig::GetString("locale:region")) ++ locale = (lang.value() + "-" + region.value()); ++ if (auto value = MaskConfig::GetString("navigator.language")) ++ locale = value.value(); ++ if (locale.empty()) ++ return nullptr; ++ return locale.c_str(); ++} ++ ++const char* Locale::GetDefaultLocale() { ++ // In Camoufox, GetDefaultLocale is defined outside the header ++ // to access MaskConfig. ++ if (auto value = GetCamouLocale()) ++ return value; ++ return uloc_getDefault(); ++} ++ + template + bool IsStructurallyValidLanguageTag(Span aLanguage) { + // unicode_language_subtag = alpha{2,3} | alpha{5,8}; +diff --git a/intl/components/src/Locale.h b/intl/components/src/Locale.h +index 1f4e06f543..448486a479 100644 +--- a/intl/components/src/Locale.h ++++ b/intl/components/src/Locale.h +@@ -190,8 +190,11 @@ using UniqueChars = UniquePtr; + */ + class MOZ_STACK_CLASS Locale final { + LanguageSubtag mLanguage = {}; ++ mutable LanguageSubtag mCamouLanguage = {}; + ScriptSubtag mScript = {}; ++ mutable ScriptSubtag mCamouScript = {}; + RegionSubtag mRegion = {}; ++ mutable RegionSubtag mCamouRegion = {}; + + using VariantsVector = Vector; + using ExtensionsVector = Vector; +@@ -314,9 +317,28 @@ class MOZ_STACK_CLASS Locale final { + } + }; + +- const LanguageSubtag& Language() const { return mLanguage; } +- const ScriptSubtag& Script() const { return mScript; } +- const RegionSubtag& Region() const { return mRegion; } ++ const LanguageSubtag& Language() const { ++ if (const char* lang = GetCamouLanguage()) { ++ mCamouLanguage.Set(mozilla::Span(lang, strlen(lang))); ++ return mCamouLanguage; ++ } ++ return mLanguage; ++ } ++ const ScriptSubtag& Script() const { ++ if (const char* script = GetCamouScript()) { ++ mCamouScript.Set(mozilla::Span(script, strlen(script))); ++ return mCamouScript; ++ } ++ return mScript; ++ } ++ const RegionSubtag& Region() const { ++ if (const char* region = GetCamouRegion()) { ++ mCamouRegion.Set(mozilla::Span(region, strlen(region))); ++ return mCamouRegion; ++ } ++ return mRegion; ++ } ++ + auto Variants() const { return SubtagEnumeration(mVariants); } + auto Extensions() const { return SubtagEnumeration(mExtensions); } + Maybe> PrivateUse() const { +@@ -483,7 +505,15 @@ class MOZ_STACK_CLASS Locale final { + * + * Also see . + */ +- static const char* GetDefaultLocale() { return uloc_getDefault(); } ++ static const char* GetDefaultLocale(); ++ ++ static const char* GetCamouLocale(); ++ ++ static const char* GetCamouLanguage(); ++ ++ static const char* GetCamouRegion(); ++ ++ static const char* GetCamouScript(); + + /** + * Returns an iterator over all supported locales. +diff --git a/intl/locale/OSPreferences.cpp b/intl/locale/OSPreferences.cpp +index b87924b61a..2eaa960c04 100644 +--- a/intl/locale/OSPreferences.cpp ++++ b/intl/locale/OSPreferences.cpp +@@ -406,6 +406,10 @@ bool OSPreferences::GetDateTimeConnectorPattern(const nsACString& aLocale, + */ + NS_IMETHODIMP + OSPreferences::GetSystemLocales(nsTArray& aRetVal) { ++ if (const char* camouLocale = mozilla::intl::Locale::GetCamouLocale()) { ++ aRetVal.AppendElement(camouLocale); ++ return NS_OK; ++ } + if (!mSystemLocales.IsEmpty()) { + aRetVal = mSystemLocales.Clone(); + return NS_OK; +@@ -425,7 +429,10 @@ OSPreferences::GetSystemLocales(nsTArray& aRetVal) { + + NS_IMETHODIMP + OSPreferences::GetSystemLocale(nsACString& aRetVal) { +- if (!mSystemLocales.IsEmpty()) { ++ if (const char* camouLocale = mozilla::intl::Locale::GetCamouLocale()) { ++ aRetVal = camouLocale; ++ return NS_OK; ++ } else if (!mSystemLocales.IsEmpty()) { + aRetVal = mSystemLocales[0]; + } else { + AutoTArray locales; +@@ -439,6 +446,10 @@ OSPreferences::GetSystemLocale(nsACString& aRetVal) { + + NS_IMETHODIMP + OSPreferences::GetRegionalPrefsLocales(nsTArray& aRetVal) { ++ if (const char* camouLocale = mozilla::intl::Locale::GetCamouLocale()) { ++ aRetVal.AppendElement(camouLocale); ++ return NS_OK; ++ } + if (!mRegionalPrefsLocales.IsEmpty()) { + aRetVal = mRegionalPrefsLocales.Clone(); + return NS_OK; diff --git a/settings/properties.json b/settings/properties.json index 9b0be58..3bfefe7 100644 --- a/settings/properties.json +++ b/settings/properties.json @@ -58,5 +58,8 @@ { "property": "geolocation:latitude", "type": "double" }, { "property": "geolocation:longitude", "type": "double" }, { "property": "geolocation:accuracy", "type": "double" }, - { "property": "timezone", "type": "str" } + { "property": "timezone", "type": "str" }, + { "property": "locale:language", "type": "str" }, + { "property": "locale:region", "type": "str" }, + { "property": "locale:script", "type": "str" } ] diff --git a/upstream.sh b/upstream.sh index 3140952..d7683f2 100644 --- a/upstream.sh +++ b/upstream.sh @@ -1,2 +1,2 @@ version=130.0.1 -release=beta.7 +release=beta.8