get-browser-fingerprint 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc CHANGED
@@ -1,12 +1,5 @@
1
1
  {
2
- "parserOptions": {
3
- "ecmaVersion": 2020,
4
- "sourceType": "module"
5
- },
6
- "env": {
7
- "es6": true,
8
- "browser": true,
9
- "node": true
10
- },
11
- "extends": ["eslint:recommended"]
12
- }
2
+ "extends": [
3
+ "eslint-config-xs/react"
4
+ ]
5
+ }
@@ -0,0 +1,24 @@
1
+ name: commit
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-22.04
11
+ timeout-minutes: 3
12
+ steps:
13
+ - name: checkout
14
+ uses: actions/checkout@v2
15
+
16
+ - name: setup node
17
+ uses: actions/setup-node@v2
18
+ with:
19
+ node-version-file: '.nvmrc'
20
+
21
+ - name: test
22
+ run: |
23
+ yarn install
24
+ yarn test
@@ -0,0 +1,28 @@
1
+ name: tag
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - v*
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-22.04
11
+ timeout-minutes: 3
12
+ steps:
13
+ - name: checkout
14
+ uses: actions/checkout@v2
15
+
16
+ - name: setup node
17
+ uses: actions/setup-node@v2
18
+ with:
19
+ node-version-file: '.nvmrc'
20
+ registry-url: https://registry.npmjs.org/
21
+
22
+ - name: release
23
+ env:
24
+ NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
25
+ run: |
26
+ yarn install
27
+ yarn test
28
+ npm publish
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- v14.5
1
+ v16.16
package/README.md CHANGED
@@ -1,21 +1,33 @@
1
1
  # get-browser-fingerprint
2
2
 
3
- Zero dependencies package exporting a single function which computes a browser fingerprint.
3
+ Zero dependencies package exporting a single, fast (<15ms) and synchronous function which computes a browser fingerprint, without requiring any permission to the user.
4
4
 
5
5
  ## Usage
6
6
 
7
7
  Get browser fingerprint:
8
- ```javascript
8
+ ```js
9
9
  import getBrowserFingerprint from 'get-browser-fingerprint';
10
10
  const fingerprint = getBrowserFingerprint();
11
11
  console.log(fingerprint);
