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 +4 -11
- package/.github/workflows/commit.yml +24 -0
- package/.github/workflows/tag.yml +28 -0
- package/.nvmrc +1 -1
- package/README.md +19 -7
- package/package.json +17 -22
- package/src/index.html +45 -16
- package/src/index.js +168 -180
- package/src/index.spec.js +52 -27
- package/.babelrc +0 -1
- package/.editorconfig +0 -11
- package/.prettierignore +0 -6
- package/.prettierrc +0 -5
package/.eslintrc
CHANGED
|
@@ -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
|
-
|
|
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
|
-
```
|
|
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
|
-
- `
|
|
16
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
14
|
+
const data = hardwareOnly
|
|
15
|
+
? JSON.stringify({
|
|
16
|
+
canvas,
|
|
17
|
+
colorDepth,
|
|
18
|
+
deviceMemory,
|
|
39
19
|
devicePixelRatio,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
43
|
+
platform,
|
|
64
44
|
timezone,
|
|
45
|
+
timezoneOffset,
|
|
65
46
|
touchSupport,
|
|
66
|
-
|
|
47
|
+
userAgent,
|
|
48
|
+
vendor,
|
|
67
49
|
webgl,
|
|
68
50
|
webglInfo,
|
|
69
|
-
|
|
51
|
+
width,
|
|
52
|
+
});
|
|
70
53
|
|
|
71
|
-
|
|
54
|
+
const datastring = JSON.stringify(data, null, 4);
|
|
72
55
|
|
|
73
|
-
|
|
56
|
+
if (debug) console.log('fingerprint data', datastring);
|
|
74
57
|
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
const result = murmurhash3_32_gc(datastring);
|
|
59
|
+
return result;
|
|
77
60
|
};
|
|
78
61
|
|
|
79
62
|
export const getCanvasID = (debug) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
ctx.bindBuffer(ctx.ARRAY_BUFFER, h);
|
|
122
104
|
|
|
123
|
-
|
|
105
|
+
const i = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0]);
|
|
124
106
|
|
|
125
|
-
|
|
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
|
-
|
|
129
|
-
|
|
109
|
+
const j = ctx.createProgram();
|
|
110
|
+
const k = ctx.createShader(ctx.VERTEX_SHADER);
|
|
130
111
|
|
|
131
|
-
|
|
112
|
+
ctx.shaderSource(k, f);
|
|
113
|
+
ctx.compileShader(k);
|
|
132
114
|
|
|
133
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
190
|
+
k1 = 0;
|
|
229
191
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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 {
|
|
1
|
+
import { describe, it, beforeAll, afterAll, expect } from 'vitest';
|
|
2
2
|
import puppeteer from 'puppeteer';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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