├── .editorconfig ├── .eslintrc.js ├── .github └── FUNDING.yml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── dist ├── components │ ├── brave │ │ ├── brave.d.ts │ │ └── brave.js │ ├── devicePixelRatio │ │ ├── devicePixelRatio.d.ts │ │ └── devicePixelRatio.js │ ├── getBattery │ │ ├── getBattery.d.ts │ │ └── getBattery.js │ ├── index.d.ts │ ├── index.js │ ├── jsHeapSizeLimit │ │ ├── jsHeapSizeLimit.d.ts │ │ └── jsHeapSizeLimit.js │ ├── matchMedia │ │ ├── matchMedia.d.ts │ │ └── matchMedia.js │ ├── screen │ │ ├── screen.d.ts │ │ └── screen.js │ ├── timingResolution │ │ ├── timingResolution.d.ts │ │ └── timingResolution.js │ └── userAgentData │ │ ├── userAgentData.d.ts │ │ └── userAgentData.js ├── es5 │ └── opjs.min.js ├── global.t.d.ts ├── global.t.js ├── opjs.d.ts ├── opjs.esm.js ├── opjs.js └── utils │ ├── hash.d.ts │ ├── hash.js │ ├── interfaces.d.ts │ ├── interfaces.js │ ├── utils.d.ts │ └── utils.js ├── docs ├── README.md ├── modules │ └── README.md └── whitepapers │ └── rfc-opjs-v1.md ├── package-lock.json ├── package.json ├── src ├── components │ ├── brave │ │ └── brave.ts │ ├── devicePixelRatio │ │ └── devicePixelRatio.ts │ ├── getBattery │ │ └── getBattery.ts │ ├── index.ts │ ├── jsHeapSizeLimit │ │ └── jsHeapSizeLimit.ts │ ├── matchMedia │ │ └── matchMedia.ts │ ├── screen │ │ └── screen.ts │ ├── timingResolution │ │ └── timingResolution.ts │ └── userAgentData │ │ └── userAgentData.ts ├── global.t.ts ├── opjs.ts └── utils │ ├── hash.ts │ ├── interfaces.ts │ └── utils.ts ├── tsconfig.json └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": "standard-with-typescript", 7 | "overrides": [ 8 | { 9 | "env": { 10 | "node": true 11 | }, 12 | "files": [ 13 | ".eslintrc.{js,cjs}" 14 | ], 15 | "parserOptions": { 16 | "sourceType": "script" 17 | } 18 | } 19 | ], 20 | "parserOptions": { 21 | "ecmaVersion": "latest", 22 | "sourceType": "module" 23 | }, 24 | "rules": { 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Joe12387 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "trailingComma": "es5", 6 | "printWidth": 80 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": true, 3 | "eslint.workingDirectories": [{ "mode": "auto" }], 4 | "eslint.options": { 5 | "overrideConfigFile": "eslint.config.js" 6 | }, 7 | "eslint.validate": [ 8 | "javascript", 9 | "javascriptreact", 10 | "typescript", 11 | "typescriptreact" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # LICENSE.md (OverpoweredJS OS Public License v1) 2 | 3 | By using OverpoweredJS OS, you agree to both the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License terms and the OverpoweredJS OS Public License Additional Terms and Conditions Rider. 4 | 5 | You CANNOT use this for commercial purposes regardless of what kind of entity you are or represent. 6 | 7 | You CANNOT save or transmit fingerprinting data produced by OverpoweredJS unless you meet the User Consent Requirements specified in the Additional Terms and Conditions Rider. 8 | 9 | For more information, actually read the license terms. 10 | 11 | ## Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License 12 | 13 | ### BY-NC-SA 4.0 Summary (simple overview, not legally binding, view full license text) 14 | 15 | You are free to: 16 | 17 | - **Share** — copy and redistribute the material in any medium or format 18 | - **Adapt** — remix, transform, and build upon the material 19 | 20 | Under the following terms: 21 | 22 | - **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 23 | - **NonCommercial** — You may not use the material for commercial purposes. 24 | - **ShareAlike** — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 25 | 26 | ### BY-NC-SA 4.0 Legally Binding Full License Text 27 | 28 | For full details, please refer to the [Creative Commons License](https://creativecommons.org/licenses/by-nc-sa/4.0/). 29 | 30 | ## OverpoweredJS OS Public License Additional Terms and Conditions (Rider) 31 | 32 | In addition to the terms of the BY-NC-SA 4.0 license, the following additional terms apply: 33 | 34 | 1. **NonCommercial Use Definition**: NonCommercial use is defined as use that is not intended for or directed towards commercial advantage or monetary compensation. This includes but is not limited to use in educational institutions, personal projects, and non-profit organizations. 35 | 36 | 2. **User Consent Requirement**: 37 | - The software may only be used in a web page or application when a user legally consents to being tracked cross-site. 38 | - The consent must be clear and understandable such that a reasonable person would comprehend the notice. 39 | - The user must explicitly click a button or perform a similar input action to give this access. 40 | - No fingerprinting may take place before this explicit consent is obtained. 41 | 42 | 3. **Attribution Details**: Any distribution of the work must include a prominent attribution to the original author, including the author's name, the title of the work, and a link to the original source. 43 | 44 | 4. **Modification Notification**: If you adapt, remix, transform, or build upon the material, you must notify the original author of the changes made and provide a summary of the modifications. 45 | 46 | 5. **Indemnification**: Users of the material agree to indemnify and hold harmless the original author from any claims, damages, or liabilities arising from their use of the material. 47 | 48 | ## Copyright 49 | (c) 2024 Joe Rutkowski (Joe12387) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Looking for the free fingerprinting API?](https://overpoweredjs.com/) 2 | 3 | # OverpoweredJS 4 | 5 | **OverpoweredJS** is a family of two dual-licensed client-side JavaScript browser fingerprinting libraries designed to provide entropy to any mechanism of tracking individual browsers, enabling mutabile fingerprint analysis. The fingerprint objects OverpoweredJS creates contain identifiers (known as fingerprint components) that are used to compare to known fingerprints in a server-side database. 6 | 7 | The method of comparing fingerprint objects can vary, but a simple solution is to assign each fingerprint component a weight. When comparing two or more fingerprints to each other, determine which fingerprint components are the same between the objects. If a component exists with a particular key name in both objects and both values of those keys are also the same, then it can be considered a match. If a match occurs, a weight is added to an overall sum of all matching components' weights (a score) and compared to the maximum score possible (i.e. identical fingerprint objects, or all weights summed). This allows you to determine the percentage chance that these fingerprints originate from the same browser instance. 8 | 9 | Determining the weights to add to the score for each component is the difficult part. For more, see the whitepaper in `/docs`. 10 | 11 | ## Open Source 12 | 13 | **OverpoweredJS Open Source** is a public open source library with a license that allows for use **as long as users explicitly consent to being tracked**. Furthermore, an additional restriction under the licensing is that **it can be used for non-commercial uses only**. 14 | 15 | This repo is for the Open Source edition of OverpoweredJS. OverpoweredJS OS is derivative of the CS branch minus non-public modules. More modules will be added to OS in the future, but are not yet public. 16 | 17 | Feel free to fork this repo as long as you obey the license terms and do not change the terms. Pull requests are encouraged if you wish to submit improvements, including modules of unexploited APIs. 18 | 19 | ## TODO 20 | 21 | ### Known Issues 22 | - [ ] userAgentData.ts: fullVersionList is not properly sorted. 23 | - [ ] Error handling of modules is poor. 24 | - [ ] Module & stage performance metrics are not yet implemented. 25 | - [ ] Only supports modern browsers, old browsers like MSIE will not track well even if polyfills are added. 26 | - [ ] The whitepaper's markdown of math functions are broken. Feel free to submit a pull request if you can fix them. 27 | 28 | ### Unexploited APIs 29 | - [x] CanvasAPI (in development) 30 | - [x] WebGL (in development) 31 | - [x] Navigator object (in development) 32 | - [ ] AudioContext (not bothering, feel free to do it yourself) 33 | 34 | ## Closed Source 35 | 36 | Sorry, the CS branch is no longer available for licensing as it has been folded into the API's codebase. 37 | 38 | **OverpoweredJS Closed Source** is a solution for businesses and organizations that require access to the most important and highest entropy fingerprinting components. These non-public modules supply more fingerprinting components in order to improve on the Open Source edition, allowing higher accuracy, among other features. This license allows for commercial use of either the Open Source or Closed Source editions. 39 | 40 | This is specifically for businesses and organizations that require commercial use and higher accuracy. 41 | 42 | To pursue potentially purchasing a license, please contact Joe+opjs@dreggle.com. 43 | 44 | ## OverpoweredJS API 45 | The **OverpoweredJS API** uses OverpoweredJS Closed Source and server-side processing to track browser instances without having to run your own server. It is currently available and can be embedded on any site that uses HTTPS. After calling the `opjs` function, you'll get something back like this: 46 | ```json 47 | { 48 | "clusterUUID": "0W-C9Q-WS6-OHK", 49 | "hash": "981ea46cc95667439294e31fffd8d7c060a0e9f7f3f553a7c4943fa7541d9747", 50 | "botScore": 1 51 | } 52 | ``` 53 | 54 | - The `clusterUUID` is the (hopefully) unique ID that is attached to the browser. 55 | - The `hash` is simply the hash of the JSON object sent to the server. It can (and will) change, but the `clusterUUID` should not. 56 | - The `botScore` is a score from 1 to 5 with 5 being the highest likelyhood of being a bot. 57 | 58 | As of August 2024, the prototype service can track most Chromium-based browsers such as Google Chrome, Microsoft Edge and Opera. 59 | 60 | Support for other browsers such as Firefox, Brave and Safari is in development. These browsers may cause collisions (having the same `clusterUUID` as other browser instances). This may or may not change in the future. For the time being, non-Chromium browsers will be rejected by the API. Particularly, Apple devices will be the least unique due to the homogeneity of Apple's software and hardware, as well as Apple's continued efforts to make their software resistant to tracking. 61 | 62 | This is intended to be a commercial API for those priced out of similar SaaS fingerprinting solutions, and is intended to be as inexpensive as possible. 63 | 64 | The prototype API is currently up and running. [You can participate here](https://github.com/Joe12387/overpoweredjs-api/). 65 | 66 | You may participate regardless of how much traffic you have or whether or not you're going to sign up for the service when it goes into production. 67 | 68 | # Copyright 69 | (c) 2024 Joe Rutkowski (Joe12387) - Joe@dreggle.com - github.com/Joe12387 70 | -------------------------------------------------------------------------------- /dist/components/brave/brave.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const detectBrave: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/brave/brave.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | exports.__esModule = true; 39 | exports.detectBrave = void 0; 40 | var interfaces_1 = require("../../utils/interfaces"); 41 | var detectBrave = function () { return __awaiter(void 0, void 0, void 0, function () { 42 | var braveNavigator, braveClues, likeBrave, isBrave, mode, brave; 43 | return __generator(this, function (_a) { 44 | braveNavigator = navigator; 45 | if (typeof braveNavigator === 'undefined' || braveNavigator === null) 46 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-1)]; 47 | else { 48 | braveClues = [ 49 | typeof braveNavigator.brave === 'object', 50 | 'brave' in navigator, 51 | typeof braveNavigator.brave === 'object' && 52 | Object.getPrototypeOf(navigator.brave).constructor.name == 'Brave', 53 | typeof braveNavigator.brave === 'object' && 54 | typeof braveNavigator.brave.isBrave === 'function' && 55 | braveNavigator.brave.isBrave.toString() === 56 | 'function isBrave() { [native code] }', 57 | ]; 58 | likeBrave = braveClues.includes(true); 59 | isBrave = !braveClues.includes(false); 60 | mode = -1; 61 | if (likeBrave) { 62 | // TODO: Implement detection of Brave's shields status 63 | } 64 | brave = { 65 | likeBrave: likeBrave, 66 | isBrave: isBrave, 67 | mode: mode, 68 | braveClues: braveClues 69 | }; 70 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(0, { brave: brave })]; 71 | } 72 | return [2 /*return*/]; 73 | }); 74 | }); }; 75 | exports.detectBrave = detectBrave; 76 | -------------------------------------------------------------------------------- /dist/components/devicePixelRatio/devicePixelRatio.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const devicePixelRatio: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/devicePixelRatio/devicePixelRatio.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | exports.__esModule = true; 39 | exports.devicePixelRatio = void 0; 40 | var interfaces_1 = require("../../utils/interfaces"); 41 | var utils_1 = require("../../utils/utils"); 42 | var devicePixelRatio = function () { return __awaiter(void 0, void 0, void 0, function () { 43 | var devicePixelRatio; 44 | return __generator(this, function (_a) { 45 | devicePixelRatio = (0, utils_1.getSpecificTypeAndValue)(self.devicePixelRatio); 46 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(0, { devicePixelRatio: devicePixelRatio })]; 47 | }); 48 | }); }; 49 | exports.devicePixelRatio = devicePixelRatio; 50 | -------------------------------------------------------------------------------- /dist/components/getBattery/getBattery.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const getBattery: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/getBattery/getBattery.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | exports.__esModule = true; 39 | exports.getBattery = void 0; 40 | var interfaces_1 = require("../../utils/interfaces"); 41 | var utils_1 = require("../../utils/utils"); 42 | var getBattery = function (fingerprint) { return __awaiter(void 0, void 0, void 0, function () { 43 | var battery, error_1; 44 | return __generator(this, function (_a) { 45 | switch (_a.label) { 46 | case 0: 47 | if (typeof self.navigator !== 'object') { 48 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-2)]; 49 | } 50 | if (typeof self.navigator.getBattery !== 'function') { 51 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-1)]; 52 | } 53 | _a.label = 1; 54 | case 1: 55 | _a.trys.push([1, 3, , 4]); 56 | return [4 /*yield*/, self.navigator.getBattery()]; 57 | case 2: 58 | battery = _a.sent(); 59 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(0, { 60 | getBattery_level: (0, utils_1.getSpecificTypeAndValue)(battery.level), 61 | getBattery_charging: (0, utils_1.getSpecificTypeAndValue)(battery.charging), 62 | getBattery_chargingTime: (0, utils_1.getSpecificTypeAndValue)(battery.chargingTime), 63 | getBattery_dischargingTime: (0, utils_1.getSpecificTypeAndValue)(battery.dischargingTime) 64 | })]; 65 | case 3: 66 | error_1 = _a.sent(); 67 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-3)]; 68 | case 4: return [2 /*return*/]; 69 | } 70 | }); 71 | }); }; 72 | exports.getBattery = getBattery; 73 | -------------------------------------------------------------------------------- /dist/components/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare const fingerprintStage1: ComponentInterface[]; 2 | export declare const fingerprintStage2: ComponentInterface[]; 3 | export declare const fingerprintStage3: ComponentInterface[]; 4 | export interface ComponentOutputInterface { 5 | status: number; 6 | value?: string | number | object | boolean | null; 7 | type?: string; 8 | isTrusted?: boolean; 9 | } 10 | export interface ComponentInterface { 11 | name: string; 12 | func: ComponentFunctionInterface; 13 | } 14 | export type ComponentFunctionInterface = (fingerprint: object) => Promise; 15 | -------------------------------------------------------------------------------- /dist/components/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.fingerprintStage3 = exports.fingerprintStage2 = exports.fingerprintStage1 = void 0; 4 | var brave_1 = require("./brave/brave"); 5 | var timingResolution_1 = require("./timingResolution/timingResolution"); 6 | var userAgentData_1 = require("./userAgentData/userAgentData"); 7 | var jsHeapSizeLimit_1 = require("./jsHeapSizeLimit/jsHeapSizeLimit"); 8 | var screen_1 = require("./screen/screen"); 9 | var devicePixelRatio_1 = require("./devicePixelRatio/devicePixelRatio"); 10 | var getBattery_1 = require("./getBattery/getBattery"); 11 | var matchMedia_1 = require("./matchMedia/matchMedia"); 12 | exports.fingerprintStage1 = []; 13 | exports.fingerprintStage2 = []; 14 | exports.fingerprintStage3 = []; 15 | function addComponent(component, componentName, stage) { 16 | stage.push({ 17 | name: componentName, 18 | func: component 19 | }); 20 | } 21 | // stage 1 22 | addComponent(brave_1.detectBrave, 'brave', exports.fingerprintStage1); 23 | addComponent(timingResolution_1.timingResolution, 'timingResolution', exports.fingerprintStage1); 24 | addComponent(userAgentData_1.userAgentData, 'userAgentData', exports.fingerprintStage1); 25 | addComponent(jsHeapSizeLimit_1.jsHeapSizeLimit, 'jsHeapSizeLimit', exports.fingerprintStage1); 26 | // stage 2 27 | addComponent(screen_1.screen, 'screen', exports.fingerprintStage2); 28 | addComponent(devicePixelRatio_1.devicePixelRatio, 'devicePixelRatio', exports.fingerprintStage2); 29 | addComponent(getBattery_1.getBattery, 'getBattery', exports.fingerprintStage2); 30 | addComponent(matchMedia_1.matchMedia, 'matchMedia', exports.fingerprintStage2); 31 | // stage 3 32 | // this may be used for additional components that require the outputs of stages 1 and/or 2 33 | -------------------------------------------------------------------------------- /dist/components/jsHeapSizeLimit/jsHeapSizeLimit.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const jsHeapSizeLimit: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/jsHeapSizeLimit/jsHeapSizeLimit.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | exports.__esModule = true; 39 | exports.jsHeapSizeLimit = void 0; 40 | var interfaces_1 = require("../../utils/interfaces"); 41 | var utils_1 = require("../../utils/utils"); 42 | var jsHeapSizeLimit = function () { return __awaiter(void 0, void 0, void 0, function () { 43 | var performance, memory, jsHeapSizeLimit; 44 | return __generator(this, function (_a) { 45 | performance = self.performance; 46 | if (typeof performance !== 'object') 47 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-1)]; 48 | memory = performance.memory; 49 | if (typeof memory !== 'object') 50 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-2)]; 51 | jsHeapSizeLimit = (0, utils_1.getSpecificTypeAndValue)(memory.jsHeapSizeLimit); 52 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(0, { jsHeapSizeLimit: jsHeapSizeLimit })]; 53 | }); 54 | }); }; 55 | exports.jsHeapSizeLimit = jsHeapSizeLimit; 56 | -------------------------------------------------------------------------------- /dist/components/matchMedia/matchMedia.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const matchMedia: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/matchMedia/matchMedia.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __values = (this && this.__values) || function(o) { 39 | var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; 40 | if (m) return m.call(o); 41 | if (o && typeof o.length === "number") return { 42 | next: function () { 43 | if (o && i >= o.length) o = void 0; 44 | return { value: o && o[i++], done: !o }; 45 | } 46 | }; 47 | throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); 48 | }; 49 | var __read = (this && this.__read) || function (o, n) { 50 | var m = typeof Symbol === "function" && o[Symbol.iterator]; 51 | if (!m) return o; 52 | var i = m.call(o), r, ar = [], e; 53 | try { 54 | while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); 55 | } 56 | catch (error) { e = { error: error }; } 57 | finally { 58 | try { 59 | if (r && !r.done && (m = i["return"])) m.call(i); 60 | } 61 | finally { if (e) throw e.error; } 62 | } 63 | return ar; 64 | }; 65 | exports.__esModule = true; 66 | exports.matchMedia = void 0; 67 | var interfaces_1 = require("../../utils/interfaces"); 68 | var matchMedia = function (fingerprint) { return __awaiter(void 0, void 0, void 0, function () { 69 | var mediaQueries, matchMedia, matchMedia_mediaQueries, _a, _b, _c, key, value, value_1, value_1_1, item, query, result; 70 | var e_1, _d, e_2, _e; 71 | return __generator(this, function (_f) { 72 | mediaQueries = { 73 | 'prefers-contrast': ['high', 'more', 'low', 'less', 'forced', 'no-preference', 'high-contrast', 'low-contrast'], 74 | 'color-gamut': ['rec2020', 'p3', 'srgb', 'display-p3', 'a98rgb'], 75 | 'dynamic-range': ['high', 'standard', 'hdr', 'sdr'], 76 | 'video-dynamic-range': ['high', 'standard', 'hdr', 'sdr'], 77 | 'any-hover': ['hover', 'none', 'on-demand'], 78 | 'any-pointer': ['none', 'coarse', 'fine', 'hover'], 79 | 'pointer': ['none', 'coarse', 'fine', 'hover'], 80 | 'hover': ['hover', 'none', 'on-demand'], 81 | 'update': ['fast', 'slow', 'none'], 82 | 'overflow-block': ['scroll', 'none', 'optional-paged', 'paged', 'optional-paged-x', 'optional-paged-y'], 83 | 'overflow-inline': ['scroll', 'none', 'optional-paged', 'paged', 'optional-paged-x', 'optional-paged-y'], 84 | 'color': ['8', '16', '256', '4k', '8k'], 85 | 'inverted-colors': ['inverted', 'none', 'no-preference'], 86 | 'prefers-reduced-motion': ['reduce', 'no-preference', 'motion'], 87 | 'prefers-reduced-transparency': ['reduce', 'no-preference', 'transparency'], 88 | 'grid': ['0', '1', '2'], 89 | 'scripting': ['none', 'initial-only', 'enabled', 'enabled-only'], 90 | 'forced-colors': ['active', 'none', 'none', 'active'], 91 | 'display-mode': ['fullscreen', 'standalone', 'minimal-ui', 'browser', 'window'], 92 | 'aspect-ratio': ['1/1', '16/9', '16/10', '4/3', '8/5', '5/4', '5/3', '3/2', '16/12', '3/4', '9/16', '10/16', '3/5', '2/3', '12/16'], 93 | 'resolution': ['300dpi', '2dppx', '3dppx'], 94 | 'prefers-color-scheme': ['dark', 'light', 'no-preference'], 95 | 'overflow': ['auto', 'hidden'], 96 | 'transform-3d': ['0', '1'], 97 | 'device-aspect-ratio': ['1/1', '16/9', '16/10', '4/3', '8/5', '5/4', '5/3', '3/2', '16/12', '3/4', '9/16', '10/16', '3/5', '2/3', '12/16'], 98 | 'device-height': ['640px', '768px', '1024px'], 99 | 'device-width': ['320px', '360px', '375px'], 100 | 'forced-color-adjust': ['none', 'auto'], 101 | 'orientation': ['portrait', 'landscape'], 102 | 'scan': ['progressive', 'interlace'] 103 | }; 104 | matchMedia = self.matchMedia; 105 | if (typeof matchMedia !== 'function') { 106 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-1)]; 107 | } 108 | matchMedia_mediaQueries = {}; 109 | try { 110 | for (_a = __values(Object.entries(mediaQueries)), _b = _a.next(); !_b.done; _b = _a.next()) { 111 | _c = __read(_b.value, 2), key = _c[0], value = _c[1]; 112 | try { 113 | for (value_1 = (e_2 = void 0, __values(value)), value_1_1 = value_1.next(); !value_1_1.done; value_1_1 = value_1.next()) { 114 | item = value_1_1.value; 115 | query = "(".concat(key, ": ").concat(item, ")"); 116 | try { 117 | result = window.matchMedia(query); 118 | matchMedia_mediaQueries[query] = result.matches; 119 | } 120 | catch (e) { 121 | matchMedia_mediaQueries[query] = { error: true }; 122 | ; 123 | } 124 | } 125 | } 126 | catch (e_2_1) { e_2 = { error: e_2_1 }; } 127 | finally { 128 | try { 129 | if (value_1_1 && !value_1_1.done && (_e = value_1["return"])) _e.call(value_1); 130 | } 131 | finally { if (e_2) throw e_2.error; } 132 | } 133 | } 134 | } 135 | catch (e_1_1) { e_1 = { error: e_1_1 }; } 136 | finally { 137 | try { 138 | if (_b && !_b.done && (_d = _a["return"])) _d.call(_a); 139 | } 140 | finally { if (e_1) throw e_1.error; } 141 | } 142 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(0, { matchMedia_mediaQueries: matchMedia_mediaQueries })]; 143 | }); 144 | }); }; 145 | exports.matchMedia = matchMedia; 146 | -------------------------------------------------------------------------------- /dist/components/screen/screen.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const screen: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/screen/screen.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | exports.__esModule = true; 39 | exports.screen = void 0; 40 | var interfaces_1 = require("../../utils/interfaces"); 41 | var utils_1 = require("../../utils/utils"); 42 | var screen = function (fingerprint) { return __awaiter(void 0, void 0, void 0, function () { 43 | var screen, output; 44 | var _a, _b, _c; 45 | return __generator(this, function (_d) { 46 | screen = window.screen; 47 | if (typeof screen !== 'object') 48 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-1)]; 49 | output = { 50 | screen_width: (0, utils_1.getSpecificTypeAndValue)(screen.width), 51 | screen_height: (0, utils_1.getSpecificTypeAndValue)(screen.height), 52 | screen_pixelDepth: (0, utils_1.getSpecificTypeAndValue)(screen.pixelDepth), 53 | screen_colorDepth: (0, utils_1.getSpecificTypeAndValue)(screen.colorDepth), 54 | screen_availWidth: (0, utils_1.getSpecificTypeAndValue)(screen.availWidth), 55 | screen_availHeight: (0, utils_1.getSpecificTypeAndValue)(screen.availHeight), 56 | screen_availTop: (0, utils_1.getSpecificTypeAndValue)((_a = screen.availTop) !== null && _a !== void 0 ? _a : undefined), 57 | screen_availLeft: (0, utils_1.getSpecificTypeAndValue)((_b = screen.availLeft) !== null && _b !== void 0 ? _b : undefined), 58 | screen_isExtended: (0, utils_1.getSpecificTypeAndValue)((_c = screen.isExtended) !== null && _c !== void 0 ? _c : undefined), 59 | screen_orientationType: (0, utils_1.getSpecificType)(screen.orientation) 60 | }; 61 | if (output.screen_orientationType === 'object') { 62 | output.screen_orientation = { 63 | angle: (0, utils_1.getSpecificTypeAndValue)(screen.orientation.angle), 64 | type: (0, utils_1.getSpecificTypeAndValue)(screen.orientation.type) 65 | }; 66 | } 67 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(0, output)]; 68 | }); 69 | }); }; 70 | exports.screen = screen; 71 | -------------------------------------------------------------------------------- /dist/components/timingResolution/timingResolution.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const timingResolution: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/timingResolution/timingResolution.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | exports.__esModule = true; 39 | exports.timingResolution = void 0; 40 | var interfaces_1 = require("../../utils/interfaces"); 41 | var timingResolution = function () { return __awaiter(void 0, void 0, void 0, function () { 42 | var p, smallestInterval, secondSmallestInterval, now, newNow, i, difference; 43 | return __generator(this, function (_a) { 44 | p = self.performance; 45 | if (p === undefined) 46 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-1)]; 47 | if (typeof p.now !== 'function') 48 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-2)]; 49 | smallestInterval = 1; 50 | secondSmallestInterval = 1; 51 | now = p.now(); 52 | newNow = now; 53 | for (i = 0; i < 5000; i++) { 54 | now = newNow; 55 | newNow = p.now(); 56 | if (now < newNow) { 57 | difference = newNow - now; 58 | if (difference > smallestInterval) { 59 | if (difference < secondSmallestInterval) { 60 | secondSmallestInterval = difference; 61 | } 62 | } 63 | else if (difference < smallestInterval) { 64 | secondSmallestInterval = smallestInterval; 65 | smallestInterval = difference; 66 | } 67 | } 68 | } 69 | // Safari (and maybe other non-chromium browsers) may sometimes return a non-persistent value for the smallest interval. 70 | // In this case, we set the smallest interval to 1 to correct the issue if the second smallest interval is also 1. 71 | // In a normal Chromium environment excluding Brave's protections, neither of these intervals should ever be 1. 72 | if (secondSmallestInterval === 1) 73 | smallestInterval = 1; 74 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(0, { 75 | timingResolution_smallestInterval: smallestInterval, 76 | timingResolution_secondSmallestInterval: secondSmallestInterval 77 | })]; 78 | }); 79 | }); }; 80 | exports.timingResolution = timingResolution; 81 | -------------------------------------------------------------------------------- /dist/components/userAgentData/userAgentData.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOutputInterface } from '../../utils/interfaces'; 2 | export declare const userAgentData: (fingerprint: any) => Promise; 3 | -------------------------------------------------------------------------------- /dist/components/userAgentData/userAgentData.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __values = (this && this.__values) || function(o) { 39 | var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; 40 | if (m) return m.call(o); 41 | if (o && typeof o.length === "number") return { 42 | next: function () { 43 | if (o && i >= o.length) o = void 0; 44 | return { value: o && o[i++], done: !o }; 45 | } 46 | }; 47 | throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); 48 | }; 49 | exports.__esModule = true; 50 | exports.userAgentData = void 0; 51 | var interfaces_1 = require("../../utils/interfaces"); 52 | var utils_1 = require("../../utils/utils"); 53 | var parseBrand = function (arr) { 54 | var brands = []; 55 | if (!arr) 56 | return []; 57 | for (var i = 0; i < arr.length; i++) { 58 | var item = arr[i]; 59 | if (item.brand) { 60 | var brand = item.brand; 61 | if (!new RegExp('Brand', 'i').test(brand)) { 62 | brands.push(brand); 63 | } 64 | } 65 | } 66 | return brands.sort(); 67 | }; 68 | var userAgentData = function (fingerprint) { return __awaiter(void 0, void 0, void 0, function () { 69 | var userAgentData, highEntropyKeys, promises, _loop_1, highEntropyKeys_1, highEntropyKeys_1_1, key; 70 | var e_1, _a; 71 | return __generator(this, function (_b) { 72 | userAgentData = navigator.userAgentData; 73 | if (!userAgentData) 74 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-1)]; 75 | if (typeof userAgentData.getHighEntropyValues !== 'function') 76 | return [2 /*return*/, (0, interfaces_1.resolveComponent)(-2)]; 77 | highEntropyKeys = [ 78 | 'architecture', 79 | 'bitness', 80 | 'brands', 81 | 'formFactor', 82 | 'fullVersionList', 83 | 'mobile', 84 | 'model', 85 | 'platform', 86 | 'platformVersion', 87 | 'uaFullVersion', 88 | 'wow64', 89 | ]; 90 | promises = []; 91 | _loop_1 = function (key) { 92 | try { 93 | promises.push(userAgentData.getHighEntropyValues([key]).then(function (value) { 94 | var _a; 95 | return _a = {}, _a[key] = value, _a; 96 | })); 97 | } 98 | catch (e) { 99 | promises.push(Promise.resolve({ error: true })); 100 | } 101 | }; 102 | try { 103 | for (highEntropyKeys_1 = __values(highEntropyKeys), highEntropyKeys_1_1 = highEntropyKeys_1.next(); !highEntropyKeys_1_1.done; highEntropyKeys_1_1 = highEntropyKeys_1.next()) { 104 | key = highEntropyKeys_1_1.value; 105 | _loop_1(key); 106 | } 107 | } 108 | catch (e_1_1) { e_1 = { error: e_1_1 }; } 109 | finally { 110 | try { 111 | if (highEntropyKeys_1_1 && !highEntropyKeys_1_1.done && (_a = highEntropyKeys_1["return"])) _a.call(highEntropyKeys_1); 112 | } 113 | finally { if (e_1) throw e_1.error; } 114 | } 115 | return [2 /*return*/, Promise.all(promises).then(function (values) { 116 | var e_2, _a; 117 | var output = {}; 118 | var _loop_2 = function (value) { 119 | var key = Object.keys(value)[0]; 120 | var highEntropyValues = value[key]; 121 | highEntropyValues.brands = 122 | (0, utils_1.getSpecificType)(highEntropyValues.brands) === 'array' 123 | ? parseBrand(highEntropyValues.brands) 124 | : highEntropyValues.brands; 125 | (function () { 126 | var e_3, _a; 127 | if (Array.isArray(highEntropyValues.fullVersionList)) { 128 | var sortedList = {}; 129 | try { 130 | for (var _b = (e_3 = void 0, __values(highEntropyValues.fullVersionList)), _c = _b.next(); !_c.done; _c = _b.next()) { 131 | var item = _c.value; 132 | if (!new RegExp('Brand', 'i').test(item.brand)) { 133 | sortedList[item.brand] = item.version; 134 | } 135 | } 136 | } 137 | catch (e_3_1) { e_3 = { error: e_3_1 }; } 138 | finally { 139 | try { 140 | if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); 141 | } 142 | finally { if (e_3) throw e_3.error; } 143 | } 144 | highEntropyValues.fullVersionList = sortedList; 145 | } 146 | })(); 147 | for (var k in highEntropyValues) { 148 | if (Object.prototype.hasOwnProperty.call(highEntropyValues, k)) { 149 | output['userAgentData_' + k] = (0, utils_1.getSpecificTypeAndValue)(highEntropyValues[k]); 150 | } 151 | else { 152 | output['userAgentData_' + k] = { type: 'undefined' }; 153 | } 154 | } 155 | }; 156 | try { 157 | for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) { 158 | var value = values_1_1.value; 159 | _loop_2(value); 160 | } 161 | } 162 | catch (e_2_1) { e_2 = { error: e_2_1 }; } 163 | finally { 164 | try { 165 | if (values_1_1 && !values_1_1.done && (_a = values_1["return"])) _a.call(values_1); 166 | } 167 | finally { if (e_2) throw e_2.error; } 168 | } 169 | return (0, interfaces_1.resolveComponent)(0, output); 170 | })]; 171 | }); 172 | }); }; 173 | exports.userAgentData = userAgentData; 174 | -------------------------------------------------------------------------------- /dist/es5/opjs.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * OverpoweredJS OS v0.0.1 4 | * 5 | * https://github.com/Joe12387/overpoweredjs 6 | * 7 | * Copyright (c) 2022 - 2024 Joe Rutkowski 8 | * 9 | * Released under the OverpoweredJS OS License 10 | * 11 | * This software is subject to very specific licensing terms. 12 | * You should read them before using this software in any capacity. 13 | * 14 | * DO NOT: 15 | * - Remove this banner. 16 | * - Use this software for any commercial purposes. 17 | * - Use this software to track users without their consent. 18 | * 19 | * Please see the LICENSE.md file for more information. 20 | * If you don't have a LICENSE.md file, see the above URL. 21 | * 22 | * Removing this banner is considered a violation of the OverpoweredJS OS License. 23 | * 24 | **/ 25 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.opjs=t():e.opjs=t()}(this,(function(){return function(){"use strict";var e={9:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{u(r.next(e))}catch(e){i(e)}}function c(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(c){return function(u){return function(c){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,c[0]&&(a=0)),a;)try{if(n=1,r&&(o=2&c[0]?r.return:c[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,c[1])).done)return o;switch(r=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return a.label++,{value:c[1],done:!1};case 5:a.label++,r=c[1],c=[0];continue;case 7:c=a.ops.pop(),a.trys.pop();continue;default:if(!(o=a.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a};Object.defineProperty(t,"__esModule",{value:!0}),t.matchMedia=void 0;var c=n(435);t.matchMedia=function(e){return r(void 0,void 0,void 0,(function(){var e,t,n,r,u,l,f,s,p,h,d,v,y,g,b,m;return o(this,(function(o){if(e={"prefers-contrast":["high","more","low","less","forced","no-preference","high-contrast","low-contrast"],"color-gamut":["rec2020","p3","srgb","display-p3","a98rgb"],"dynamic-range":["high","standard","hdr","sdr"],"video-dynamic-range":["high","standard","hdr","sdr"],"any-hover":["hover","none","on-demand"],"any-pointer":["none","coarse","fine","hover"],pointer:["none","coarse","fine","hover"],hover:["hover","none","on-demand"],update:["fast","slow","none"],"overflow-block":["scroll","none","optional-paged","paged","optional-paged-x","optional-paged-y"],"overflow-inline":["scroll","none","optional-paged","paged","optional-paged-x","optional-paged-y"],color:["8","16","256","4k","8k"],"inverted-colors":["inverted","none","no-preference"],"prefers-reduced-motion":["reduce","no-preference","motion"],"prefers-reduced-transparency":["reduce","no-preference","transparency"],grid:["0","1","2"],scripting:["none","initial-only","enabled","enabled-only"],"forced-colors":["active","none","none","active"],"display-mode":["fullscreen","standalone","minimal-ui","browser","window"],"aspect-ratio":["1/1","16/9","16/10","4/3","8/5","5/4","5/3","3/2","16/12","3/4","9/16","10/16","3/5","2/3","12/16"],resolution:["300dpi","2dppx","3dppx"],"prefers-color-scheme":["dark","light","no-preference"],overflow:["auto","hidden"],"transform-3d":["0","1"],"device-aspect-ratio":["1/1","16/9","16/10","4/3","8/5","5/4","5/3","3/2","16/12","3/4","9/16","10/16","3/5","2/3","12/16"],"device-height":["640px","768px","1024px"],"device-width":["320px","360px","375px"],"forced-color-adjust":["none","auto"],orientation:["portrait","landscape"],scan:["progressive","interlace"]},"function"!=typeof self.matchMedia)return[2,(0,c.resolveComponent)(-1)];t={};try{for(n=i(Object.entries(e)),r=n.next();!r.done;r=n.next()){u=a(r.value,2),l=u[0],f=u[1];try{for(b=void 0,s=i(f),p=s.next();!p.done;p=s.next()){h=p.value,d="(".concat(l,": ").concat(h,")");try{v=window.matchMedia(d),t[d]=v.matches}catch(e){t[d]={error:!0}}}}catch(e){b={error:e}}finally{try{p&&!p.done&&(m=s.return)&&m.call(s)}finally{if(b)throw b.error}}}}catch(e){y={error:e}}finally{try{r&&!r.done&&(g=n.return)&&g.call(n)}finally{if(y)throw y.error}}return[2,(0,c.resolveComponent)(0,{matchMedia_mediaQueries:t})]}))}))}},403:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{u(r.next(e))}catch(e){i(e)}}function c(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(c){return function(u){return function(c){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,c[0]&&(a=0)),a;)try{if(n=1,r&&(o=2&c[0]?r.return:c[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,c[1])).done)return o;switch(r=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return a.label++,{value:c[1],done:!1};case 5:a.label++,r=c[1],c=[0];continue;case 7:c=a.ops.pop(),a.trys.pop();continue;default:if(!(o=a.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]t?u0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0}),t.userAgentData=void 0;var a=n(435),c=n(450);t.userAgentData=function(e){return r(void 0,void 0,void 0,(function(){var e,t,n,r,u,l,f,s,p;return o(this,(function(o){if(!(e=navigator.userAgentData))return[2,(0,a.resolveComponent)(-1)];if("function"!=typeof e.getHighEntropyValues)return[2,(0,a.resolveComponent)(-2)];t=["architecture","bitness","brands","formFactor","fullVersionList","mobile","model","platform","platformVersion","uaFullVersion","wow64"],n=[],r=function(t){try{n.push(e.getHighEntropyValues([t]).then((function(e){var n;return(n={})[t]=e,n})))}catch(e){n.push(Promise.resolve({error:!0}))}};try{for(u=i(t),l=u.next();!l.done;l=u.next())f=l.value,r(f)}catch(e){s={error:e}}finally{try{l&&!l.done&&(p=u.return)&&p.call(u)}finally{if(s)throw s.error}}return[2,Promise.all(n).then((function(e){var t,n,r={},o=function(e){var t=e[Object.keys(e)[0]];for(var n in t.brands="array"===(0,c.getSpecificType)(t.brands)?function(e){var t=[];if(!e)return[];for(var n=0;n0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=0){var i=e.value,a=e.type;if("object"===a){for(var f in i)if(Object.prototype.hasOwnProperty.call(i,f)){var s=f;if(l){var p=(0,c.hash)(s).toString(36);n[p]=(0,c.hash)((0,c.hash)((0,u.stringify)(i[f])).toString(36),(0,c.hash)((0,c.hash)(a).toString(36),(0,c.hash)(p,r))).toString(36)}else n[s]=i[f]}}else{var h=t+"_value",d=t+"_type";if(l){var v=(0,c.hash)(h).toString(36);n[t][v]=(0,c.hash)((0,c.hash)((0,u.stringify)(i)).toString(36),(0,c.hash)((0,c.hash)(a).toString(36),(0,c.hash)(v,r))).toString(36);var y=(0,c.hash)(d).toString(36);n[t][y]=(0,c.hash)((0,c.hash)((0,u.stringify)(i)).toString(36),(0,c.hash)((0,c.hash)(a).toString(36),(0,c.hash)(y,r))).toString(36)}else n[h]=i,n[d]=a}}return n}function s(e,t,n,r){var o=new Promise((function(e,o){return setTimeout((function(){return o(new Error("OverpoweredJS: ".concat(n," - Context: ").concat(r)))}),t)}));return Promise.race([e,o])}function p(e,t,n){console.error("OverpoweredJS: Error in ".concat(n," (").concat(t,"): ").concat(e.message))}function h(){return o(this,void 0,Promise,(function(){return i(this,(function(e){return[2,new Promise((function(e,t){var n={};"object"==typeof self.window&&null!==self.window&&"object"==typeof self.window.document&&null!==self.window.document||t(new Error("OverpoweredJS: window and/or document object not found. Only run in a browser environment. Other contexts are not supported."));var o=2e3,i=a.fingerprintStage1.map((function(e){return{promise:s(e.func(n).catch((function(t){throw p(t,"stage 1",e.name),t})),o,"OverpoweredJS: ".concat(e.name," (stage 1) timed out"),"Stage 1 - ".concat(e.name)),name:e.name}}));return Promise.all(i.map((function(e){return e.promise}))).then((function(c){c.forEach((function(e,t){var r=i[t].name;n[r]=e}));var u=a.fingerprintStage2.map((function(e){return{promise:s(e.func(n).catch((function(t){throw p(t,"stage 2",e.name),t})),o,"OverpoweredJS: ".concat(e.name," (stage 2) timed out"),"Stage 2 - ".concat(e.name)),name:e.name}}));return Promise.all(u.map((function(e){return e.promise}))).then((function(i){i.forEach((function(e,t){var r=u[t].name;n[r]=e}));var c=a.fingerprintStage3.map((function(e){return{promise:s(e.func(n).catch((function(t){throw p(t,"stage 3",e.name),t})),o,"OverpoweredJS: ".concat(e.name," (stage 3) timed out"),"Stage 3 - ".concat(e.name)),name:e.name}}));return Promise.all(c.map((function(e){return e.promise}))).then((function(t){t.forEach((function(e,t){var r=c[t].name;n[r]=e}));e({getFingerprint:function(){return function(e){var t=f(e.brave,"brave"),n=f(e.screen,"screen"),o=f(e.userAgentData,"userAgentData"),i=f(e.jsHeapSizeLimit,"jsHeapSizeLimit"),a=f(e.devicePixelRatio,"devicePixelRatio"),c=f(e.timingResolution,"timingResolution"),u=f(e.getBattery,"getBattery"),l=f(e.matchMedia,"matchMedia");return r(r(r(r(r(r(r(r({},t),n),o),i),a),c),u),l)}(n)}})})).catch((function(e){p(e,"stage 3","unknown"),t(new Error("OverpoweredJS: Error in stage 3: ".concat(e.message)))}))})).catch((function(e){p(e,"stage 2","unknown"),t(new Error("OverpoweredJS: Error in stage 2: ".concat(e.message)))}))})).catch((function(e){p(e,"stage 1","unknown"),t(new Error("OverpoweredJS: Error in stage 1: ".concat(e.message)))}))}))]}))}))}t.opjs=h,"undefined"!=typeof window&&(self.window.opjs=h),self.window.opjs().then((function(e){return console.log(e.getFingerprint())})).catch(console.error),t.default=h},963:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.hash=void 0;t.hash=function(e,t){return void 0===t&&(t=420),function(e,t){void 0===t&&(t=0);for(var n=3735928559^t,r=1103547991^t,o=0,i=void 0;o>>15,1935289751),r^=Math.imul(r^n>>>15,3405138345),2097152*((r^=(n^=r>>>16)>>>16)>>>0)+(n>>>11)}(e,t)}},435:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.resolveComponent=void 0;var r=n(450);t.resolveComponent=function(e,t){if("object"==typeof t&&null!==t){if("status"in t)throw new Error("Status already exists on the object");for(var n in t)"object"==typeof t[n]&&null!==t[n]&&(t[n].status=e)}return{status:e,value:t,type:(0,r.getSpecificType)(t)}}},450:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.stringify=t.getSpecificTypeAndValue=t.getSpecificType=void 0;t.getSpecificType=function(e){var t=typeof e;if("object"===t){if(null===e)return"null";if(Array.isArray(e))return"array"}if("number"===t){if(e===1/0)return"infinity";if(e===-1/0)return"-infinity";if(isNaN(e))return"nan"}return t};t.getSpecificTypeAndValue=function(e){return{type:(0,t.getSpecificType)(e),value:e}};t.stringify=function(e){if("object"==typeof JSON&&"function"==typeof JSON.stringify)return JSON.stringify(e);function n(e){return'"'+e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'}return null===e?"null":"number"==typeof e||"boolean"==typeof e?String(e):"string"==typeof e?n(e):void 0===e?"null":Array.isArray(e)?"["+e.map((function(e){return void 0===e?"null":(0,t.stringify)(e)})).join(",")+"]":"object"==typeof e?"{"+Object.keys(e).filter((function(t){return void 0!==e[t]})).map((function(r){return n(r)+":"+(0,t.stringify)(e[r])})).join(",")+"}":"null"}}},t={};var n=function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={exports:{}};return e[r].call(i.exports,i,i.exports,n),i.exports}(149);return n=n.default}()})); -------------------------------------------------------------------------------- /dist/global.t.d.ts: -------------------------------------------------------------------------------- 1 | interface Navigator { 2 | getBattery?: () => Promise; 3 | } 4 | interface BatteryManager { 5 | level: number; 6 | charging: boolean; 7 | chargingTime: number; 8 | dischargingTime: number; 9 | } 10 | -------------------------------------------------------------------------------- /dist/global.t.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | -------------------------------------------------------------------------------- /dist/opjs.d.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * OverpoweredJS v0.0.1 4 | * 5 | * https://github.com/Joe12387/overpoweredjs 6 | * 7 | * 8 | **/ 9 | declare global { 10 | interface Window { 11 | opjs: typeof opjs; 12 | } 13 | interface Navigator { 14 | brave?: object; 15 | userAgentData?: { 16 | getHighEntropyValues: (keys: string[]) => Promise<{ 17 | brands: string[]; 18 | }>; 19 | }; 20 | } 21 | } 22 | type GetFingerprintFunction = () => unknown; 23 | export declare function opjs(): Promise<{ 24 | getFingerprint: GetFingerprintFunction; 25 | }>; 26 | export default opjs; 27 | -------------------------------------------------------------------------------- /dist/opjs.esm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * OverpoweredJS OS v0.0.1 4 | * 5 | * https://github.com/Joe12387/overpoweredjs 6 | * 7 | * Copyright (c) 2022 - 2024 Joe Rutkowski 8 | * 9 | * Released under the OverpoweredJS OS License 10 | * 11 | * This software is subject to very specific licensing terms. 12 | * You should read them before using this software in any capacity. 13 | * 14 | * DO NOT: 15 | * - Remove this banner. 16 | * - Use this software for any commercial purposes. 17 | * - Use this software to track users without their consent. 18 | * 19 | * Please see the LICENSE.md file for more information. 20 | * If you don't have a LICENSE.md file, see the above URL. 21 | * 22 | * Removing this banner is considered a violation of the OverpoweredJS OS License. 23 | * 24 | **/ 25 | var e={d:(n,t)=>{for(var r in t)e.o(t,r)&&!e.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:t[r]})},o:(e,n)=>Object.prototype.hasOwnProperty.call(e,n)},n={};e.d(n,{A:()=>H,$:()=>G});var t=function(e){var n=typeof e;if("object"===n){if(null===e)return"null";if(Array.isArray(e))return"array"}if("number"===n){if(e===1/0)return"infinity";if(e===-1/0)return"-infinity";if(isNaN(e))return"nan"}return n},r=function(e){return{type:t(e),value:e}},o=function(e){if("object"==typeof JSON&&"function"==typeof JSON.stringify)return JSON.stringify(e);function n(e){return'"'+e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'}return null===e?"null":"number"==typeof e||"boolean"==typeof e?String(e):"string"==typeof e?n(e):void 0===e?"null":Array.isArray(e)?"["+e.map((function(e){return void 0===e?"null":o(e)})).join(",")+"]":"object"==typeof e?"{"+Object.keys(e).filter((function(n){return void 0!==e[n]})).map((function(t){return n(t)+":"+o(e[t])})).join(",")+"}":"null"},i=function(e,n){if("object"==typeof n&&null!==n){if("status"in n)throw new Error("Status already exists on the object");for(var r in n)"object"==typeof n[r]&&null!==n[r]&&(n[r].status=e)}return{status:e,value:n,type:t(n)}},a=function(e,n,t,r){return new(t||(t=Promise))((function(o,i){function a(e){try{u(r.next(e))}catch(e){i(e)}}function c(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var n;e.done?o(e.value):(n=e.value,n instanceof t?n:new t((function(e){e(n)}))).then(a,c)}u((r=r.apply(e,n||[])).next())}))},c=function(e,n){var t,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(c){return function(u){return function(c){if(t)throw new TypeError("Generator is already executing.");for(;i&&(i=0,c[0]&&(a=0)),a;)try{if(t=1,r&&(o=2&c[0]?r.return:c[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,c[1])).done)return o;switch(r=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return a.label++,{value:c[1],done:!1};case 5:a.label++,r=c[1],c=[0];continue;case 7:c=a.ops.pop(),a.trys.pop();continue;default:if(!(o=a.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")},h=function(e,n,t,r){return new(t||(t=Promise))((function(o,i){function a(e){try{u(r.next(e))}catch(e){i(e)}}function c(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var n;e.done?o(e.value):(n=e.value,n instanceof t?n:new t((function(e){e(n)}))).then(a,c)}u((r=r.apply(e,n||[])).next())}))},d=function(e,n){var t,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(c){return function(u){return function(c){if(t)throw new TypeError("Generator is already executing.");for(;i&&(i=0,c[0]&&(a=0)),a;)try{if(t=1,r&&(o=2&c[0]?r.return:c[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,c[1])).done)return o;switch(r=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return a.label++,{value:c[1],done:!1};case 5:a.label++,r=c[1],c=[0];continue;case 7:c=a.ops.pop(),a.trys.pop();continue;default:if(!(o=a.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")},j=function(e,n){var t="function"==typeof Symbol&&e[Symbol.iterator];if(!t)return e;var r,o,i=t.call(e),a=[];try{for(;(void 0===n||n-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(t=i.return)&&t.call(i)}finally{if(o)throw o.error}}return a},E=[],O=[],P=[];function _(e,n,t){t.push({name:n,func:e})}_((function(){return a(void 0,void 0,void 0,(function(){var e,n,t,r;return c(this,(function(o){return null==(e=navigator)?[2,i(-1)]:(n=["object"==typeof e.brave,"brave"in navigator,"object"==typeof e.brave&&"Brave"==Object.getPrototypeOf(navigator.brave).constructor.name,"object"==typeof e.brave&&"function"==typeof e.brave.isBrave&&"function isBrave() { [native code] }"===e.brave.isBrave.toString()],t=n.includes(!0),r=!n.includes(!1),[2,i(0,{brave:{likeBrave:t,isBrave:r,mode:-1,braveClues:n}})])}))}))}),"brave",E),_((function(){return u(void 0,void 0,void 0,(function(){var e,n,t,r,o,a,c;return l(this,(function(u){if(void 0===(e=self.performance))return[2,i(-1)];if("function"!=typeof e.now)return[2,i(-2)];for(n=1,t=1,r=e.now(),o=r,a=0;a<5e3;a++)r=o,o=e.now(),rn?c>>15,1935289751),r^=Math.imul(r^t>>>15,3405138345),2097152*((r^=(t^=r>>>16)>>>16)>>>0)+(t>>>11)}(e,n)},B=function(){return B=Object.assign||function(e){for(var n,t=1,r=arguments.length;t0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=0){var a=e.value,c=e.type;if("object"===c){for(var u in a)if(Object.prototype.hasOwnProperty.call(a,u)){var l=u;if(R){var f=T(l).toString(36);t[f]=T(T(o(a[u])).toString(36),T(T(c).toString(36),T(f,r))).toString(36)}else t[l]=a[u]}}else{var s=n+"_value",p=n+"_type";if(R){var h=T(s).toString(36);t[n][h]=T(T(o(a)).toString(36),T(T(c).toString(36),T(h,r))).toString(36);var d=T(p).toString(36);t[n][d]=T(T(o(a)).toString(36),T(T(c).toString(36),T(d,r))).toString(36)}else t[s]=a,t[p]=c}}return t}function D(e,n,t,r){var o=new Promise((function(e,o){return setTimeout((function(){return o(new Error("OverpoweredJS: ".concat(t," - Context: ").concat(r)))}),n)}));return Promise.race([e,o])}function M(e,n,t){console.error("OverpoweredJS: Error in ".concat(t," (").concat(n,"): ").concat(e.message))}function G(){return A(this,void 0,Promise,(function(){return J(this,(function(e){return[2,new Promise((function(e,n){var t={};"object"==typeof self.window&&null!==self.window&&"object"==typeof self.window.document&&null!==self.window.document||n(new Error("OverpoweredJS: window and/or document object not found. Only run in a browser environment. Other contexts are not supported."));var r=2e3,o=E.map((function(e){return{promise:D(e.func(t).catch((function(n){throw M(n,"stage 1",e.name),n})),r,"OverpoweredJS: ".concat(e.name," (stage 1) timed out"),"Stage 1 - ".concat(e.name)),name:e.name}}));return Promise.all(o.map((function(e){return e.promise}))).then((function(i){i.forEach((function(e,n){var r=o[n].name;t[r]=e}));var a=O.map((function(e){return{promise:D(e.func(t).catch((function(n){throw M(n,"stage 2",e.name),n})),r,"OverpoweredJS: ".concat(e.name," (stage 2) timed out"),"Stage 2 - ".concat(e.name)),name:e.name}}));return Promise.all(a.map((function(e){return e.promise}))).then((function(o){o.forEach((function(e,n){var r=a[n].name;t[r]=e}));var i=P.map((function(e){return{promise:D(e.func(t).catch((function(n){throw M(n,"stage 3",e.name),n})),r,"OverpoweredJS: ".concat(e.name," (stage 3) timed out"),"Stage 3 - ".concat(e.name)),name:e.name}}));return Promise.all(i.map((function(e){return e.promise}))).then((function(n){n.forEach((function(e,n){var r=i[n].name;t[r]=e}));e({getFingerprint:function(){return function(e){var n=L(e.brave,"brave"),t=L(e.screen,"screen"),r=L(e.userAgentData,"userAgentData"),o=L(e.jsHeapSizeLimit,"jsHeapSizeLimit"),i=L(e.devicePixelRatio,"devicePixelRatio"),a=L(e.timingResolution,"timingResolution"),c=L(e.getBattery,"getBattery"),u=L(e.matchMedia,"matchMedia");return B(B(B(B(B(B(B(B({},n),t),r),o),i),a),c),u)}(t)}})})).catch((function(e){M(e,"stage 3","unknown"),n(new Error("OverpoweredJS: Error in stage 3: ".concat(e.message)))}))})).catch((function(e){M(e,"stage 2","unknown"),n(new Error("OverpoweredJS: Error in stage 2: ".concat(e.message)))}))})).catch((function(e){M(e,"stage 1","unknown"),n(new Error("OverpoweredJS: Error in stage 1: ".concat(e.message)))}))}))]}))}))}"undefined"!=typeof window&&(self.window.opjs=G),self.window.opjs().then((function(e){return console.log(e.getFingerprint())})).catch(console.error);const H=G;var V=n.A,z=n.$;export{V as default,z as opjs}; -------------------------------------------------------------------------------- /dist/opjs.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /*! 3 | * 4 | * OverpoweredJS v0.0.1 5 | * 6 | * https://github.com/Joe12387/overpoweredjs 7 | * 8 | * 9 | **/ 10 | var __assign = (this && this.__assign) || function () { 11 | __assign = Object.assign || function(t) { 12 | for (var s, i = 1, n = arguments.length; i < n; i++) { 13 | s = arguments[i]; 14 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 15 | t[p] = s[p]; 16 | } 17 | return t; 18 | }; 19 | return __assign.apply(this, arguments); 20 | }; 21 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 22 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 23 | return new (P || (P = Promise))(function (resolve, reject) { 24 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 25 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 26 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 27 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 28 | }); 29 | }; 30 | var __generator = (this && this.__generator) || function (thisArg, body) { 31 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 32 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 33 | function verb(n) { return function (v) { return step([n, v]); }; } 34 | function step(op) { 35 | if (f) throw new TypeError("Generator is already executing."); 36 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 37 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 38 | if (y = 0, t) op = [op[0] & 2, t.value]; 39 | switch (op[0]) { 40 | case 0: case 1: t = op; break; 41 | case 4: _.label++; return { value: op[1], done: false }; 42 | case 5: _.label++; y = op[1]; op = [0]; continue; 43 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 44 | default: 45 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 46 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 47 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 48 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 49 | if (t[2]) _.ops.pop(); 50 | _.trys.pop(); continue; 51 | } 52 | op = body.call(thisArg, _); 53 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 54 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 55 | } 56 | }; 57 | exports.__esModule = true; 58 | exports.opjs = void 0; 59 | var components_1 = require("./components"); 60 | var hash_1 = require("./utils/hash"); 61 | var utils_1 = require("./utils/utils"); 62 | var isOpaque = false; // vestigial from CS Edition, may be used in future versions of OS Edition 63 | function processModule(module, name) { 64 | var processedModule = {}; 65 | processedModule[name] = {}; 66 | var status = module['status']; 67 | var status_identifier = 'status'; 68 | if (isOpaque) { 69 | processedModule[name][status_identifier] = status; 70 | } 71 | else { 72 | if (typeof processedModule[name][status_identifier] !== 'undefined') { 73 | throw new Error('Status already exists on the object'); 74 | } 75 | processedModule[name][status_identifier] = status; 76 | } 77 | if (status >= 0) { 78 | var value = module['value']; 79 | var type = module['type']; 80 | if (type === 'object') { 81 | for (var key in value) { 82 | if (Object.prototype.hasOwnProperty.call(value, key)) { 83 | var identifier = key; 84 | if (isOpaque) { 85 | var keyHash = (0, hash_1.hash)(identifier).toString(36); 86 | processedModule[keyHash] = (0, hash_1.hash)((0, hash_1.hash)((0, utils_1.stringify)(value[key])).toString(36), (0, hash_1.hash)((0, hash_1.hash)(type).toString(36), (0, hash_1.hash)(keyHash, status))).toString(36); 87 | } 88 | else { 89 | processedModule[identifier] = value[key]; 90 | } 91 | } 92 | } 93 | } 94 | else { 95 | var value_identifier = name + '_value'; 96 | var type_identifier = name + '_type'; 97 | if (isOpaque) { 98 | var value_keyHash = (0, hash_1.hash)(value_identifier).toString(36); 99 | processedModule[name][value_keyHash] = (0, hash_1.hash)((0, hash_1.hash)((0, utils_1.stringify)(value)).toString(36), (0, hash_1.hash)((0, hash_1.hash)(type).toString(36), (0, hash_1.hash)(value_keyHash, status))).toString(36); 100 | var type_keyHash = (0, hash_1.hash)(type_identifier).toString(36); 101 | processedModule[name][type_keyHash] = (0, hash_1.hash)((0, hash_1.hash)((0, utils_1.stringify)(value)).toString(36), (0, hash_1.hash)((0, hash_1.hash)(type).toString(36), (0, hash_1.hash)(type_keyHash, status))).toString(36); 102 | } 103 | else { 104 | processedModule[value_identifier] = value; 105 | processedModule[type_identifier] = type; 106 | } 107 | } 108 | } 109 | return processedModule; 110 | } 111 | function processFingerprint(fingerprint) { 112 | var brave = processModule(fingerprint['brave'], 'brave'); 113 | var screen = processModule(fingerprint['screen'], 'screen'); 114 | var userAgentData = processModule(fingerprint['userAgentData'], 'userAgentData'); 115 | var jsHeapSizeLimit = processModule(fingerprint['jsHeapSizeLimit'], 'jsHeapSizeLimit'); 116 | var devicePixelRatio = processModule(fingerprint['devicePixelRatio'], 'devicePixelRatio'); 117 | var timingResolution = processModule(fingerprint['timingResolution'], 'timingResolution'); 118 | var getBattery = processModule(fingerprint['getBattery'], 'getBattery'); 119 | var matchMedia = processModule(fingerprint['matchMedia'], 'matchMedia'); 120 | var processedFingerprint = __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({}, brave), screen), userAgentData), jsHeapSizeLimit), devicePixelRatio), timingResolution), getBattery), matchMedia); 121 | return processedFingerprint; 122 | } 123 | function withTimeout(promise, ms, error, context) { 124 | var timeout = new Promise(function (_, reject) { 125 | return setTimeout(function () { return reject(new Error("OverpoweredJS: ".concat(error, " - Context: ").concat(context))); }, ms); 126 | }); 127 | return Promise.race([promise, timeout]); 128 | } 129 | function handleError(error, stage, componentName) { 130 | console.error("OverpoweredJS: Error in ".concat(componentName, " (").concat(stage, "): ").concat(error.message)); 131 | } 132 | function opjs() { 133 | return __awaiter(this, void 0, void 0, function () { 134 | return __generator(this, function (_a) { 135 | return [2 /*return*/, new Promise(function (resolve, reject) { 136 | var fingerprint = {}; 137 | if (typeof self.window !== 'object' || self.window === null || typeof self.window.document !== 'object' || self.window.document === null) { 138 | reject(new Error('OverpoweredJS: window and/or document object not found. Only run in a browser environment. Other contexts are not supported.')); 139 | } 140 | var timeoutMs = 2000; // 2 seconds 141 | var fingerprintStage1Promises = components_1.fingerprintStage1.map(function (component) { return ({ 142 | promise: withTimeout(component.func(fingerprint)["catch"](function (error) { 143 | handleError(error, 'stage 1', component.name); 144 | throw error; 145 | }), timeoutMs, "OverpoweredJS: ".concat(component.name, " (stage 1) timed out"), "Stage 1 - ".concat(component.name)), 146 | name: component.name 147 | }); }); 148 | return Promise.all(fingerprintStage1Promises.map(function (p) { return p.promise; })) 149 | .then(function (results) { 150 | results.forEach(function (result, index) { 151 | var name = fingerprintStage1Promises[index].name; 152 | fingerprint[name] = result; 153 | }); 154 | var fingerprintStage2Promises = components_1.fingerprintStage2.map(function (component) { return ({ 155 | promise: withTimeout(component.func(fingerprint)["catch"](function (error) { 156 | handleError(error, 'stage 2', component.name); 157 | throw error; 158 | }), timeoutMs, "OverpoweredJS: ".concat(component.name, " (stage 2) timed out"), "Stage 2 - ".concat(component.name)), 159 | name: component.name 160 | }); }); 161 | return Promise.all(fingerprintStage2Promises.map(function (p) { return p.promise; })) 162 | .then(function (results) { 163 | results.forEach(function (result, index) { 164 | var name = fingerprintStage2Promises[index].name; 165 | fingerprint[name] = result; 166 | }); 167 | var fingerprintStage3Promises = components_1.fingerprintStage3.map(function (component) { return ({ 168 | promise: withTimeout(component.func(fingerprint)["catch"](function (error) { 169 | handleError(error, 'stage 3', component.name); 170 | throw error; 171 | }), timeoutMs, "OverpoweredJS: ".concat(component.name, " (stage 3) timed out"), "Stage 3 - ".concat(component.name)), 172 | name: component.name 173 | }); }); 174 | return Promise.all(fingerprintStage3Promises.map(function (p) { return p.promise; })) 175 | .then(function (results) { 176 | results.forEach(function (result, index) { 177 | var name = fingerprintStage3Promises[index].name; 178 | fingerprint[name] = result; 179 | }); 180 | var getFingerprint = function () { return processFingerprint(fingerprint); }; 181 | resolve({ getFingerprint: getFingerprint }); 182 | })["catch"](function (error) { 183 | handleError(error, 'stage 3', 'unknown'); 184 | reject(new Error("OverpoweredJS: Error in stage 3: ".concat(error.message))); 185 | }); 186 | })["catch"](function (error) { 187 | handleError(error, 'stage 2', 'unknown'); 188 | reject(new Error("OverpoweredJS: Error in stage 2: ".concat(error.message))); 189 | }); 190 | })["catch"](function (error) { 191 | handleError(error, 'stage 1', 'unknown'); 192 | reject(new Error("OverpoweredJS: Error in stage 1: ".concat(error.message))); 193 | }); 194 | })]; 195 | }); 196 | }); 197 | } 198 | exports.opjs = opjs; 199 | if (typeof window !== 'undefined') { 200 | self.window.opjs = opjs; 201 | } 202 | // for debugging purposes only 203 | self.window 204 | .opjs() 205 | .then(function (fp) { return console.log(fp.getFingerprint()); })["catch"](console.error); 206 | exports["default"] = opjs; 207 | -------------------------------------------------------------------------------- /dist/utils/hash.d.ts: -------------------------------------------------------------------------------- 1 | export declare const hash: (str: string, seed?: number) => number; 2 | -------------------------------------------------------------------------------- /dist/utils/hash.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.hash = void 0; 4 | /* 5 | cyrb53a beta (c) 2023 bryc (github.com/bryc) 6 | License: Public domain (or MIT if needed). Attribution appreciated. 7 | This is a work-in-progress, and changes to the algorithm are expected. 8 | The original cyrb53 has a slight mixing bias in the low bits of h1. 9 | This doesn't affect collision rate, but I want to try to improve it. 10 | This new version has preliminary improvements in avalanche behavior. 11 | */ 12 | var cyrb53a_beta = function (str, seed) { 13 | if (seed === void 0) { seed = 0; } 14 | var h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; 15 | for (var i = 0, ch = void 0; i < str.length; i++) { 16 | ch = str.charCodeAt(i); 17 | h1 = Math.imul(h1 ^ ch, 0x85ebca77); 18 | h2 = Math.imul(h2 ^ ch, 0xc2b2ae3d); 19 | } 20 | h1 ^= Math.imul(h1 ^ (h2 >>> 15), 0x735a2d97); 21 | h2 ^= Math.imul(h2 ^ (h1 >>> 15), 0xcaf649a9); 22 | h1 ^= h2 >>> 16; 23 | h2 ^= h1 >>> 16; 24 | return 2097152 * (h2 >>> 0) + (h1 >>> 11); 25 | }; 26 | var hash = function (str, seed) { 27 | if (seed === void 0) { seed = 420; } 28 | return cyrb53a_beta(str, seed); 29 | }; 30 | exports.hash = hash; 31 | -------------------------------------------------------------------------------- /dist/utils/interfaces.d.ts: -------------------------------------------------------------------------------- 1 | export interface ComponentInterface { 2 | name: string; 3 | func: ComponentFunctionInterface; 4 | } 5 | export type ComponentFunctionInterface = (fingerprint: object) => ComponentOutputInterface; 6 | export interface ComponentOutputInterface { 7 | status: number; 8 | value?: string | number | object | boolean | null; 9 | type?: string; 10 | isTrusted?: boolean; 11 | } 12 | export declare const resolveComponent: (status: number, value?: string | number | object | boolean | null) => ComponentOutputInterface; 13 | -------------------------------------------------------------------------------- /dist/utils/interfaces.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.resolveComponent = void 0; 4 | var utils_1 = require("./utils"); 5 | var resolveComponent = function (status, value) { 6 | if (typeof value === 'object' && value !== null) { 7 | if ('status' in value) { 8 | throw new Error('Status already exists on the object'); 9 | } 10 | for (var key in value) { 11 | if (typeof value[key] === 'object' && value[key] !== null) { 12 | value[key].status = status; 13 | } 14 | } 15 | } 16 | return { 17 | status: status, 18 | value: value, 19 | type: (0, utils_1.getSpecificType)(value) 20 | }; 21 | }; 22 | exports.resolveComponent = resolveComponent; 23 | -------------------------------------------------------------------------------- /dist/utils/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare const getSpecificType: (variable: unknown) => "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "null" | "array" | "infinity" | "-infinity" | "nan"; 2 | export declare const getSpecificTypeAndValue: (variable: unknown) => { 3 | type: string; 4 | value: unknown; 5 | }; 6 | export declare const stringify: (value: any) => any; 7 | -------------------------------------------------------------------------------- /dist/utils/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.stringify = exports.getSpecificTypeAndValue = exports.getSpecificType = void 0; 4 | var getSpecificType = function (variable) { 5 | var typeofVariable = typeof variable; 6 | if (typeofVariable === 'object') { 7 | if (variable === null) { 8 | return 'null'; 9 | } 10 | if (Array.isArray(variable)) { 11 | return 'array'; 12 | } 13 | } 14 | if (typeofVariable === 'number') { 15 | if (variable === Infinity) { 16 | return 'infinity'; 17 | } 18 | if (variable === -Infinity) { 19 | return '-infinity'; 20 | } 21 | if (isNaN(variable)) { 22 | return 'nan'; 23 | } 24 | } 25 | return typeofVariable; 26 | }; 27 | exports.getSpecificType = getSpecificType; 28 | var getSpecificTypeAndValue = function (variable) { 29 | return { 30 | type: (0, exports.getSpecificType)(variable), 31 | value: variable 32 | }; 33 | }; 34 | exports.getSpecificTypeAndValue = getSpecificTypeAndValue; 35 | var stringify = function (value) { 36 | if (typeof JSON === 'object' && typeof JSON.stringify === 'function') { 37 | return JSON.stringify(value); 38 | } 39 | function escapeString(str) { 40 | return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; 41 | } 42 | if (value === null) { 43 | return 'null'; 44 | } 45 | if (typeof value === 'number' || typeof value === 'boolean') { 46 | return String(value); 47 | } 48 | if (typeof value === 'string') { 49 | return escapeString(value); 50 | } 51 | if (typeof value === 'undefined') { 52 | return 'null'; 53 | } 54 | if (Array.isArray(value)) { 55 | var arrayValues = value.map(function (item) { return (typeof item === 'undefined' ? 'null' : (0, exports.stringify)(item)); }).join(','); 56 | return '[' + arrayValues + ']'; 57 | } 58 | if (typeof value === 'object') { 59 | var objectKeys = Object.keys(value); 60 | var objectValues = objectKeys 61 | .filter(function (key) { return typeof value[key] !== 'undefined'; }) 62 | .map(function (key) { return escapeString(key) + ':' + (0, exports.stringify)(value[key]); }) 63 | .join(','); 64 | return '{' + objectValues + '}'; 65 | } 66 | return 'null'; // fallback for unsupported types 67 | }; 68 | exports.stringify = stringify; 69 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /docs/modules/README.md: -------------------------------------------------------------------------------- 1 | # Current Modules 2 | - brave.ts - Detects Brave Browser (uncompleted) 3 | - devicePixelRadio.ts - Retrieves the value of `window.devicePixelRatio` 4 | - getBattery.ts - Uses the battery API to retrieve battery data such as power level and if the battery is being charged 5 | - jsHeapSizeLimit.ts - Retrieves the value of `performance.memory.jsHeapSizeLimit` in Chromium browsers 6 | - matchMedia.ts - Enumerates media features via `window.matchMedia` 7 | - screen.ts - Enumerates the `screen` object 8 | - timingResolution.ts - Gets the timing resolution of Chromium browsers using `performance.now` 9 | - userAgentData.ts - Uses the `navigator.userAgentData` API on Chromium browsers to retrieve data about the browser 10 | -------------------------------------------------------------------------------- /docs/whitepapers/rfc-opjs-v1.md: -------------------------------------------------------------------------------- 1 | # Request for Comments: Weighted Matching System for Browser Fingerprinting 2 | 3 | Note: This RFC was generated by OpenAI's GPT-4o in May 2024 (and revised in June 2024) summarizing my (Joe Rutkowski / Joe12387) overall theory in processing numerous fingerprinting values/hashes from different methods/modules/components implemented in OverpoweredJS. This is generally just an example of how fingerprinting values produced by OverpoweredJS may be handled. There are almost certainly more efficient ways of processing this data in order to implement tracking and this method does have some issues which could be resolved by another whitepaper. If you have a theory on how to better process this data, please submit a pull request adding your RFC to the `whitepapers` folder. Thanks! 4 | 5 | ## Abstract 6 | 7 | This document describes the specifications and implementation details of a weighted matching system for browser fingerprinting. The system is designed to identify unique browser instances by comparing various fingerprinting methods and assigning weights based on the uniqueness of their values. The goal is to achieve accurate fingerprint matching while optimizing performance and scalability. 8 | 9 | ## 1. Introduction 10 | 11 | Browser fingerprinting is a technique used to identify and track individual browsers by collecting various pieces of information about the browser and the device it is running on. This system aims to enhance the accuracy of browser fingerprinting by assigning weights to different fingerprinting methods based on their uniqueness and comparing them to a database of previously captured fingerprints. 12 | 13 | ## 2. Objectives 14 | 15 | - To assign weights to different fingerprinting methods based on the number of unique values they can generate. 16 | - To compare client fingerprints against a database of known fingerprints and determine matches based on weighted scores. 17 | - To add new fingerprints to the database if no match is found. 18 | - To ensure the system is scalable and can handle a large volume of requests efficiently. 19 | 20 | ## 3. Fingerprinting Methods 21 | 22 | The system uses several fingerprinting methods, each with a different range of unique values. Examples include: 23 | - `navigator.platform` 24 | - `WebGL fingerprint` 25 | - `navigator.userAgent` 26 | - `screen.width` & `screen.height` 27 | - `timezone` 28 | 29 | ## 4. Weighting System 30 | 31 | ### 4.1 Calculation of Weights 32 | 33 | Weights are assigned to each fingerprinting method based on the logarithm of the number of unique values they can generate. This ensures that methods with a larger range of values have a higher weight, reflecting their greater contribution to identifying a unique browser instance. 34 | 35 | #### Formula 36 | 37 | \[ W_{method} = \log(n_{unique}) \] 38 | 39 | Where: 40 | - \( W_{method} \) is the weight of the fingerprinting method. 41 | - \( n_{unique} \) is the number of unique values the method can generate. 42 | 43 | ### 4.2 Normalization of Weights 44 | 45 | Weights are normalized so that the sum of all weights equals 1. This ensures proportional distribution of importance among different methods. 46 | 47 | \[ W_{normalized} = \frac{W_{method}}{\sum{W_{method}}} \] 48 | 49 | ## 5. Fingerprint Matching 50 | 51 | ### 5.1 Comparison Algorithm 52 | 53 | The comparison algorithm calculates the matching score between a client fingerprint and each fingerprint in the database. The matching score is determined by summing the weights of the methods that have matching values. 54 | 55 | ### 5.2 Matching Threshold 56 | 57 | A fingerprint is considered a match if the total matching score exceeds a predefined threshold (e.g., 90%). 58 | 59 | \[ \text{Match Score} = \left( \frac{\sum{W_{matched}}}{\sum{W_{total}}} \right) \times 100 \] 60 | 61 | Where: 62 | - \( W_{matched} \) is the sum of weights for matching methods. 63 | - \( W_{total} \) is the sum of all method weights. 64 | 65 | ## 6. System Architecture 66 | 67 | ### 6.1 API Endpoints 68 | 69 | The system provides an HTTP API for fingerprint submission and comparison. 70 | 71 | #### `/fingerprint` (POST) 72 | 73 | - **Request Body**: JSON object containing the client's fingerprint data. 74 | - **Response**: JSON object indicating whether a match was found and if the fingerprint was added to the database. 75 | 76 | ### 6.2 Database 77 | 78 | The fingerprint data is stored in a database optimized for high read and write throughput. MongoDB is recommended for its flexibility and scalability. 79 | 80 | ## 7. Implementation 81 | 82 | ### 7.1 Node.js Server 83 | 84 | The server is built using Node.js and Express. It handles HTTP requests, compares fingerprints, and interacts with the MongoDB database. 85 | 86 | ### 7.2 Code Example 87 | 88 | An example implementation would include setting up a Node.js server with Express, connecting to MongoDB, and defining endpoints for fingerprint submission and comparison. The server would calculate weights, normalize them, and compare fingerprints against a stored database, adding new fingerprints if no match is found. 89 | 90 | ```javascript 91 | const express = require('express'); 92 | const bodyParser = require('body-parser'); 93 | const mongoose = require('mongoose'); 94 | const app = express(); 95 | const port = 3000; 96 | 97 | // Connect to MongoDB 98 | mongoose.connect('mongodb://localhost:27017/fingerprintDB', { useNewUrlParser: true, useUnifiedTopology: true }); 99 | 100 | const fingerprintSchema = new mongoose.Schema({ 101 | navigatorPlatform: String, 102 | webgl: String, 103 | // Add other fingerprint methods here 104 | }); 105 | 106 | const Fingerprint = mongoose.model('Fingerprint', fingerprintSchema); 107 | 108 | // Example unique value counts for different methods 109 | const uniqueValueCounts = { 110 | navigatorPlatform: 10, 111 | webgl: 100000, 112 | // Add other methods and their unique value counts here 113 | }; 114 | 115 | // Step 1: Determine the raw weights based on the log of unique value counts 116 | const rawWeights = {}; 117 | for (const method in uniqueValueCounts) { 118 | rawWeights[method] = Math.log(uniqueValueCounts[method]); 119 | } 120 | 121 | // Step 2: Normalize the weights 122 | const totalRawWeight = Object.values(rawWeights).reduce((sum, weight) => sum + weight, 0); 123 | const normalizedWeights = {}; 124 | for (const method in rawWeights) { 125 | normalizedWeights[method] = rawWeights[method] / totalRawWeight; 126 | } 127 | 128 | // Function to compare fingerprints 129 | function compareFingerprints(clientFingerprint, databaseFingerprint) { 130 | let totalWeight = 0; 131 | let matchWeight = 0; 132 | 133 | for (const method in clientFingerprint) { 134 | totalWeight += normalizedWeights[method]; 135 | 136 | if (clientFingerprint[method] === databaseFingerprint[method]) { 137 | matchWeight += normalizedWeights[method]; 138 | } 139 | } 140 | 141 | // Calculate the matching score as a percentage 142 | const matchScore = (matchWeight / totalWeight) * 100; 143 | 144 | // Determine if the fingerprints are considered a match 145 | return matchScore >= 90; // 90% threshold 146 | } 147 | 148 | app.use(bodyParser.json()); 149 | 150 | // API endpoint to check and add fingerprint 151 | app.post('/fingerprint', async (req, res) => { 152 | const clientFingerprint = req.body; 153 | 154 | try { 155 | const fingerprints = await Fingerprint.find(); 156 | 157 | for (const fingerprint of fingerprints) { 158 | if (compareFingerprints(clientFingerprint, fingerprint)) { 159 | return res.status(200).json({ match: true }); 160 | } 161 | } 162 | 163 | // If no match found, add the fingerprint to the database 164 | const newFingerprint = new Fingerprint(clientFingerprint); 165 | await newFingerprint.save(); 166 | res.status(201).json({ match: false, message: 'Fingerprint added to the database' }); 167 | } catch (error) { 168 | res.status(500).json({ error: error.message }); 169 | } 170 | }); 171 | 172 | app.listen(port, () => { 173 | console.log(`Server running at http://localhost:${port}/`); 174 | }); 175 | ``` 176 | 177 | # Copyright 178 | (c) 2024 Joe Rutkowski (Joe12387) Joe@dreggle.com - Released under the OverpoweredJS OS Public License 179 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overpoweredjs", 3 | "version": "0.0.1", 4 | "description": "An even more powerful browser fingerprinting library for creating persistent, unique and long-lasting digital fingerprints.", 5 | "main": "./dist/opjs.js", 6 | "module": "./dist/opjs.esm.js", 7 | "types": "./dist/opjs.d.ts", 8 | "scripts": { 9 | "build-for-cdn": "npx webpack", 10 | "build-module": "tsc", 11 | "build": "npm run build-for-cdn && npm run build-module" 12 | }, 13 | "keywords": [ 14 | "browser", 15 | "fingerprinting", 16 | "browser fingerprinting", 17 | "browser-fingerprinting", 18 | "browserfingerprinting", 19 | "chrome", 20 | "chromium", 21 | "safari", 22 | "firefox", 23 | "msie", 24 | "ie", 25 | "internet explorer", 26 | "internet-explorer", 27 | "internetexplorer", 28 | "edge", 29 | "opera", 30 | "brave" 31 | ], 32 | "author": "Joe Rutkowski (Joe12387)", 33 | "license": "CC-BY-NC-SA-4.0", 34 | "devDependencies": { 35 | "@typescript-eslint/eslint-plugin": "^6.21.0", 36 | "@typescript-eslint/parser": "^6.4.0", 37 | "eslint": "^8.57.0", 38 | "eslint-config-prettier": "^8.8.0", 39 | "eslint-config-standard-with-typescript": "^43.0.1", 40 | "eslint-plugin-import": "^2.29.1", 41 | "eslint-plugin-n": "^16.6.2", 42 | "eslint-plugin-prettier": "^4.2.1", 43 | "eslint-plugin-promise": "^6.6.0", 44 | "globals": "^15.9.0", 45 | "import-fresh": "^3.3.0", 46 | "prettier": "^3.2.5", 47 | "terser-webpack-plugin": "^5.3.10", 48 | "ts-loader": "^9.4.2", 49 | "typescript": "^4.9.5", 50 | "webpack": "^5.75.0", 51 | "webpack-cli": "^5.1.4" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/components/brave/brave.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | interface BraveNavigator extends Navigator { 7 | brave?: { 8 | // eslint-disable-next-line @typescript-eslint/ban-types 9 | isBrave?: Function; 10 | }; 11 | } 12 | 13 | export const detectBrave: ( 14 | fingerprint 15 | ) => Promise = async () => { 16 | const braveNavigator = navigator as BraveNavigator; 17 | if (typeof braveNavigator === 'undefined' || braveNavigator === null) 18 | return resolveComponent(-1); 19 | else { 20 | const braveClues = [ 21 | typeof braveNavigator.brave === 'object', 22 | 'brave' in navigator, 23 | typeof braveNavigator.brave === 'object' && 24 | Object.getPrototypeOf(navigator.brave).constructor.name == 'Brave', 25 | typeof braveNavigator.brave === 'object' && 26 | typeof braveNavigator.brave.isBrave === 'function' && 27 | braveNavigator.brave.isBrave.toString() === 28 | 'function isBrave() { [native code] }', 29 | ]; 30 | const likeBrave = braveClues.includes(true); // If any of the clues are true, it's likely Brave. 31 | const isBrave = !braveClues.includes(false); // If all of the clues are true, it's almost certainly Brave. 32 | const mode = -1; // -1 = unknown, 0 = shields down, 1 = shields up, 2 = shields up + aggressive 33 | if (likeBrave) { 34 | // TODO: Implement detection of Brave's shields status 35 | } 36 | const brave = { 37 | likeBrave, 38 | isBrave, 39 | mode, 40 | braveClues, 41 | }; 42 | return resolveComponent(0, { brave }); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/devicePixelRatio/devicePixelRatio.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | import { getSpecificTypeAndValue } from '../../utils/utils'; 7 | 8 | export const devicePixelRatio: ( 9 | fingerprint 10 | ) => Promise = async () => { 11 | const devicePixelRatio = getSpecificTypeAndValue(self.devicePixelRatio); 12 | return resolveComponent(0, { devicePixelRatio }); 13 | }; 14 | -------------------------------------------------------------------------------- /src/components/getBattery/getBattery.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | import { getSpecificTypeAndValue } from '../../utils/utils'; 7 | 8 | export const getBattery: ( 9 | fingerprint 10 | ) => Promise = async (fingerprint) => { 11 | if (typeof self.navigator !== 'object') { 12 | return resolveComponent(-2); 13 | } 14 | if (typeof self.navigator.getBattery !== 'function') { 15 | return resolveComponent(-1); 16 | } 17 | try { 18 | const battery = await self.navigator.getBattery(); 19 | return resolveComponent(0, { 20 | getBattery_level: getSpecificTypeAndValue(battery.level), 21 | getBattery_charging: getSpecificTypeAndValue(battery.charging), 22 | getBattery_chargingTime: getSpecificTypeAndValue(battery.chargingTime), 23 | getBattery_dischargingTime: getSpecificTypeAndValue(battery.dischargingTime), 24 | }); 25 | } catch (error) { 26 | return resolveComponent(-3); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | import { detectBrave } from './brave/brave'; 2 | import { timingResolution } from './timingResolution/timingResolution'; 3 | import { userAgentData } from './userAgentData/userAgentData'; 4 | import { jsHeapSizeLimit } from './jsHeapSizeLimit/jsHeapSizeLimit'; 5 | import { screen } from './screen/screen'; 6 | import { devicePixelRatio } from './devicePixelRatio/devicePixelRatio'; 7 | import { getBattery } from './getBattery/getBattery'; 8 | import { matchMedia } from './matchMedia/matchMedia'; 9 | 10 | export const fingerprintStage1: ComponentInterface[] = []; 11 | export const fingerprintStage2: ComponentInterface[] = []; 12 | export const fingerprintStage3: ComponentInterface[] = []; 13 | 14 | export interface ComponentOutputInterface { 15 | status: number; 16 | value?: string | number | object | boolean | null; 17 | type?: string; 18 | isTrusted?: boolean; 19 | } 20 | export interface ComponentInterface { 21 | name: string; 22 | func: ComponentFunctionInterface; 23 | } 24 | export type ComponentFunctionInterface = ( 25 | fingerprint: object 26 | ) => Promise; 27 | 28 | function addComponent( 29 | component: ComponentFunctionInterface, 30 | componentName: string, 31 | stage: ComponentInterface[], 32 | ): void { 33 | stage.push({ 34 | name: componentName, 35 | func: component, 36 | }); 37 | } 38 | 39 | // stage 1 40 | addComponent(detectBrave, 'brave', fingerprintStage1); 41 | addComponent(timingResolution, 'timingResolution', fingerprintStage1); 42 | addComponent(userAgentData, 'userAgentData', fingerprintStage1); 43 | addComponent(jsHeapSizeLimit, 'jsHeapSizeLimit', fingerprintStage1); 44 | 45 | // stage 2 46 | addComponent(screen, 'screen', fingerprintStage2); 47 | addComponent(devicePixelRatio, 'devicePixelRatio', fingerprintStage2); 48 | addComponent(getBattery, 'getBattery', fingerprintStage2); 49 | addComponent(matchMedia, 'matchMedia', fingerprintStage2); 50 | 51 | // stage 3 52 | // this may be used for additional components that require the outputs of stages 1 and/or 2 53 | -------------------------------------------------------------------------------- /src/components/jsHeapSizeLimit/jsHeapSizeLimit.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | import { getSpecificTypeAndValue } from '../../utils/utils'; 7 | 8 | interface Performance { 9 | memory?: { 10 | jsHeapSizeLimit: number; 11 | }; 12 | } 13 | 14 | export const jsHeapSizeLimit: ( 15 | fingerprint 16 | ) => Promise = async () => { 17 | const performance = self.performance as Performance; 18 | if (typeof performance !== 'object') return resolveComponent(-1); 19 | const memory = performance.memory; 20 | if (typeof memory !== 'object') return resolveComponent(-2); 21 | const jsHeapSizeLimit = getSpecificTypeAndValue(memory.jsHeapSizeLimit); 22 | return resolveComponent(0, { jsHeapSizeLimit }); 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/matchMedia/matchMedia.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | export const matchMedia: ( 7 | fingerprint 8 | ) => Promise < ComponentOutputInterface > = async (fingerprint) => { 9 | /** 10 | * 11 | * `mediaQueries` list adapted from ThumbmarkJS: https://github.com/thumbmarkjs/thumbmarkjs/blob/b6d6a7f69efe7b72321ff86a11233c814ce097a2/src/components/screen/screen.ts 12 | * 13 | * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries 14 | * 15 | * I don't know if half of these queries are even supported by any browser, but they are here for completeness. 16 | * 17 | */ 18 | const mediaQueries: { 19 | [k: string]: string[] 20 | } = { 21 | 'prefers-contrast': ['high', 'more', 'low', 'less', 'forced', 'no-preference', 'high-contrast', 'low-contrast'], 22 | 'color-gamut': ['rec2020', 'p3', 'srgb', 'display-p3', 'a98rgb'], 23 | 'dynamic-range': ['high', 'standard', 'hdr', 'sdr'], 24 | 'video-dynamic-range': ['high', 'standard', 'hdr', 'sdr'], 25 | 'any-hover': ['hover', 'none', 'on-demand'], 26 | 'any-pointer': ['none', 'coarse', 'fine', 'hover'], 27 | 'pointer': ['none', 'coarse', 'fine', 'hover'], 28 | 'hover': ['hover', 'none', 'on-demand'], 29 | 'update': ['fast', 'slow', 'none'], 30 | 'overflow-block': ['scroll', 'none', 'optional-paged', 'paged', 'optional-paged-x', 'optional-paged-y'], 31 | 'overflow-inline': ['scroll', 'none', 'optional-paged', 'paged', 'optional-paged-x', 'optional-paged-y'], 32 | 'color': ['8', '16', '256', '4k', '8k'], 33 | 'inverted-colors': ['inverted', 'none', 'no-preference'], 34 | 'prefers-reduced-motion': ['reduce', 'no-preference', 'motion'], 35 | 'prefers-reduced-transparency': ['reduce', 'no-preference', 'transparency'], 36 | 'grid': ['0', '1', '2'], 37 | 'scripting': ['none', 'initial-only', 'enabled', 'enabled-only'], 38 | 'forced-colors': ['active', 'none', 'none', 'active'], 39 | 'display-mode': ['fullscreen', 'standalone', 'minimal-ui', 'browser', 'window'], 40 | 'aspect-ratio': ['1/1', '16/9', '16/10', '4/3', '8/5', '5/4', '5/3', '3/2', '16/12', '3/4', '9/16', '10/16', '3/5', '2/3', '12/16'], 41 | 'resolution': ['300dpi', '2dppx', '3dppx'], 42 | 'prefers-color-scheme': ['dark', 'light', 'no-preference'], 43 | 'overflow': ['auto', 'hidden'], 44 | 'transform-3d': ['0', '1'], 45 | 'device-aspect-ratio': ['1/1', '16/9', '16/10', '4/3', '8/5', '5/4', '5/3', '3/2', '16/12', '3/4', '9/16', '10/16', '3/5', '2/3', '12/16'], 46 | 'device-height': ['640px', '768px', '1024px'], 47 | 'device-width': ['320px', '360px', '375px'], 48 | 'forced-color-adjust': ['none', 'auto'], 49 | 'orientation': ['portrait', 'landscape'], 50 | 'scan': ['progressive', 'interlace'], 51 | }; 52 | const matchMedia = self.matchMedia; 53 | if (typeof matchMedia !== 'function') { 54 | return resolveComponent(-1); 55 | } 56 | const matchMedia_mediaQueries = {}; 57 | for (const [key, value] of Object.entries(mediaQueries)) { 58 | for (const item of value) { 59 | const query = `(${key}: ${item})`; 60 | try { 61 | const result = window.matchMedia(query); 62 | matchMedia_mediaQueries[query] = result.matches; 63 | } catch (e) { 64 | matchMedia_mediaQueries[query] = { error: true };; 65 | } 66 | } 67 | } 68 | return resolveComponent(0, { matchMedia_mediaQueries }); 69 | }; 70 | -------------------------------------------------------------------------------- /src/components/screen/screen.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | import { getSpecificType, getSpecificTypeAndValue } from '../../utils/utils'; 7 | 8 | interface ScreenOutput { 9 | screen_width: ReturnType; 10 | screen_height: ReturnType; 11 | screen_pixelDepth: ReturnType; 12 | screen_colorDepth: ReturnType; 13 | screen_availWidth: ReturnType; 14 | screen_availHeight: ReturnType; 15 | screen_availTop: ReturnType; 16 | screen_availLeft: ReturnType; 17 | screen_isExtended: ReturnType; 18 | screen_orientationType: ReturnType; 19 | screen_orientation?: { 20 | angle: ReturnType; 21 | type: ReturnType; 22 | }; 23 | } 24 | 25 | interface ExtendedScreen extends Screen { 26 | availTop?: number; 27 | availLeft?: number; 28 | isExtended?: boolean; 29 | } 30 | 31 | export const screen: ( 32 | fingerprint: any 33 | ) => Promise = async (fingerprint) => { 34 | const screen = window.screen as ExtendedScreen; 35 | if (typeof screen !== 'object') return resolveComponent(-1); 36 | 37 | const output: ScreenOutput = { 38 | screen_width: getSpecificTypeAndValue(screen.width), 39 | screen_height: getSpecificTypeAndValue(screen.height), 40 | screen_pixelDepth: getSpecificTypeAndValue(screen.pixelDepth), 41 | screen_colorDepth: getSpecificTypeAndValue(screen.colorDepth), 42 | screen_availWidth: getSpecificTypeAndValue(screen.availWidth), 43 | screen_availHeight: getSpecificTypeAndValue(screen.availHeight), 44 | screen_availTop: getSpecificTypeAndValue(screen.availTop ?? undefined), 45 | screen_availLeft: getSpecificTypeAndValue(screen.availLeft ?? undefined), 46 | screen_isExtended: getSpecificTypeAndValue(screen.isExtended ?? undefined), 47 | screen_orientationType: getSpecificType(screen.orientation), 48 | }; 49 | 50 | if (output.screen_orientationType === 'object') { 51 | output.screen_orientation = { 52 | angle: getSpecificTypeAndValue(screen.orientation.angle), 53 | type: getSpecificTypeAndValue(screen.orientation.type), 54 | }; 55 | } 56 | 57 | return resolveComponent(0, output); 58 | }; 59 | -------------------------------------------------------------------------------- /src/components/timingResolution/timingResolution.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | export const timingResolution: ( 7 | fingerprint 8 | ) => Promise = async () => { 9 | const p = self.performance; 10 | 11 | if (p === undefined) return resolveComponent(-1); 12 | if (typeof p.now !== 'function') return resolveComponent(-2); 13 | 14 | let smallestInterval = 1; 15 | let secondSmallestInterval = 1; 16 | 17 | let now = p.now(); 18 | let newNow = now; 19 | 20 | for (let i = 0; i < 5000; i++) { 21 | now = newNow; 22 | newNow = p.now(); 23 | 24 | if (now < newNow) { 25 | const difference = newNow - now; 26 | if (difference > smallestInterval) { 27 | if (difference < secondSmallestInterval) { 28 | secondSmallestInterval = difference; 29 | } 30 | } else if (difference < smallestInterval) { 31 | secondSmallestInterval = smallestInterval; 32 | smallestInterval = difference; 33 | } 34 | } 35 | } 36 | // Safari (and maybe other non-chromium browsers) may sometimes return a non-persistent value for the smallest interval. 37 | // In this case, we set the smallest interval to 1 to correct the issue if the second smallest interval is also 1. 38 | // In a normal Chromium environment excluding Brave's protections, neither of these intervals should ever be 1. 39 | if (secondSmallestInterval === 1) smallestInterval = 1; 40 | return resolveComponent(0, { 41 | timingResolution_smallestInterval: smallestInterval, 42 | timingResolution_secondSmallestInterval: secondSmallestInterval, 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/userAgentData/userAgentData.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentOutputInterface, 3 | resolveComponent, 4 | } from '../../utils/interfaces'; 5 | 6 | import { getSpecificType, getSpecificTypeAndValue } from '../../utils/utils'; 7 | 8 | interface BrandItem { 9 | brand: string; 10 | fullVersion: string; 11 | } 12 | 13 | const parseBrand = (arr: unknown[]): string[] => { 14 | const brands: string[] = []; 15 | if (!arr) return []; 16 | for (let i = 0; i < arr.length; i++) { 17 | const item = arr[i] as BrandItem; 18 | if (item.brand) { 19 | const brand = item.brand; 20 | if (!new RegExp('Brand', 'i').test(brand)) { 21 | brands.push(brand); 22 | } 23 | } 24 | } 25 | return brands.sort(); 26 | }; 27 | 28 | export const userAgentData: ( 29 | fingerprint 30 | ) => Promise = async (fingerprint) => { 31 | const userAgentData = navigator.userAgentData; 32 | if (!userAgentData) return resolveComponent(-1); 33 | if (typeof userAgentData.getHighEntropyValues !== 'function') 34 | return resolveComponent(-2); 35 | const highEntropyKeys = [ 36 | 'architecture', 37 | 'bitness', 38 | 'brands', 39 | 'formFactor', 40 | 'fullVersionList', 41 | 'mobile', 42 | 'model', 43 | 'platform', 44 | 'platformVersion', 45 | 'uaFullVersion', 46 | 'wow64', 47 | ]; 48 | const promises: Promise< 49 | { [x: string]: { brands: string[] } } | { error: boolean } 50 | >[] = []; 51 | for (const key of highEntropyKeys) { 52 | try { 53 | promises.push( 54 | userAgentData.getHighEntropyValues([key]).then((value) => { 55 | return { [key]: value }; 56 | }) 57 | ); 58 | } catch (e: unknown) { 59 | promises.push(Promise.resolve({ error: true })); 60 | } 61 | } 62 | return Promise.all(promises).then((values) => { 63 | const output = {}; 64 | for (const value of values) { 65 | const key = Object.keys(value)[0]; 66 | const highEntropyValues = value[key]; 67 | highEntropyValues.brands = 68 | getSpecificType(highEntropyValues.brands) === 'array' 69 | ? parseBrand(highEntropyValues.brands) 70 | : highEntropyValues.brands; 71 | 72 | (() => { 73 | if (Array.isArray(highEntropyValues.fullVersionList)) { 74 | const sortedList = {}; 75 | for (const item of highEntropyValues.fullVersionList) { 76 | if (!new RegExp('Brand', 'i').test(item.brand)) { 77 | sortedList[item.brand] = item.version; 78 | } 79 | } 80 | highEntropyValues.fullVersionList = sortedList; 81 | } 82 | })(); 83 | 84 | for (const k in highEntropyValues) { 85 | if (Object.prototype.hasOwnProperty.call(highEntropyValues, k)) { 86 | output['userAgentData_' + k] = getSpecificTypeAndValue(highEntropyValues[k]); 87 | } else { 88 | output['userAgentData_' + k] = { type: 'undefined' }; 89 | } 90 | } 91 | } 92 | return resolveComponent(0, output); 93 | }); 94 | }; 95 | -------------------------------------------------------------------------------- /src/global.t.ts: -------------------------------------------------------------------------------- 1 | interface Navigator { 2 | getBattery?: () => Promise; 3 | } 4 | 5 | interface BatteryManager { 6 | level: number; 7 | charging: boolean; 8 | chargingTime: number; 9 | dischargingTime: number; 10 | } 11 | -------------------------------------------------------------------------------- /src/opjs.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * OverpoweredJS v0.0.1 4 | * 5 | * https://github.com/Joe12387/overpoweredjs 6 | * 7 | * 8 | **/ 9 | 10 | import { 11 | fingerprintStage1, 12 | fingerprintStage2, 13 | fingerprintStage3, 14 | } from './components'; 15 | 16 | import { hash } from './utils/hash'; 17 | 18 | import { stringify } from './utils/utils'; 19 | 20 | declare global { 21 | interface Window { 22 | opjs: typeof opjs; 23 | } 24 | interface Navigator { 25 | brave?: object; 26 | userAgentData?: { 27 | getHighEntropyValues: (keys: string[]) => Promise<{ brands: string[] }>; 28 | }; 29 | } 30 | } 31 | 32 | const isOpaque = false; // vestigial from CS Edition, may be used in future versions of OS Edition 33 | 34 | function processModule(module: object, name: string): object { 35 | const processedModule = {}; 36 | processedModule[name] = {}; 37 | const status = module['status']; 38 | const status_identifier = 'status'; 39 | if (isOpaque) { 40 | processedModule[name][status_identifier] = status; 41 | } else { 42 | if (typeof processedModule[name][status_identifier] !== 'undefined') { 43 | throw new Error('Status already exists on the object'); 44 | } 45 | processedModule[name][status_identifier] = status; 46 | } 47 | if (status >= 0) { 48 | const value = module['value']; 49 | const type = module['type']; 50 | if (type === 'object') { 51 | for (const key in value) { 52 | if (Object.prototype.hasOwnProperty.call(value, key)) { 53 | const identifier = key; 54 | if (isOpaque) { 55 | const keyHash = hash(identifier).toString(36); 56 | processedModule[keyHash] = hash( 57 | hash(stringify(value[key])).toString(36), 58 | hash(hash(type).toString(36), hash(keyHash, status)) 59 | ).toString(36); 60 | } else { 61 | processedModule[identifier] = value[key]; 62 | } 63 | } 64 | } 65 | } else { 66 | const value_identifier = name + '_value'; 67 | const type_identifier = name + '_type'; 68 | if (isOpaque) { 69 | const value_keyHash = hash(value_identifier).toString(36); 70 | processedModule[name][value_keyHash] = hash( 71 | hash(stringify(value)).toString(36), 72 | hash(hash(type).toString(36), hash(value_keyHash, status)) 73 | ).toString(36); 74 | const type_keyHash = hash(type_identifier).toString(36); 75 | processedModule[name][type_keyHash] = hash( 76 | hash(stringify(value)).toString(36), 77 | hash(hash(type).toString(36), hash(type_keyHash, status)) 78 | ).toString(36); 79 | } else { 80 | processedModule[value_identifier] = value; 81 | processedModule[type_identifier] = type; 82 | } 83 | } 84 | } 85 | return processedModule; 86 | } 87 | 88 | function processFingerprint(fingerprint: object): object { 89 | const brave = processModule(fingerprint['brave'], 'brave'); 90 | const screen = processModule(fingerprint['screen'], 'screen'); 91 | const userAgentData = processModule(fingerprint['userAgentData'], 'userAgentData'); 92 | const jsHeapSizeLimit = processModule(fingerprint['jsHeapSizeLimit'], 'jsHeapSizeLimit'); 93 | const devicePixelRatio = processModule(fingerprint['devicePixelRatio'], 'devicePixelRatio'); 94 | const timingResolution = processModule(fingerprint['timingResolution'], 'timingResolution'); 95 | const getBattery = processModule(fingerprint['getBattery'], 'getBattery'); 96 | const matchMedia = processModule(fingerprint['matchMedia'], 'matchMedia'); 97 | 98 | const processedFingerprint = { 99 | ...brave, 100 | ...screen, 101 | ...userAgentData, 102 | ...jsHeapSizeLimit, 103 | ...devicePixelRatio, 104 | ...timingResolution, 105 | ...getBattery, 106 | ...matchMedia, 107 | }; 108 | 109 | return processedFingerprint; 110 | } 111 | 112 | type GetFingerprintFunction = () => unknown; 113 | 114 | function withTimeout(promise: Promise, ms: number, error: string, context: string): Promise { 115 | const timeout = new Promise((_, reject) => 116 | setTimeout(() => reject(new Error(`OverpoweredJS: ${error} - Context: ${context}`)), ms) 117 | ); 118 | return Promise.race([promise, timeout]); 119 | } 120 | 121 | function handleError(error: Error, stage: string, componentName: string) { 122 | console.error(`OverpoweredJS: Error in ${componentName} (${stage}): ${error.message}`); 123 | } 124 | 125 | export async function opjs(): Promise<{ 126 | getFingerprint: GetFingerprintFunction; 127 | }> { 128 | return new Promise<{ 129 | getFingerprint: GetFingerprintFunction; 130 | }>((resolve, reject) => { 131 | const fingerprint = {}; 132 | 133 | if (typeof self.window !== 'object' || self.window === null || typeof self.window.document !== 'object' || self.window.document === null) { 134 | reject(new Error('OverpoweredJS: window and/or document object not found. Only run in a browser environment. Other contexts are not supported.')); 135 | } 136 | 137 | const timeoutMs = 2000; // 2 seconds 138 | 139 | const fingerprintStage1Promises = fingerprintStage1.map((component) => ({ 140 | promise: withTimeout( 141 | component.func(fingerprint).catch((error) => { 142 | handleError(error, 'stage 1', component.name); 143 | throw error; 144 | }), 145 | timeoutMs, 146 | `OverpoweredJS: ${component.name} (stage 1) timed out`, 147 | `Stage 1 - ${component.name}` 148 | ), 149 | name: component.name, 150 | })); 151 | 152 | return Promise.all(fingerprintStage1Promises.map((p) => p.promise)) 153 | .then((results) => { 154 | results.forEach((result, index) => { 155 | const name = fingerprintStage1Promises[index].name; 156 | fingerprint[name] = result; 157 | }); 158 | 159 | const fingerprintStage2Promises = fingerprintStage2.map( 160 | (component) => ({ 161 | promise: withTimeout( 162 | component.func(fingerprint).catch((error) => { 163 | handleError(error, 'stage 2', component.name); 164 | throw error; 165 | }), 166 | timeoutMs, 167 | `OverpoweredJS: ${component.name} (stage 2) timed out`, 168 | `Stage 2 - ${component.name}` 169 | ), 170 | name: component.name, 171 | }) 172 | ); 173 | 174 | return Promise.all(fingerprintStage2Promises.map((p) => p.promise)) 175 | .then((results) => { 176 | results.forEach((result, index) => { 177 | const name = fingerprintStage2Promises[index].name; 178 | fingerprint[name] = result; 179 | }); 180 | 181 | const fingerprintStage3Promises = fingerprintStage3.map( 182 | (component) => ({ 183 | promise: withTimeout( 184 | component.func(fingerprint).catch((error) => { 185 | handleError(error, 'stage 3', component.name); 186 | throw error; 187 | }), 188 | timeoutMs, 189 | `OverpoweredJS: ${component.name} (stage 3) timed out`, 190 | `Stage 3 - ${component.name}` 191 | ), 192 | name: component.name, 193 | }) 194 | ); 195 | 196 | return Promise.all(fingerprintStage3Promises.map((p) => p.promise)) 197 | .then((results) => { 198 | results.forEach((result, index) => { 199 | const name = fingerprintStage3Promises[index].name; 200 | fingerprint[name] = result; 201 | }); 202 | 203 | const getFingerprint: GetFingerprintFunction = () => processFingerprint(fingerprint); 204 | resolve({ getFingerprint }); 205 | }) 206 | .catch((error) => { 207 | handleError(error, 'stage 3', 'unknown'); 208 | reject(new Error(`OverpoweredJS: Error in stage 3: ${error.message}`)); 209 | }); 210 | }) 211 | .catch((error) => { 212 | handleError(error, 'stage 2', 'unknown'); 213 | reject(new Error(`OverpoweredJS: Error in stage 2: ${error.message}`)); 214 | }); 215 | }) 216 | .catch((error) => { 217 | handleError(error, 'stage 1', 'unknown'); 218 | reject(new Error(`OverpoweredJS: Error in stage 1: ${error.message}`)); 219 | }); 220 | }); 221 | } 222 | 223 | if (typeof window !== 'undefined') { 224 | self.window.opjs = opjs; 225 | } 226 | 227 | // for debugging purposes only 228 | // self.window 229 | // .opjs() 230 | // .then((fp) => console.log(fp.getFingerprint())) 231 | // .catch(console.error); 232 | 233 | export default opjs; 234 | -------------------------------------------------------------------------------- /src/utils/hash.ts: -------------------------------------------------------------------------------- 1 | /* 2 | cyrb53a beta (c) 2023 bryc (github.com/bryc) 3 | License: Public domain (or MIT if needed). Attribution appreciated. 4 | This is a work-in-progress, and changes to the algorithm are expected. 5 | The original cyrb53 has a slight mixing bias in the low bits of h1. 6 | This doesn't affect collision rate, but I want to try to improve it. 7 | This new version has preliminary improvements in avalanche behavior. 8 | */ 9 | const cyrb53a_beta = (str: string, seed: number = 0): number => { 10 | let h1 = 0xdeadbeef ^ seed, 11 | h2 = 0x41c6ce57 ^ seed; 12 | for (let i = 0, ch: number; i < str.length; i++) { 13 | ch = str.charCodeAt(i); 14 | h1 = Math.imul(h1 ^ ch, 0x85ebca77); 15 | h2 = Math.imul(h2 ^ ch, 0xc2b2ae3d); 16 | } 17 | h1 ^= Math.imul(h1 ^ (h2 >>> 15), 0x735a2d97); 18 | h2 ^= Math.imul(h2 ^ (h1 >>> 15), 0xcaf649a9); 19 | h1 ^= h2 >>> 16; 20 | h2 ^= h1 >>> 16; 21 | return 2097152 * (h2 >>> 0) + (h1 >>> 11); 22 | }; 23 | 24 | export const hash = (str: string, seed: number = 420): number => { 25 | return cyrb53a_beta(str, seed); 26 | }; 27 | -------------------------------------------------------------------------------- /src/utils/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { getSpecificType } from './utils'; 2 | 3 | export interface ComponentInterface { 4 | name: string; 5 | func: ComponentFunctionInterface; 6 | } 7 | 8 | export type ComponentFunctionInterface = ( 9 | fingerprint: object 10 | ) => ComponentOutputInterface; 11 | 12 | export interface ComponentOutputInterface { 13 | status: number; 14 | value?: string | number | object | boolean | null; 15 | type?: string; 16 | isTrusted?: boolean; 17 | } 18 | 19 | export const resolveComponent = ( 20 | status: number, 21 | value?: string | number | object | boolean | null 22 | ): ComponentOutputInterface => { 23 | if (typeof value === 'object' && value !== null) { 24 | if ('status' in value) { 25 | throw new Error('Status already exists on the object'); 26 | } 27 | for (const key in value) { 28 | if (typeof value[key] === 'object' && value[key] !== null) { 29 | value[key].status = status; 30 | } 31 | } 32 | } 33 | return { 34 | status: status, 35 | value: value, 36 | type: getSpecificType(value), 37 | }; 38 | }; 39 | -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | export const getSpecificType = (variable: unknown) => { 2 | const typeofVariable = typeof variable; 3 | if (typeofVariable === 'object') { 4 | if (variable === null) { 5 | return 'null'; 6 | } 7 | if (Array.isArray(variable)) { 8 | return 'array'; 9 | } 10 | } 11 | if (typeofVariable === 'number') { 12 | if (variable === Infinity) { 13 | return 'infinity'; 14 | } 15 | if (variable === -Infinity) { 16 | return '-infinity'; 17 | } 18 | if (isNaN(variable as number)) { 19 | return 'nan'; 20 | } 21 | } 22 | return typeofVariable; 23 | }; 24 | 25 | export const getSpecificTypeAndValue = (variable: unknown) => { 26 | return { 27 | type: getSpecificType(variable), 28 | value: variable, 29 | }; 30 | }; 31 | 32 | export const stringify = (value: any) => { 33 | if (typeof JSON === 'object' && typeof JSON.stringify === 'function') { 34 | return JSON.stringify(value); 35 | } 36 | 37 | function escapeString(str: string): string { 38 | return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; 39 | } 40 | 41 | if (value === null) { 42 | return 'null'; 43 | } 44 | if (typeof value === 'number' || typeof value === 'boolean') { 45 | return String(value); 46 | } 47 | if (typeof value === 'string') { 48 | return escapeString(value); 49 | } 50 | if (typeof value === 'undefined') { 51 | return 'null'; 52 | } 53 | if (Array.isArray(value)) { 54 | const arrayValues = value.map(item => (typeof item === 'undefined' ? 'null' : stringify(item))).join(','); 55 | return '[' + arrayValues + ']'; 56 | } 57 | if (typeof value === 'object') { 58 | const objectKeys = Object.keys(value); 59 | const objectValues = objectKeys 60 | .filter(key => typeof value[key] !== 'undefined') 61 | .map(key => escapeString(key) + ':' + stringify(value[key])) 62 | .join(','); 63 | return '{' + objectValues + '}'; 64 | } 65 | return 'null'; // fallback for unsupported types 66 | }; 67 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "./src", 4 | "lib": [ 5 | "es2016", 6 | "dom" 7 | ], 8 | "outDir": "./dist", 9 | "declaration": true, 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "strict": true, 13 | "skipLibCheck": true, 14 | "noImplicitAny": false, 15 | "removeComments": false, 16 | "downlevelIteration": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const TerserPlugin = require('terser-webpack-plugin') 4 | const packageJson = require('./package.json') 5 | 6 | const currentYear = new Date().getFullYear() 7 | 8 | const bannerText = `/*! 9 | * 10 | * OverpoweredJS OS v${packageJson.version} 11 | * 12 | * https://github.com/Joe12387/overpoweredjs 13 | * 14 | * Copyright (c) 2022 - ${currentYear} Joe Rutkowski 15 | * 16 | * Released under the OverpoweredJS OS License 17 | * 18 | * This software is subject to very specific licensing terms. 19 | * You should read them before using this software in any capacity. 20 | * 21 | * DO NOT: 22 | * - Remove this banner. 23 | * - Use this software for any commercial purposes. 24 | * - Use this software to track users without their consent. 25 | * 26 | * Please see the LICENSE.md file for more information. 27 | * If you don't have a LICENSE.md file, see the above URL. 28 | * 29 | * Removing this banner is considered a violation of the OverpoweredJS OS License. 30 | * 31 | **/` 32 | 33 | class PreserveBannerPlugin { 34 | apply (compiler) { 35 | compiler.hooks.compilation.tap('PreserveBannerPlugin', (compilation) => { 36 | compilation.hooks.processAssets.tap( 37 | { 38 | name: 'PreserveBannerPlugin', 39 | stage: webpack.Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE 40 | }, 41 | (assets) => { 42 | for (const assetName in assets) { 43 | if (assetName.endsWith('.js')) { 44 | const source = assets[assetName].source() 45 | assets[assetName] = { 46 | source: () => bannerText + '\n' + source, 47 | size: () => bannerText.length + source.length 48 | } 49 | } 50 | } 51 | } 52 | ) 53 | }) 54 | } 55 | } 56 | 57 | const commonConfig = (isEsm) => ({ 58 | entry: './src/opjs.ts', 59 | mode: 'production', 60 | resolve: { 61 | extensions: ['.ts'] 62 | }, 63 | module: { 64 | rules: [ 65 | { 66 | test: /\.ts$/, 67 | use: { 68 | loader: 'ts-loader', 69 | options: { 70 | transpileOnly: true, 71 | compilerOptions: isEsm 72 | ? { 73 | module: 'es2015' 74 | } 75 | : undefined 76 | } 77 | }, 78 | exclude: /node_modules/ 79 | } 80 | ] 81 | }, 82 | optimization: { 83 | minimize: true, 84 | minimizer: [ 85 | new TerserPlugin({ 86 | terserOptions: { 87 | format: { 88 | comments: false 89 | } 90 | }, 91 | extractComments: false 92 | }) 93 | ] 94 | }, 95 | plugins: [] 96 | }) 97 | 98 | const umdConfig = { 99 | ...commonConfig(false), 100 | output: { 101 | path: path.resolve(__dirname, 'dist/es5'), 102 | filename: 'opjs.min.js', 103 | library: { 104 | name: 'opjs', 105 | type: 'umd' 106 | }, 107 | globalObject: 'this', 108 | libraryExport: 'default' 109 | }, 110 | target: ['web', 'es5'], 111 | plugins: [ 112 | ...commonConfig(false).plugins, 113 | new PreserveBannerPlugin() 114 | ] 115 | } 116 | 117 | const esmConfig = { 118 | ...commonConfig(true), 119 | output: { 120 | path: path.resolve(__dirname, 'dist'), 121 | filename: 'opjs.esm.js', 122 | library: { 123 | type: 'module' 124 | } 125 | }, 126 | experiments: { 127 | outputModule: true 128 | }, 129 | plugins: [ 130 | ...commonConfig(true).plugins, 131 | new PreserveBannerPlugin() 132 | ] 133 | } 134 | 135 | module.exports = [umdConfig, esmConfig] 136 | --------------------------------------------------------------------------------