12
12
  ```
13
13
 
14
14
  Options available:
15
- - `enableWebgl`: enable webgl renderer, 5x times slower but deadly powerful (default `false`)
16
- - `debug`: log data used to generate fingerprint to console (default `false`)
15
+ - `hardwareOnly` (default `false`): leverage only hardware info about device
16
+ - `enableWebgl` (default `false`): enable webgl renderer, ~4x times slower but adds another deadly powerful hardware detection layer on top of canvas
17
+ - `debug`: log data used to generate fingerprint to console and add canvas/webgl canvas to body to see rendered image (default `false`)
17
18
 
18
- ## Disclaimer
19
+ ⚠️ Be careful: the strongest discriminating factor is canvas token which can't be computed on old devices (eg: iPhone 6), deal accordingly ⚠️
19
20
 
20
- Be careful:
21
- - strongest discriminating factor is canvas token which can't be computed on old devices (eg: iPhone 6)
21
+ ## Development
22
+
23
+ To test locally:
24
+ ```sh
25
+ nvm install
26
+ yarn install
27
+ yarn test
28
+ ```
29
+
30
+ To run example locally:
31
+ ```sh
32
+ yarn http-server src -o -c-1 -p 80
33
+ ```
package/package.json CHANGED
@@ -1,24 +1,19 @@
1
1
  {
2
- "name": "get-browser-fingerprint",
3
- "version": "2.0.1",
4
- "author": "Damiano Barbati <damiano.barbati@gmail.com> (http://github.com/damianobarbati)",
5
- "repository": "https://github.com/damianobarbati/get-browser-fingerprint",
6
- "license": "MIT",
7
- "main": "src/index.js",
8
- "type": "module",
9
- "scripts": {
10
- "eslint": "eslint --ignore-path .gitignore",
11
- "prettier": "prettier --ignore-unknown",
12
- "test": "node src/index.spec.js || echo 'test failed'"
13
- },
14
- "devDependencies": {
15
- "@babel/eslint-parser": "^7.13.14",
16
- "eslint": "^7.26.0",
17
- "eslint-config-prettier": "^8.1.0",
18
- "eslint-plugin-editorconfig": "^3.0.2",
19
- "eslint-plugin-prettier": "^3.3.1",
20
- "http-server": "^0.12.3",
21
- "prettier": "^2.2.1",
22
- "puppeteer": "^9.1.1"
23
- }
2
+ "name": "get-browser-fingerprint",
3
+ "version": "3.0.0",
4
+ "author": "Damiano Barbati <damiano.barbati@gmail.com> (https://github.com/damianobarbati)",
5
+ "repository": "https://github.com/damianobarbati/get-browser-fingerprint",
6
+ "license": "MIT",
7
+ "main": "src/index.js",
8
+ "type": "module",
9
+ "scripts": {
10
+ "eslint": "eslint --ignore-path .gitignore --fix",
11
+ "test": "vitest run"
12
+ },
13
+ "devDependencies": {
14
+ "eslint-config-xs": "^1.6.2",
15
+ "http-server": "^14.1.1",
16
+ "puppeteer": "^19.8.0",
17
+ "vitest": "^0.29.7"
18
+ }
24
19
  }
package/src/index.html CHANGED
@@ -1,18 +1,47 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
- <body>
4
- <h1></h1>
5
- <!-- yarn http-server src -o -c-1 -->
6
- <script type="module">
7
- import getFingerprint from './index.js';
8
-
9
- const t0 = performance.now();
10
- const fingerprint = getFingerprint({ debug: true });
11
- const t1 = performance.now();
12
-
13
- const result = `Fingerprint: ${fingerprint} (in ${(t1 - t0).toFixed(0)} ms)`;
14
- console.log(result);
15
- document.querySelector('h1').innerText = result;
16
- </script>
17
- </body>
18
- </html>
3
+ <head>
4
+ <title>get-browser-fingerprint demo</title>
5
+ </head>
6
+ <body>
7
+ <h1>get-browser-fingerprint</h1>
8
+
9
+ <h2 id="default"></h2>
10
+ <h2 id="hardwareOnly"></h2>
11
+ <h2 id="enableWebgl"></h2>
12
+
13
+ <script type="module">
14
+ import getFingerprint from './index.js';
15
+
16
+ args_default: {
17
+ const t0 = performance.now();
18
+ const fingerprint = getFingerprint({ debug: true });
19
+ const t1 = performance.now();
20
+
21
+ const result = `Fingerprint: ${fingerprint} (computed in ${(t1 - t0).toFixed(0)} ms)`;
22
+ console.log(result);
23
+ document.getElementById('default').innerText = result;
24
+ }
25
+
26
+ args_hardwareOnly: {
27
+ const t0 = performance.now();
28
+ const fingerprint = getFingerprint({ hardwareOnly: true, debug: true });
29
+ const t1 = performance.now();
30
+
31
+ const result = `Fingerprint with hardwareOnly=true: ${fingerprint} (computed in ${(t1 - t0).toFixed(0)} ms)`;
32
+ console.log(result);
33
+ document.getElementById('hardwareOnly').innerText = result;
34
+ }
35
+
36
+ args_enableWebgl: {
37
+ const t0 = performance.now();
38
+ const fingerprint = getFingerprint({ enableWebgl: true, debug: true });
39
+ const t1 = performance.now();
40
+
41
+ const result = `Fingerprint with enableWebgl=true: ${fingerprint} (computed in ${(t1 - t0).toFixed(0)} ms)`;
42
+ console.log(result);
43
+ document.getElementById('enableWebgl').innerText = result;
44
+ }
45
+ </script>
46
+ </body>
47
+ </html>
package/src/index.js CHANGED
@@ -1,237 +1,225 @@
1
- export default ({ enableWebgl = false, debug = false } = {}) => {
2
- let { devicePixelRatio } = window;
3
- // weird behaviour when getting value from localhost vs ip!!!
4
- devicePixelRatio = +parseInt(devicePixelRatio);
5
-
6
- const {
7
- appName,
8
- appCodeName,
9
- appVersion,
10
- cookieEnabled,
11
- deviceMemory,
12
- doNotTrack,
13
- hardwareConcurrency,
14
- language,
15
- languages,
16
- maxTouchPoints,
17
- platform,
18
- product,
19
- productSub,
20
- userAgent,
21
- vendor,
22
- vendorSub,
23
- webdriver,
24
- } = window.navigator;
1
+ const getBrowserFingerprint = ({ hardwareOnly = false, enableWebgl = false, debug = false } = {}) => {
2
+ const { cookieEnabled, deviceMemory, doNotTrack, hardwareConcurrency, language, languages, maxTouchPoints, platform, userAgent, vendor } = window.navigator;
25
3
 
26
- const plugins = Object.entries(window.navigator.plugins).map(([, plugin]) => plugin.name);
27
- const mimeTypes = Object.entries(window.navigator.mimeTypes).map(([, mimeType]) => mimeType.type);
4
+ const { width, height, colorDepth, pixelDepth } = window.screen;
5
+ const timezoneOffset = new Date().getTimezoneOffset();
6
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
7
+ const touchSupport = 'ontouchstart' in window;
8
+ const devicePixelRatio = window.devicePixelRatio;
28
9
 
29
- const { width, height, colorDepth, pixelDepth } = window.screen;
30
- const timezoneOffset = new Date().getTimezoneOffset();
31
- const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
32
- const touchSupport = 'ontouchstart' in window;
10
+ const canvas = getCanvasID(debug);
11
+ const webgl = enableWebgl ? getWebglID(debug) : undefined; // undefined will remove this from the stringify down here
12
+ const webglInfo = enableWebgl ? getWebglInfo(debug) : undefined; // undefined will remove this from the stringify down here
33
13
 
34
- const canvas = getCanvasID(debug);
35
- const webgl = enableWebgl ? getWebglID(debug) : null;
36
- const webglInfo = getWebglInfo(debug);
37
-
38
- const data = {
14
+ const data = hardwareOnly
15
+ ? JSON.stringify({
16
+ canvas,
17
+ colorDepth,
18
+ deviceMemory,
39
19
  devicePixelRatio,
40
- appName,
41
- appCodeName,
42
- appVersion,
20
+ hardwareConcurrency,
21
+ height,
22
+ maxTouchPoints,
23
+ pixelDepth,
24
+ platform,
25
+ touchSupport,
26
+ webgl,
27
+ webglInfo,
28
+ width,
29
+ })
30
+ : JSON.stringify({
31
+ canvas,
32
+ colorDepth,
43
33
  cookieEnabled,
44
34
  deviceMemory,
35
+ devicePixelRatio,
45
36
  doNotTrack,
46
37
  hardwareConcurrency,
38
+ height,
47
39
  language,
48
40
  languages,
49
41
  maxTouchPoints,
50
- mimeTypes,
51
- platform,
52
- plugins,
53
- product,
54
- productSub,
55
- userAgent,
56
- vendor,
57
- vendorSub,
58
- webdriver,
59
- width,
60
- height,
61
- colorDepth,
62
42
  pixelDepth,
63
- timezoneOffset,
43
+ platform,
64
44
  timezone,
45
+ timezoneOffset,
65
46
  touchSupport,
66
- canvas,
47
+ userAgent,
48
+ vendor,
67
49
  webgl,
68
50
  webglInfo,
69
- };
51
+ width,
52
+ });
70
53
 
71
- const datastring = JSON.stringify(data, null, 4);
54
+ const datastring = JSON.stringify(data, null, 4);
72
55
 
73
- if (debug) console.log('fingerprint data', datastring);
56
+ if (debug) console.log('fingerprint data', datastring);
74
57
 
75
- const result = murmurhash3_32_gc(datastring);
76
- return result;
58
+ const result = murmurhash3_32_gc(datastring);
59
+ return result;
77
60
  };
78
61
 
79
62
  export const getCanvasID = (debug) => {
80
- try {
81
- const canvas = document.createElement('canvas');
82
- const ctx = canvas.getContext('2d');
83
- const text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}|;:',<.>/?";
84
- ctx.textBaseline = 'top';
85
- ctx.font = "14px 'Arial'";
86
- ctx.textBaseline = 'alphabetic';
87
- ctx.fillStyle = '#f60';
88
- ctx.fillRect(125, 1, 62, 20);
89
- ctx.fillStyle = '#069';
90
- ctx.fillText(text, 2, 15);
91
- ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
92
- ctx.fillText(text, 4, 17);
93
-
94
- const result = canvas.toDataURL();
95
-
96
- if (debug) {
97
- document.body.appendChild(canvas);
98
- } else {
99
- ctx.clearRect(0, 0, canvas.width, canvas.height);
100
- }
101
-
102
- return murmurhash3_32_gc(result);
103
- } catch {
104
- return null;
63
+ try {
64
+ const canvas = document.createElement('canvas');
65
+ const ctx = canvas.getContext('2d');
66
+ const text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}|;:',<.>/?";
67
+ ctx.textBaseline = 'top';
68
+ ctx.font = "14px 'Arial'";
69
+ ctx.textBaseline = 'alphabetic';
70
+ ctx.fillStyle = '#f60';
71
+ ctx.fillRect(125, 1, 62, 20);
72
+ ctx.fillStyle = '#069';
73
+ ctx.fillText(text, 2, 15);
74
+ ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
75
+ ctx.fillText(text, 4, 17);
76
+
77
+ const result = canvas.toDataURL();
78
+
79
+ if (debug) {
80
+ document.body.appendChild(canvas);
81
+ } else {
82
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
105
83
  }
84
+
85
+ return murmurhash3_32_gc(result);
86
+ } catch {
87
+ return null;
88
+ }
106
89
  };
107
90
 
108
91
  export const getWebglID = (debug) => {
109
- try {
110
- const canvas = document.createElement('canvas');
111
- const ctx = canvas.getContext('webgl');
112
- canvas.width = 256;
113
- canvas.height = 128;
114
-
115
- const f = 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}';
116
- const g = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}';
117
- const h = ctx.createBuffer();
92
+ try {
93
+ const canvas = document.createElement('canvas');
94
+ const ctx = canvas.getContext('webgl');
95
+ canvas.width = 256;
96
+ canvas.height = 128;
118
97
 
119
- ctx.bindBuffer(ctx.ARRAY_BUFFER, h);
98
+ const f =
99
+ 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}';
100
+ const g = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}';
101
+ const h = ctx.createBuffer();
120
102
 
121
- const i = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0]);
103
+ ctx.bindBuffer(ctx.ARRAY_BUFFER, h);
122
104
 
123
- ctx.bufferData(ctx.ARRAY_BUFFER, i, ctx.STATIC_DRAW), (h.itemSize = 3), (h.numItems = 3);
105
+ const i = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0]);
124
106
 
125
- const j = ctx.createProgram();
126
- const k = ctx.createShader(ctx.VERTEX_SHADER);
107
+ ctx.bufferData(ctx.ARRAY_BUFFER, i, ctx.STATIC_DRAW), (h.itemSize = 3), (h.numItems = 3);
127
108
 
128
- ctx.shaderSource(k, f);
129
- ctx.compileShader(k);
109
+ const j = ctx.createProgram();
110
+ const k = ctx.createShader(ctx.VERTEX_SHADER);
130
111
 
131
- const l = ctx.createShader(ctx.FRAGMENT_SHADER);
112
+ ctx.shaderSource(k, f);
113
+ ctx.compileShader(k);
132
114
 
133
- ctx.shaderSource(l, g);
134
- ctx.compileShader(l);
135
- ctx.attachShader(j, k);
136
- ctx.attachShader(j, l);
137
- ctx.linkProgram(j);
138
- ctx.useProgram(j);
115
+ const l = ctx.createShader(ctx.FRAGMENT_SHADER);
139
116
 
140
- j.vertexPosAttrib = ctx.getAttribLocation(j, 'attrVertex');
141
- j.offsetUniform = ctx.getUniformLocation(j, 'uniformOffset');
117
+ ctx.shaderSource(l, g);
118
+ ctx.compileShader(l);
119
+ ctx.attachShader(j, k);
120
+ ctx.attachShader(j, l);
121
+ ctx.linkProgram(j);
122
+ ctx.useProgram(j);
142
123
 
143
- ctx.enableVertexAttribArray(j.vertexPosArray);
144
- ctx.vertexAttribPointer(j.vertexPosAttrib, h.itemSize, ctx.FLOAT, !1, 0, 0);
145
- ctx.uniform2f(j.offsetUniform, 1, 1);
146
- ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, h.numItems);
124
+ j.vertexPosAttrib = ctx.getAttribLocation(j, 'attrVertex');
125
+ j.offsetUniform = ctx.getUniformLocation(j, 'uniformOffset');
147
126
 
148
- const n = new Uint8Array(canvas.width * canvas.height * 4);
149
- ctx.readPixels(0, 0, canvas.width, canvas.height, ctx.RGBA, ctx.UNSIGNED_BYTE, n);
127
+ ctx.enableVertexAttribArray(j.vertexPosArray);
128
+ ctx.vertexAttribPointer(j.vertexPosAttrib, h.itemSize, ctx.FLOAT, !1, 0, 0);
129
+ ctx.uniform2f(j.offsetUniform, 1, 1);
130
+ ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, h.numItems);
150
131
 
151
- const result = JSON.stringify(n).replace(/,?"[0-9]+":/g, '');
132
+ const n = new Uint8Array(canvas.width * canvas.height * 4);
133
+ ctx.readPixels(0, 0, canvas.width, canvas.height, ctx.RGBA, ctx.UNSIGNED_BYTE, n);
152
134
 
153
- if (debug) {
154
- document.body.appendChild(canvas);
155
- } else {
156
- ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
157
- }
135
+ const result = JSON.stringify(n).replace(/,?"[0-9]+":/g, '');
158
136
 
159
- return murmurhash3_32_gc(result);
160
- } catch {
161
- return null;
137
+ if (debug) {
138
+ document.body.appendChild(canvas);
139
+ } else {
140
+ ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
162
141
  }
142
+
143
+ return murmurhash3_32_gc(result);
144
+ } catch {
145
+ return null;
146
+ }
163
147
  };
164
148
 
165
149
  export const getWebglInfo = () => {
166
- try {
167
- const ctx = document.createElement('canvas').getContext('webgl');
168
-
169
- const result = {
170
- VERSION: ctx.getParameter(ctx.VERSION),
171
- SHADING_LANGUAGE_VERSION: ctx.getParameter(ctx.SHADING_LANGUAGE_VERSION),
172
- VENDOR: ctx.getParameter(ctx.VENDOR),
173
- SUPORTED_EXTENSIONS: ctx.getSupportedExtensions(),
174
- };
175
-
176
- return result;
177
- } catch {
178
- return null;
179
- }
150
+ try {
151
+ const ctx = document.createElement('canvas').getContext('webgl');
152
+
153
+ const result = {
154
+ VERSION: ctx.getParameter(ctx.VERSION),
155
+ SHADING_LANGUAGE_VERSION: ctx.getParameter(ctx.SHADING_LANGUAGE_VERSION),
156
+ VENDOR: ctx.getParameter(ctx.VENDOR),
157
+ SUPORTED_EXTENSIONS: ctx.getSupportedExtensions(),
158
+ };
159
+
160
+ return result;
161
+ } catch {
162
+ return null;
163
+ }
180
164
  };
181
165
 
182
166
  export const murmurhash3_32_gc = (key) => {
183
- const remainder = key.length & 3; // key.length % 4
184
- const bytes = key.length - remainder;
185
- const c1 = 0xcc9e2d51;
186
- const c2 = 0x1b873593;
187
-
188
- let h1, h1b, k1;
189
-
190
- for (let i = 0; i < bytes; i++) {
191
- k1 = (key.charCodeAt(i) & 0xff) | ((key.charCodeAt(++i) & 0xff) << 8) | ((key.charCodeAt(++i) & 0xff) << 16) | ((key.charCodeAt(++i) & 0xff) << 24);
192
- ++i;
193
-
194
- k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
195
- k1 = (k1 << 15) | (k1 >>> 17);
196
- k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
167
+ const remainder = key.length & 3; // key.length % 4
168
+ const bytes = key.length - remainder;
169
+ const c1 = 0xcc9e2d51;
170
+ const c2 = 0x1b873593;
197
171
 
198
- h1 ^= k1;
199
- h1 = (h1 << 13) | (h1 >>> 19);
200
- h1b = ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
201
- h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
202
- }
172
+ let h1, h1b, k1;
203
173
 
204
- const i = bytes - 1;
205
-
206
- k1 = 0;
207
-
208
- switch (remainder) {
209
- case 3: {
210
- k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
211
- break;
212
- }
213
- case 2: {
214
- k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
215
- break;
216
- }
217
- case 1: {
218
- k1 ^= key.charCodeAt(i) & 0xff;
219
- break;
220
- }
221
- }
174
+ for (let i = 0; i < bytes; i++) {
175
+ k1 = (key.charCodeAt(i) & 0xff) | ((key.charCodeAt(++i) & 0xff) << 8) | ((key.charCodeAt(++i) & 0xff) << 16) | ((key.charCodeAt(++i) & 0xff) << 24);
176
+ ++i;
222
177
 
223
178
  k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
224
179
  k1 = (k1 << 15) | (k1 >>> 17);
225
180
  k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
181
+
226
182
  h1 ^= k1;
183
+ h1 = (h1 << 13) | (h1 >>> 19);
184
+ h1b = ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
185
+ h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
186
+ }
187
+
188
+ const i = bytes - 1;
227
189
 
228
- h1 ^= key.length;
190
+ k1 = 0;
229
191
 
230
- h1 ^= h1 >>> 16;
231
- h1 = ((h1 & 0xffff) * 0x85ebca6b + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
232
- h1 ^= h1 >>> 13;
233
- h1 = ((h1 & 0xffff) * 0xc2b2ae35 + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 0xffffffff;
234
- h1 ^= h1 >>> 16;
192
+ switch (remainder) {
193
+ case 3: {
194
+ k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
195
+ break;
196
+ }
197
+ case 2: {
198
+ k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
199
+ break;
200
+ }
201
+ case 1: {
202
+ k1 ^= key.charCodeAt(i) & 0xff;
203
+ break;
204
+ }
205
+ }
206
+
207
+ k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
208
+ k1 = (k1 << 15) | (k1 >>> 17);
209
+ k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
210
+ h1 ^= k1;
235
211
 
236
- return h1 >>> 0;
212
+ h1 ^= key.length;
213
+
214
+ h1 ^= h1 >>> 16;
215
+ h1 = ((h1 & 0xffff) * 0x85ebca6b + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
216
+ h1 ^= h1 >>> 13;
217
+ h1 = ((h1 & 0xffff) * 0xc2b2ae35 + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 0xffffffff;
218
+ h1 ^= h1 >>> 16;
219
+
220
+ return h1 >>> 0;
237
221
  };
222
+
223
+ window.getBrowserFingerprint = getBrowserFingerprint;
224
+
225
+ export default getBrowserFingerprint;
package/src/index.spec.js CHANGED
@@ -1,28 +1,53 @@
1
- import { strict as assert } from 'assert';
1
+ import { describe, it, beforeAll, afterAll, expect } from 'vitest';
2
2
  import puppeteer from 'puppeteer';
3
- import getBrowserFingerprint from './index.js';
4
-
5
- (async () => {
6
- await (async () => {
7
- const browser = await puppeteer.launch();
8
- const page = await browser.newPage();
9
- const result = await page.evaluate(getBrowserFingerprint);
10
- await browser.close();
11
-
12
- assert.deepStrictEqual(Number.isInteger(result), true, 'fingerprint is not an integer');
13
- assert.deepStrictEqual(String(result).length > 7, true, 'fingerprint is not long enough');
14
- })();
15
-
16
- await (async () => {
17
- const browser = await puppeteer.launch();
18
- const page = await browser.newPage();
19
- const result = await page.evaluate(getBrowserFingerprint, true);
20
- await browser.close();
21
-
22
- assert.deepStrictEqual(Number.isInteger(result), true, 'fingerprint is not an integer');
23
- assert.deepStrictEqual(String(result).length > 7, true, 'fingerprint is not long enough');
24
- })();
25
- })()
26
- .then(console.log)
27
- .catch(console.error)
28
- .finally(process.exit);
3
+
4
+ describe('getBrowserFingerprint', () => {
5
+ let browser, page;
6
+
7
+ beforeAll(async () => {
8
+ browser = await puppeteer.launch({
9
+ // headless: false,
10
+ // devtools: true,
11
+ });
12
+ page = await browser.newPage();
13
+
14
+ await page.addScriptTag({
15
+ type: 'module',
16
+ path: './src/index.js',
17
+ });
18
+ });
19
+
20
+ afterAll(async () => {
21
+ await browser.close();
22
+ });
23
+
24
+ it('works without args', async () => {
25
+ const result = await page.evaluate(() => {
26
+ const result = window.getBrowserFingerprint();
27
+ return result;
28
+ });
29
+
30
+ expect(typeof result).toBe('number');
31
+ expect(String(result).length).toBeGreaterThanOrEqual(7);
32
+ });
33
+
34
+ it('works without hardwareOnly=true', async () => {
35
+ const result = await page.evaluate(() => {
36
+ const result = window.getBrowserFingerprint();
37
+ return result;
38
+ });
39
+
40
+ expect(typeof result).toBe('number');
41
+ expect(String(result).length).toBeGreaterThanOrEqual(7);
42
+ });
43
+
44
+ it('works with enableWebgl=true', async () => {
45
+ const result = await page.evaluate(() => {
46
+ const result = window.getBrowserFingerprint({ enableWebgl: true });
47
+ return result;
48
+ });
49
+
50
+ expect(typeof result).toBe('number');
51
+ expect(String(result).length).toBeGreaterThanOrEqual(7);
52
+ });
53
+ });
package/.babelrc DELETED
@@ -1 +0,0 @@
1
- {}
package/.editorconfig DELETED
@@ -1,11 +0,0 @@
1
- [*]
2
- charset = utf-8
3
- end_of_line = lf
4
- indent_style = space
5
- indent_size = 4
6
- insert_final_newline = true
7
- trim_trailing_whitespace = true
8
-
9
- # don't trim spaces in markdown files because 2 spaces = new line
10
- [*.md]
11
- trim_trailing_whitespace = false
package/.prettierignore DELETED
@@ -1,6 +0,0 @@
1
- *.md
2
- .idea/
3
- node_modules/
4
- coverage/
5
- .cache/
6
- .DS_Store
package/.prettierrc DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "printWidth": 200,
3
- "semi": true,
4
- "singleQuote": true
5
- }