function fingerprintPayload(config) {
const scope = globalThis;
const marker = '__browserCommanderFingerprint';
if (scope[marker]) {
return;
}
Object.defineProperty(scope, marker, {
value: true,
configurable: true,
enumerable: false,
writable: false,
});
const nativeSources = new WeakMap();
const functionToString = Function.prototype.toString;
const objectDefineProperty = Object.defineProperty;
function asNative(fn, name) {
try {
objectDefineProperty(fn, 'name', {
value: name,
configurable: true,
enumerable: false,
writable: false,
});
} catch {
}
nativeSources.set(fn, `function ${name}() { [native code] }`);
return fn;
}
const patchedToString = function toString() {
const source = nativeSources.get(this);
return source === undefined ? functionToString.call(this) : source;
};
nativeSources.set(patchedToString, functionToString.call(functionToString));
Function.prototype.toString = patchedToString;
function defineNativeGetter(target, property, produce) {
if (!target) {
return;
}
const descriptor = Object.getOwnPropertyDescriptor(target, property);
if (descriptor && !descriptor.configurable) {
return;
}
const getter = asNative(() => produce(), `get ${property}`);
try {
objectDefineProperty(target, property, {
get: getter,
set: undefined,
enumerable: descriptor ? descriptor.enumerable : true,
configurable: true,
});
} catch {
}
}
const navigatorPrototype = scope.Navigator && scope.Navigator.prototype;
const workerNavigatorPrototype =
scope.WorkerNavigator && scope.WorkerNavigator.prototype;
function defineNavigator(property, value) {
defineNativeGetter(navigatorPrototype, property, () => value);
defineNativeGetter(workerNavigatorPrototype, property, () => value);
}
if (config.webdriver !== undefined) {
defineNavigator('webdriver', config.webdriver);
}
if (config.deviceMemory !== undefined) {
defineNavigator('deviceMemory', config.deviceMemory);
}
if (config.hardwareConcurrency !== undefined) {
defineNavigator('hardwareConcurrency', config.hardwareConcurrency);
}
if (config.vendor !== undefined) {
defineNavigator('vendor', config.vendor);
}
if (config.platform !== undefined) {
defineNavigator('platform', config.platform);
}
if (config.maxTouchPoints !== undefined) {
defineNavigator('maxTouchPoints', config.maxTouchPoints);
}
if (config.doNotTrack !== undefined) {
defineNativeGetter(
navigatorPrototype,
'doNotTrack',
() => config.doNotTrack
);
}
if (config.languages !== undefined) {
defineNavigator('language', config.languages[0]);
defineNativeGetter(navigatorPrototype, 'languages', () =>
Object.freeze(config.languages.slice())
);
defineNativeGetter(workerNavigatorPrototype, 'languages', () =>
Object.freeze(config.languages.slice())
);
}
const screenPrototype = scope.Screen && scope.Screen.prototype;
if (config.screen) {
for (const property of Object.keys(config.screen)) {
const value = config.screen[property];
defineNativeGetter(screenPrototype, property, () => value);
}
}
if (config.webgl) {
const replacements = new Map();
if (config.webgl.unmaskedVendor !== undefined) {
replacements.set(0x9245, config.webgl.unmaskedVendor);
}
if (config.webgl.unmaskedRenderer !== undefined) {
replacements.set(0x9246, config.webgl.unmaskedRenderer);
}
if (config.webgl.vendor !== undefined) {
replacements.set(0x1f00, config.webgl.vendor);
}
if (config.webgl.renderer !== undefined) {
replacements.set(0x1f01, config.webgl.renderer);
}
for (const name of ['WebGLRenderingContext', 'WebGL2RenderingContext']) {
const prototype = scope[name] && scope[name].prototype;
if (!prototype || typeof prototype.getParameter !== 'function') {
continue;
}
const original = prototype.getParameter;
const patched = asNative(function getParameter(parameter) {
if (replacements.has(parameter)) {
return replacements.get(parameter);
}
return original.call(this, parameter);
}, 'getParameter');
prototype.getParameter = patched;
}
}
}