Skip to content

Commit

Permalink
Remote value support (#65)
Browse files Browse the repository at this point in the history
* Moving to use Swift module
* Make values for hardware remoted
* Support feature setting types objects from remote config
* Support apple silicon config values

Co-authored-by: Brad Slayter <[email protected]>
  • Loading branch information
jonathanKingston and SlayterDev authored Jul 27, 2022
1 parent 80dcfca commit b06512e
Show file tree
Hide file tree
Showing 15 changed files with 511 additions and 39 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build/
lib/
Sources/ContentScopeScripts/dist/
integration-test/extension/contentScope.js
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Sources/ContentScopeScripts/dist/** binary
build/** binary linguist-generated
shared/** binary linguist-generated
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
node_modules/
.swiftpm
27 changes: 27 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "ContentScopeScripts",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "ContentScopeScripts",
targets: ["ContentScopeScripts"]),
],
dependencies: [
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "ContentScopeScripts",
dependencies: [],
resources: [
.process("dist")
]
),
]
)
5 changes: 5 additions & 0 deletions Sources/ContentScopeScripts/ContentScopeScripts.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

public struct ContentScopeScripts {
public static var Bundle: Bundle = .module
}
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,17 @@
};
// TODO
preferences.cookie = {};

// Copy feature settings from remote config to preferences object
preferences.featureSettings = {};
remoteFeatureNames.forEach((featureName) => {
if (!enabledFeatures.includes(featureName)) {
return
}

preferences.featureSettings[featureName] = data.features[featureName].settings;
});

return preferences
}

Expand Down Expand Up @@ -1596,6 +1607,83 @@
})
}

// We use this method to detect M1 macs and set appropriate API values to prevent sites from detecting fingerprinting protections
function isAppleSilicon () {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');

// Best guess if the device is an Apple Silicon
// https://stackoverflow.com/a/65412357
return gl.getSupportedExtensions().indexOf('WEBGL_compressed_texture_etc') !== -1
}

/**
* Take configSeting which should be an array of possible values.
* If a value contains a criteria that is a match for this environment then return that value.
* Otherwise return the first value that doesn't have a criteria.
*
* @param {*[]} configSetting - Config setting which should contain a list of possible values
* @returns {*|undefined} - The value from the list that best matches the criteria in the config
*/
function processAttrByCriteria (configSetting) {
let bestOption;
for (const item of configSetting) {
if (item.criteria) {
if (item.criteria.arch === 'AppleSilicon' && isAppleSilicon()) {
bestOption = item;
break
}
} else {
bestOption = item;
}
}

return bestOption
}

/**
* Get the value of a config setting.
* If the value is not set, return the default value.
* If the value is not an object, return the value.
* If the value is an object, check its type property.
*
* @param {string} featureName
* @param {object} args
* @param {string} prop
* @param {any} defaultValue - The default value to use if the config setting is not set
* @returns The value of the config setting or the default value
*/
function getFeatureAttr (featureName, args, prop, defaultValue) {
let configSetting = getFeatureSetting(featureName, args, prop);

if (configSetting === undefined) {
return defaultValue
}

const configSettingType = typeof configSetting;
switch (configSettingType) {
case 'object':
if (Array.isArray(configSetting)) {
configSetting = processAttrByCriteria(configSetting);
if (configSetting === undefined) {
return defaultValue
}
}

if (!configSetting.type) {
return defaultValue
}

if (configSetting.type === 'undefined') {
return undefined
}

return configSetting.value
default:
return defaultValue
}
}

/**
* @param {string} featureName
* @param {object} args
Expand Down Expand Up @@ -3610,24 +3698,26 @@
init: init$9
});

const featureName$1 = 'fingerprinting-hardware';

function init$8 (args) {
const Navigator = globalThis.Navigator;
const navigator = globalThis.navigator;

overrideProperty('keyboard', {
object: Navigator.prototype,
origValue: navigator.keyboard,
targetValue: undefined
targetValue: getFeatureAttr(featureName$1, args, 'keyboard')
});
overrideProperty('hardwareConcurrency', {
object: Navigator.prototype,
origValue: navigator.hardwareConcurrency,
targetValue: 2
targetValue: getFeatureAttr(featureName$1, args, 'hardwareConcurrency', 2)
});
overrideProperty('deviceMemory', {
object: Navigator.prototype,
origValue: navigator.deviceMemory,
targetValue: 8
targetValue: getFeatureAttr(featureName$1, args, 'deviceMemory', 8)
});
}

Expand All @@ -3636,6 +3726,8 @@
init: init$8
});

const featureName = 'fingerprinting-screen-size';

/**
* normalize window dimensions, if more than one monitor is in play.
* X/Y values are set in the browser based on distance to the main monitor top or left, which
Expand Down Expand Up @@ -3725,12 +3817,12 @@
origPropertyValues.availTop = overrideProperty('availTop', {
object: Screen.prototype,
origValue: screen.availTop,
targetValue: 0
targetValue: getFeatureSetting(featureName, args, 'availTop')
});
origPropertyValues.availLeft = overrideProperty('availLeft', {
object: Screen.prototype,
origValue: screen.availLeft,
targetValue: 0
targetValue: getFeatureSetting(featureName, args, 'availLeft')
});
origPropertyValues.availWidth = overrideProperty('availWidth', {
object: Screen.prototype,
Expand All @@ -3745,12 +3837,12 @@
overrideProperty('colorDepth', {
object: Screen.prototype,
origValue: screen.colorDepth,
targetValue: 24
targetValue: getFeatureSetting(featureName, args, 'colorDepth')
});
overrideProperty('pixelDepth', {
object: Screen.prototype,
origValue: screen.pixelDepth,
targetValue: 24
targetValue: getFeatureSetting(featureName, args, 'pixelDepth')
});

window.addEventListener('resize', function () {
Expand Down
2 changes: 1 addition & 1 deletion build/chrome/inject.js

Large diffs are not rendered by default.

95 changes: 88 additions & 7 deletions build/firefox/inject.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b06512e

Please sign in to comment.