Make navigator.geolocation coordinates configurable (#272)
The geolocation shim returned a fixed Frankfurt position. With the timezone now configurable, a deployment in another region would report a location that does not match its exit IP or timezone, the mismatch issue #228 describes. Set OBSCURA_GEOLOCATION="lat,lon" to override the reported coordinates; the value is range-checked and falls back to the Frankfurt default when unset or malformed. The existing positional jitter is preserved.
This commit is contained in:
1 parent
a23dad7cd9
commit
82b7a0baa8
3 files changed
+37
-4
No files matched your search
@@ -9,6 +9,22 @@ use url::Url;
|
|||||||
use crate::context::BrowserContext;
|
use crate::context::BrowserContext;
|
||||||
use crate::lifecycle::LifecycleState;
|
use crate::lifecycle::LifecycleState;
|
||||||
|
|
||||||
|
/// Parse `OBSCURA_GEOLOCATION="lat,lon"` for the navigator.geolocation shim.
|
||||||
|
/// Returns None when unset or malformed, leaving the built-in default in place.
|
||||||
|
/// Lets a deployment align the reported coordinates with the region its exit IP
|
||||||
|
/// resolves to, so timezone and location stay consistent (issue #228).
|
||||||
|
fn env_geolocation() -> Option<(f64, f64)> {
|
||||||
|
let raw = std::env::var("OBSCURA_GEOLOCATION").ok()?;
|
||||||
|
let (lat, lon) = raw.split_once(',')?;
|
||||||
|
let lat: f64 = lat.trim().parse().ok()?;
|
||||||
|
let lon: f64 = lon.trim().parse().ok()?;
|
||||||
|
let valid = lat.is_finite()
|
||||||
|
&& lon.is_finite()
|
||||||
|
&& (-90.0..=90.0).contains(&lat)
|
||||||
|
&& (-180.0..=180.0).contains(&lon);
|
||||||
|
valid.then_some((lat, lon))
|
||||||
|
}
|
||||||
|
|
||||||
fn decode_data_uri(uri: &str) -> Option<Vec<u8>> {
|
fn decode_data_uri(uri: &str) -> Option<Vec<u8>> {
|
||||||
let rest = uri.strip_prefix("data:")?;
|
let rest = uri.strip_prefix("data:")?;
|
||||||
let comma = rest.find(',')?;
|
let comma = rest.find(',')?;
|
||||||
@@ -272,6 +288,9 @@ impl Page {
|
|||||||
&self.context.ua_platform,
|
&self.context.ua_platform,
|
||||||
&self.context.ua_platform_version,
|
&self.context.ua_platform_version,
|
||||||
);
|
);
|
||||||
|
if let Some((lat, lon)) = env_geolocation() {
|
||||||
|
rt.set_geolocation(lat, lon);
|
||||||
|
}
|
||||||
|
|
||||||
rt.set_cookie_jar(self.context.cookie_jar.clone());
|
rt.set_cookie_jar(self.context.cookie_jar.clone());
|
||||||
rt.set_http_client(self.http_client.clone());
|
rt.set_http_client(self.http_client.clone());
|
||||||
|
|||||||
Vendored
+4
-4
@@ -2447,8 +2447,8 @@ globalThis.navigator = {
|
|||||||
geolocation: {
|
geolocation: {
|
||||||
getCurrentPosition(success, error) {
|
getCurrentPosition(success, error) {
|
||||||
const coords = {
|
const coords = {
|
||||||
latitude: 50.1109 + (_fpRand(500) - 0.5) * 0.1,
|
latitude: (globalThis.__obscura_geo_lat ?? 50.1109) + (_fpRand(500) - 0.5) * 0.1,
|
||||||
longitude: 8.6821 + (_fpRand(501) - 0.5) * 0.1,
|
longitude: (globalThis.__obscura_geo_lon ?? 8.6821) + (_fpRand(501) - 0.5) * 0.1,
|
||||||
accuracy: 10 + _fpRand(502) * 40,
|
accuracy: 10 + _fpRand(502) * 40,
|
||||||
altitude: null,
|
altitude: null,
|
||||||
altitudeAccuracy: null,
|
altitudeAccuracy: null,
|
||||||
@@ -2461,8 +2461,8 @@ globalThis.navigator = {
|
|||||||
watchPosition(success, error) {
|
watchPosition(success, error) {
|
||||||
if (typeof success === 'function') {
|
if (typeof success === 'function') {
|
||||||
const coords = {
|
const coords = {
|
||||||
latitude: 50.1109 + (_fpRand(503) - 0.5) * 0.1,
|
latitude: (globalThis.__obscura_geo_lat ?? 50.1109) + (_fpRand(503) - 0.5) * 0.1,
|
||||||
longitude: 8.6821 + (_fpRand(504) - 0.5) * 0.1,
|
longitude: (globalThis.__obscura_geo_lon ?? 8.6821) + (_fpRand(504) - 0.5) * 0.1,
|
||||||
accuracy: 10 + _fpRand(505) * 40,
|
accuracy: 10 + _fpRand(505) * 40,
|
||||||
altitude: null,
|
altitude: null,
|
||||||
altitudeAccuracy: null,
|
altitudeAccuracy: null,
|
||||||
|
|||||||
@@ -219,6 +219,20 @@ impl ObscuraJsRuntime {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Override the coordinates the navigator.geolocation shim reports. The
|
||||||
|
/// values are injected as numeric globals the bootstrap reads; when unset it
|
||||||
|
/// keeps the built-in default. Callers validate the range before calling.
|
||||||
|
pub fn set_geolocation(&mut self, latitude: f64, longitude: f64) {
|
||||||
|
let _ = self.runtime.execute_script(
|
||||||
|
"<set-geo>",
|
||||||
|
format!(
|
||||||
|
"globalThis.__obscura_geo_lat={};globalThis.__obscura_geo_lon={};",
|
||||||
|
latitude, longitude
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn evaluate(&mut self, expression: &str) -> Result<serde_json::Value, String> {
|
pub fn evaluate(&mut self, expression: &str) -> Result<serde_json::Value, String> {
|
||||||
let wrapped = Self::wrap_expression(expression);
|
let wrapped = Self::wrap_expression(expression);
|
||||||
let result = self
|
let result = self
|
||||||
|
|||||||
Reference in new issue
Block a user