summaryrefslogtreecommitdiff
path: root/data/extensions/jsr@javascriptrestrictor/wrappingS-GEO.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/jsr@javascriptrestrictor/wrappingS-GEO.js')
-rw-r--r--data/extensions/jsr@javascriptrestrictor/wrappingS-GEO.js383
1 files changed, 0 insertions, 383 deletions
diff --git a/data/extensions/jsr@javascriptrestrictor/wrappingS-GEO.js b/data/extensions/jsr@javascriptrestrictor/wrappingS-GEO.js
deleted file mode 100644
index a23ba47..0000000
--- a/data/extensions/jsr@javascriptrestrictor/wrappingS-GEO.js
+++ /dev/null
@@ -1,383 +0,0 @@
-/** \file
- * \brief This file contains wrappers for the Geolocation API
- *
- * \see https://www.w3.org/TR/geolocation-API/
- *
- * \author Copyright (C) 2019 Martin Timko
- * \author Copyright (C) 2020 Libor Polcak
- * \author Copyright (C) 2020 Peter Marko
- * \author Copyright (C) 2021 Giorgio Maone
- *
- * \license SPDX-License-Identifier: GPL-3.0-or-later
- */
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-//
-
-/** \file
- * \ingroup wrappers
- *
- * The goal is to prevent leaks of user current position. The Geolocation API also provides access
- * to high precision timestamps which can be used to various web attacks (see for example,
- * http://www.jucs.org/jucs_21_9/clock_skew_based_computer,
- * https://lirias.kuleuven.be/retrieve/389086).
- *
- * Although it is true that the user needs to specificaly approve access to location facilities,
- * these wrappers aim on improving the control of the precision of the geolocation.
- *
- * The wrappers support the following controls:
- *
- * * Accurate data: the extension provides precise geolocation position but modifies the time
- * precision in conformance with the Date and Performance wrappers.
- * * Modified position: the extension modifies the time precision of the time stamps in
- * conformance with the Date and Performance wrappers, and additionally, allows to limit the
- * precision of the current position to hundered of meters, kilometers, tens, or hundereds of
- * kilometers.
- *
- * When modifying position:
- *
- * * Repeated calls of `navigator.geolocation.getCurrentPosition()` return the same position
- * without page load and typically return another position after page reload.
- * * `navigator.geolocation.watchPosition()` does not change position.
- */
-
-(function() {
- var processOriginalGPSDataObject_globals = `
- let geo_prng = alea(domainHash, "Geolocation");
- let randomx = geo_prng();
- let randomy = geo_prng();
- /**
- * Make sure that repeated calls shows the same position (BUT different objects, via cloning)
- * to reduce fingerprintablity.
- */
- let previouslyReturnedCoords;
- let clone = obj => Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
-
- /**
- * \brief Store the limit for the returned timestamps.
- *
- * This is used to avoid returning older readings compared to the previous readings.
- * The timestamp needs to be the same as was last time or newser.
- */
- var geoTimestamp = Date.now();
- `;
- /**
- * \brief Modifies the given PositionObject according to settings
- *
- * \param expectedMaxAge The maximal age of the returned time stamps as defined by the wrapped API
- * (https://www.w3.org/TR/geolocation-API/#max-age)
- * \param originalPositionObject the position object to be returned without this wrapper, see the
- * Position interface (https://www.w3.org/TR/geolocation-API/#position)
- *
- * The function modifies the originalPositionObject and stores it for later readins. The returned
- * position does not modify during the life time of a pages. The returned postion can be different
- * after a page reload.
- *
- * The goal of the behavoiur is to prevent learning the current position so that different
- * original postion can be mapped to the same position and the same position should generally
- * yield a different outcome to prevent correlation of user activities.
- *
- * The algorithm works as follows:
- * 1. The Earth surface is partitioned into squares (tiles) with the edge derived from the desired
- * accuracy.
- * 2. The position from the originalPositionObject is mapped to its tile and eight adjacent tiles.
- * 3. A position in the current tile and the eight adjacent tiles is selected randomly.
- *
- * The returned timestamp is not older than 1 hour and it is the same as was during the last call
- * or newer. Different calls to the function can yield different timestamps.
- *
- * \bug The tile-based approach does not work correctly near poles but:
- * * The function returns fake locations near poles.
- * * As there are not many people near poles, we do not believe this wrapping is useful near poles
- * so we do not consider this bug as important.
- */
-
- function spoofCall(fakeData, originalPositionObject, successCallback) {
- // proxying the original object lessens the fingerprintable weirdness
- // (e.g. accessors on the instance rather than on the prototype)
- fakeData = clone(fakeData);
- let pos = new Proxy(originalPositionObject, {
- get(target, key) {
- return (key in fakeData) ? fakeData[key] : target[key];
- },
- getPrototypeOf(target) {
- return Object.getPrototypeOf(target);
- }
- });
- successCallback(pos);
- }
-
- function processOriginalGPSDataObject(expectedMaxAge, originalPositionObject) {
- if (expectedMaxAge === undefined) {
- expectedMaxAge = 0; // default value
- }
- // Set reasonable expectedMaxAge of 1 hour for later computation
- expectedMaxAge = Math.min(3600000, expectedMaxAge);
- geoTimestamp = Math.max(geoTimestamp, Date.now() - Math.random()*expectedMaxAge);
-
- let spoofPos = coords => {
- let pos = { timestamp: geoTimestamp };
- if (coords) pos.coords = coords;
- spoofCall(pos, originalPositionObject, successCallback);
- };
-
- if (provideAccurateGeolocationData) {
- return spoofPos();
- }
- if (previouslyReturnedCoords) {
- return spoofPos(clone(previouslyReturnedCoords));
- }
-
- const EQUATOR_LEN = 40074;
- const HALF_MERIDIAN = 10002;
- const DESIRED_ACCURACY_KM = desiredAccuracy*2;
-
- var lat = originalPositionObject.coords.latitude;
- var lon = originalPositionObject.coords.longitude;
- // Compute (approximate) distance from 0 meridian [m]
- var x = (lon * (EQUATOR_LEN * Math.cos((lat/90)*(Math.PI/2))) / 180);
- // Compute (approximate) distance from equator [m]
- var y = (lat / 90) * (HALF_MERIDIAN);
-
- // Compute the coordinates of the left bottom corner of the tile in which the orig position is
- var xmin = Math.floor(x / DESIRED_ACCURACY_KM) * DESIRED_ACCURACY_KM;
- var ymin = Math.floor(y / DESIRED_ACCURACY_KM) * DESIRED_ACCURACY_KM;
-
- // The position to be returned should be in the original tile and the 8 adjacent tiles:
- // +----+----+----+
- // | | | |
- // +----+----+----+
- // | |orig| |
- // +----+----+----+
- // | | | |
- // +----+----+----+
- var newx = xmin + randomx * 3 * DESIRED_ACCURACY_KM - DESIRED_ACCURACY_KM;
- var newy = ymin + randomy * 3 * DESIRED_ACCURACY_KM - DESIRED_ACCURACY_KM;
-
- if (Math.abs(newy) > (HALF_MERIDIAN)) {
- newy = (HALF_MERIDIAN + HALF_MERIDIAN - Math.abs(newy)) * (newy < 0 ? -1 : 1);
- newx = -newx;
- }
-
- var newLatitude = newy / HALF_MERIDIAN * 90;
- var newLongitude = newx * 180 / (EQUATOR_LEN * Math.cos((newLatitude/90)*(Math.PI/2)));
-
- while (newLongitude < -180) {
- newLongitude += 360;
- }
- while (newLongitude > 180) {
- newLongitude -= 360;
- }
-
- var newAccuracy = DESIRED_ACCURACY_KM * 1000 * 2.5; // in meters
-
- previouslyReturnedCoords = {
- latitude: newLatitude,
- longitude: newLongitude,
- altitude: null,
- accuracy: newAccuracy,
- altitudeAccuracy: null,
- heading: null,
- speed: null,
- __proto__: originalPositionObject.coords.__proto__
- };
- spoofPos(previouslyReturnedCoords);
- };
- /**
- * \brief process the parameters of the wrapping function
- *
- * Checks if the wrappers should be active, and the position modified. Transforms the desired
- * precision into kilometers.
- */
- var setArgs = `
- var enableGeolocation = (args[0] !== 0);
- var provideAccurateGeolocationData = (args[0] === -1);
- let desiredAccuracy = 0;
- switch (args[0]) {
- case 2:
- desiredAccuracy = 0.1;
- break;
- case 3:
- desiredAccuracy = 1;
- break;
- case 4:
- desiredAccuracy = 10;
- break;
- case 5:
- desiredAccuracy = 100;
- break;
- }
- `;
-
- var wrappers = [
- {
- parent_object: "Navigator.prototype",
- parent_object_property: "geolocation",
- wrapped_objects: [],
- helping_code: setArgs,
- post_wrapping_code: [
- {
- code_type: "delete_properties",
- parent_object: "Navigator.prototype",
- apply_if: "!enableGeolocation",
- delete_properties: ["geolocation"],
- }
- ],
- },
- {
- parent_object: "window",
- parent_object_property: "Geolocation",
- wrapped_objects: [],
- helping_code: setArgs,
- post_wrapping_code: [
- {
- code_type: "delete_properties",
- parent_object: "window",
- apply_if: "!enableGeolocation",
- delete_properties: ["Geolocation"],
- }
- ],
- },
- {
- parent_object: "window",
- parent_object_property: "GeolocationCoordinates",
- wrapped_objects: [],
- helping_code: setArgs,
- post_wrapping_code: [
- {
- code_type: "delete_properties",
- parent_object: "window",
- apply_if: "!enableGeolocation",
- delete_properties: ["GeolocationCoordinates"],
- }
- ],
- },
- {
- parent_object: "window",
- parent_object_property: "GeolocationPosition",
- wrapped_objects: [],
- helping_code: setArgs,
- post_wrapping_code: [
- {
- code_type: "delete_properties",
- parent_object: "window",
- apply_if: "!enableGeolocation",
- delete_properties: ["GeolocationPosition"],
- }
- ],
- },
- {
- parent_object: "window",
- parent_object_property: "GeolocationPositionError",
- wrapped_objects: [],
- helping_code: setArgs,
- post_wrapping_code: [
- {
- code_type: "delete_properties",
- parent_object: "window",
- apply_if: "!enableGeolocation",
- delete_properties: ["GeolocationPositionError"],
- }
- ],
- },
- {
- parent_object: "Geolocation.prototype",
- parent_object_property: "getCurrentPosition",
-
- wrapped_objects: [
- {
- original_name: "Geolocation.prototype.getCurrentPosition",
- callable_name: "originalGetCurrentPosition",
- },
- ],
- helping_code: setArgs + processOriginalGPSDataObject_globals,
- wrapping_function_args: "successCallback, errorCallback, origOptions",
- /** \fn fake Geolocation.prototype.getCurrentPosition
- * \brief Provide a fake geolocation position
- */
- wrapping_function_body: `
- ${spoofCall}
- ${processOriginalGPSDataObject}
- var options = {
- enableHighAccuracy: false,
- };
- if (origOptions) try {
- if ("timeout" in origOptions) {
- options.timeout = origOptions.timeout;
- }
- if ("maximumAge" in origOptions) {
- option.maximumAge = origOptions.maximumAge;
- }
- }
- catch { /* Undefined or another error */}
- originalGetCurrentPosition.call(this, processOriginalGPSDataObject.bind(null, options.maximumAge), errorCallback, options);
- `,
- },
- {
- parent_object: "Geolocation.prototype",
- parent_object_property: "watchPosition",
- wrapped_objects: [
- {
- original_name: "Geolocation.prototype.watchPosition",
- wrapped_name: "originalWatchPosition",
- },
- ],
- helping_code: setArgs + processOriginalGPSDataObject_globals + "let watchPositionCounter = 0;",
- wrapping_function_args: "successCallback, errorCallback, origOptions",
- /** \fn fake Geolocation.prototype.watchPosition
- * Geolocation.prototype.watchPosition intended use concerns tracking user position changes.
- * JShelter provides four modes of operaion:
- * * current position approximation: Always return the same data, the same as getCurrentPosition()
- * * accurate data: Return exact position but fake timestamp
- */
- wrapping_function_body: `
- if (provideAccurateGeolocationData) {
- function wrappedSuccessCallback(originalPositionObject) {
- geoTimestamp = Date.now(); // Limit the timestamp accuracy by calling possibly wrapped function
- return spoofCall({ timestamp: geoTimestamp }, originalPositionObject, succesCallback);
- }
- originalWatchPosition.call(this, wrappedSuccessCallback, errorCallback, origOptions);
- }
- else {
- // Re-use the wrapping of n.g.getCurrentPosition()
- Geolocation.prototype.getCurrentPosition(successCallback, errorCallback, origOptions);
- watchPositionCounter++;
- return watchPositionCounter;
- }
- `,
- },
- {
- parent_object: "Geolocation.prototype",
- parent_object_property: "clearWatch",
- wrapped_objects: [
- {
- original_name: "Geolocation.prototype.clearWatch",
- wrapped_name: "originalClearWatch",
- },
- ],
- helping_code: setArgs,
- wrapping_function_args: "id",
- /** \fn fake_or_original Geolocation.prototype.clearWatch
- * If the Geolocation API provides correct data, call the original implementation,
- * otherwise do nothing as the watchPosition object was not created.
- */
- wrapping_function_body: `
- if (provideAccurateGeolocationData) {
- originalClearWatch.call(Geolocation.prototype, id);
- }
- `,
- }
- ]
- add_wrappers(wrappers);
-})();