fpscanner 0.9.4 → 0.9.5-beta.1
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/README.md +42 -15
- package/dist/detections/hasBotUserAgent.d.ts +3 -0
- package/dist/detections/hasBotUserAgent.d.ts.map +1 -0
- package/dist/detections/hasGPUMismatch.d.ts +3 -0
- package/dist/detections/hasGPUMismatch.d.ts.map +1 -0
- package/dist/detections/hasHeadlessChromeScreenResolution.d.ts.map +1 -1
- package/dist/detections/hasInconsistentEtsl.d.ts +3 -0
- package/dist/detections/hasInconsistentEtsl.d.ts.map +1 -0
- package/dist/detections/hasMismatchLanguages.d.ts +3 -0
- package/dist/detections/hasMismatchLanguages.d.ts.map +1 -0
- package/dist/detections/hasPlatformMismatch.d.ts +3 -0
- package/dist/detections/hasPlatformMismatch.d.ts.map +1 -0
- package/dist/fpScanner.cjs.js +2 -2
- package/dist/fpScanner.es.js +323 -223
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/signals/browserFeatures.d.ts +18 -0
- package/dist/signals/browserFeatures.d.ts.map +1 -1
- package/dist/signals/iframe.d.ts.map +1 -1
- package/dist/signals/worker.d.ts.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -4
- package/scripts/safe-publish.js +111 -0
- package/src/detections/hasBotUserAgent.ts +11 -0
- package/src/detections/hasGPUMismatch.ts +25 -0
- package/src/detections/hasHeadlessChromeScreenResolution.ts +0 -3
- package/src/detections/hasImpossibleDeviceMemory.ts +1 -1
- package/src/detections/hasInconsistentEtsl.ts +21 -0
- package/src/detections/hasMismatchLanguages.ts +13 -0
- package/src/detections/hasPlatformMismatch.ts +46 -0
- package/src/index.ts +48 -5
- package/src/signals/browserFeatures.ts +37 -11
- package/src/signals/iframe.ts +9 -1
- package/src/signals/worker.ts +31 -8
- package/src/types.ts +23 -0
package/dist/index.d.ts
CHANGED
|
@@ -12,10 +12,11 @@ declare class FingerprintScanner {
|
|
|
12
12
|
* Bitmasks are extensible - new boolean fields are appended without breaking existing positions.
|
|
13
13
|
*
|
|
14
14
|
* Sections:
|
|
15
|
-
* - det: fastBotDetectionDetails bitmask (
|
|
15
|
+
* - det: fastBotDetectionDetails bitmask (21 bits: headlessChromeScreenResolution, hasWebdriver,
|
|
16
16
|
* hasWebdriverWritable, hasSeleniumProperty, hasCDP, hasPlaywright, hasImpossibleDeviceMemory,
|
|
17
17
|
* hasHighCPUCount, hasMissingChromeObject, hasWebdriverIframe, hasWebdriverWorker,
|
|
18
|
-
* hasMismatchWebGLInWorker, hasMismatchPlatformIframe, hasMismatchPlatformWorker
|
|
18
|
+
* hasMismatchWebGLInWorker, hasMismatchPlatformIframe, hasMismatchPlatformWorker,
|
|
19
|
+
* hasMismatchLanguages, hasInconsistentEtsl, hasBotUserAgent, hasGPUMismatch, hasPlatformMismatch)
|
|
19
20
|
* - auto: automation bitmask (5 bits: webdriver, webdriverWritable, selenium, cdp, playwright) + hash
|
|
20
21
|
* - dev: WIDTHxHEIGHT + cpu + mem + device bitmask + hash of all device signals
|
|
21
22
|
* - brw: features.bitmask + extensions.bitmask + plugins bitmask (3 bits) + hash of browser signals
|
|
@@ -28,8 +29,7 @@ declare class FingerprintScanner {
|
|
|
28
29
|
private encryptFingerprint;
|
|
29
30
|
/**
|
|
30
31
|
* Detection rules with name and severity.
|
|
31
|
-
|
|
32
|
-
*/
|
|
32
|
+
*/
|
|
33
33
|
private getDetectionRules;
|
|
34
34
|
private runDetectionRules;
|
|
35
35
|
collectFingerprint(options?: CollectFingerprintOptions): Promise<string | Fingerprint>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2DA,OAAO,EAAE,WAAW,EAA0C,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAGzG,cAAM,kBAAkB;IACpB,OAAO,CAAC,WAAW,CAAc;;YAwMnB,aAAa;IAQ3B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,4BAA4B;YAgMtB,kBAAkB;IAoBhC;;MAEE;IACF,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,iBAAiB;IAsCnB,kBAAkB,CAAC,OAAO,GAAE,yBAA6C;CA0HlF;AAED,eAAe,kBAAkB,CAAC;AAClC,cAAc,SAAS,CAAC"}
|
|
@@ -10,5 +10,23 @@ export declare function browserFeatures(): {
|
|
|
10
10
|
webAssembly: boolean;
|
|
11
11
|
buffer: boolean;
|
|
12
12
|
showModalDialog: boolean;
|
|
13
|
+
safari: boolean;
|
|
14
|
+
webkitPrefixedFunction: boolean;
|
|
15
|
+
mozPrefixedFunction: boolean;
|
|
16
|
+
usb: boolean;
|
|
17
|
+
browserCapture: boolean;
|
|
18
|
+
paymentRequestUpdateEvent: boolean;
|
|
19
|
+
pressureObserver: boolean;
|
|
20
|
+
audioSession: boolean;
|
|
21
|
+
selectAudioOutput: boolean;
|
|
22
|
+
barcodeDetector: boolean;
|
|
23
|
+
battery: boolean;
|
|
24
|
+
devicePosture: boolean;
|
|
25
|
+
documentPictureInPicture: boolean;
|
|
26
|
+
eyeDropper: boolean;
|
|
27
|
+
editContext: boolean;
|
|
28
|
+
fencedFrame: boolean;
|
|
29
|
+
sanitizer: boolean;
|
|
30
|
+
otpCredential: boolean;
|
|
13
31
|
};
|
|
14
32
|
//# sourceMappingURL=browserFeatures.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browserFeatures.d.ts","sourceRoot":"","sources":["../../src/signals/browserFeatures.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browserFeatures.d.ts","sourceRoot":"","sources":["../../src/signals/browserFeatures.ts"],"names":[],"mappings":"AAUA,wBAAgB,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuC9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iframe.d.ts","sourceRoot":"","sources":["../../src/signals/iframe.ts"],"names":[],"mappings":"AAEA,wBAAgB,MAAM;;;;;;;
|
|
1
|
+
{"version":3,"file":"iframe.d.ts","sourceRoot":"","sources":["../../src/signals/iframe.ts"],"names":[],"mappings":"AAEA,wBAAgB,MAAM;;;;;;;EAuCrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/signals/worker.ts"],"names":[],"mappings":"AAEA,wBAAsB,MAAM,
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/signals/worker.ts"],"names":[],"mappings":"AAEA,wBAAsB,MAAM,qBAiG3B"}
|
package/dist/types.d.ts
CHANGED
|
@@ -63,6 +63,24 @@ export interface BrowserFeaturesSignal {
|
|
|
63
63
|
webAssembly: SignalValue<boolean>;
|
|
64
64
|
buffer: SignalValue<boolean>;
|
|
65
65
|
showModalDialog: SignalValue<boolean>;
|
|
66
|
+
safari: SignalValue<boolean>;
|
|
67
|
+
webkitPrefixedFunction: SignalValue<boolean>;
|
|
68
|
+
mozPrefixedFunction: SignalValue<boolean>;
|
|
69
|
+
usb: SignalValue<boolean>;
|
|
70
|
+
browserCapture: SignalValue<boolean>;
|
|
71
|
+
paymentRequestUpdateEvent: SignalValue<boolean>;
|
|
72
|
+
pressureObserver: SignalValue<boolean>;
|
|
73
|
+
audioSession: SignalValue<boolean>;
|
|
74
|
+
selectAudioOutput: SignalValue<boolean>;
|
|
75
|
+
barcodeDetector: SignalValue<boolean>;
|
|
76
|
+
battery: SignalValue<boolean>;
|
|
77
|
+
devicePosture: SignalValue<boolean>;
|
|
78
|
+
documentPictureInPicture: SignalValue<boolean>;
|
|
79
|
+
eyeDropper: SignalValue<boolean>;
|
|
80
|
+
editContext: SignalValue<boolean>;
|
|
81
|
+
fencedFrame: SignalValue<boolean>;
|
|
82
|
+
sanitizer: SignalValue<boolean>;
|
|
83
|
+
otpCredential: SignalValue<boolean>;
|
|
66
84
|
}
|
|
67
85
|
export interface MediaQueriesSignal {
|
|
68
86
|
prefersColorScheme: SignalValue<string | null>;
|
|
@@ -179,6 +197,11 @@ export interface FastBotDetectionDetails {
|
|
|
179
197
|
hasMismatchPlatformWorker: DetectionRuleResult;
|
|
180
198
|
hasSwiftshaderRenderer: DetectionRuleResult;
|
|
181
199
|
hasUTCTimezone: DetectionRuleResult;
|
|
200
|
+
hasMismatchLanguages: DetectionRuleResult;
|
|
201
|
+
hasInconsistentEtsl: DetectionRuleResult;
|
|
202
|
+
hasBotUserAgent: DetectionRuleResult;
|
|
203
|
+
hasGPUMismatch: DetectionRuleResult;
|
|
204
|
+
hasPlatformMismatch: DetectionRuleResult;
|
|
182
205
|
}
|
|
183
206
|
export interface Fingerprint {
|
|
184
207
|
signals: FingerprintSignals;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fpscanner",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5-beta.1",
|
|
4
4
|
"description": "A lightweight browser fingerprinting and bot detection library with encryption, obfuscation, and cross-context validation",
|
|
5
5
|
"main": "dist/fpScanner.cjs.js",
|
|
6
6
|
"module": "dist/fpScanner.es.js",
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
"test:vitest": "vitest",
|
|
30
30
|
"test:playwright": "npm run build:obfuscate && npx playwright test",
|
|
31
31
|
"test:playwright:headed": "npm run build:obfuscate && npx playwright test --headed",
|
|
32
|
-
"prepublishOnly": "node scripts/verify-publish.js"
|
|
32
|
+
"prepublishOnly": "node scripts/verify-publish.js",
|
|
33
|
+
"publish:beta": "node scripts/safe-publish.js beta",
|
|
34
|
+
"publish:stable": "node scripts/safe-publish.js stable"
|
|
33
35
|
},
|
|
34
36
|
"repository": {
|
|
35
37
|
"type": "git",
|
|
@@ -58,9 +60,9 @@
|
|
|
58
60
|
},
|
|
59
61
|
"homepage": "https://github.com/antoinevastel/fpscanner",
|
|
60
62
|
"dependencies": {
|
|
61
|
-
"ua-parser-js": "^0.7.18",
|
|
62
63
|
"javascript-obfuscator": "^5.1.0",
|
|
63
|
-
"terser": "^5.46.0"
|
|
64
|
+
"terser": "^5.46.0",
|
|
65
|
+
"ua-parser-js": "^0.7.18"
|
|
64
66
|
},
|
|
65
67
|
"devDependencies": {
|
|
66
68
|
"@playwright/test": "^1.57.0",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const publishType = process.argv[2]; // 'beta' or 'stable'
|
|
8
|
+
|
|
9
|
+
if (!publishType || !['beta', 'stable'].includes(publishType)) {
|
|
10
|
+
console.error('❌ Usage: npm run publish:beta or npm run publish:stable');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ROOT_DIR = path.resolve(__dirname, '..');
|
|
15
|
+
const DIST_DIR = path.join(ROOT_DIR, 'dist');
|
|
16
|
+
|
|
17
|
+
function run(command, description) {
|
|
18
|
+
console.log(`\n🔄 ${description}...`);
|
|
19
|
+
try {
|
|
20
|
+
execSync(command, { cwd: ROOT_DIR, stdio: 'inherit' });
|
|
21
|
+
console.log(`✅ ${description} - Done`);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(`❌ ${description} - Failed`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function checkGitStatus() {
|
|
29
|
+
console.log('\n🔍 Checking git status...');
|
|
30
|
+
try {
|
|
31
|
+
const status = execSync('git status --porcelain', { cwd: ROOT_DIR, encoding: 'utf8' });
|
|
32
|
+
if (status.trim()) {
|
|
33
|
+
console.error('❌ Git working directory is not clean. Please commit or stash changes first.');
|
|
34
|
+
console.error('\nUncommitted changes:');
|
|
35
|
+
console.error(status);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
console.log('✅ Git working directory is clean');
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('❌ Failed to check git status');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getPackageVersion() {
|
|
46
|
+
const packageJson = require(path.join(ROOT_DIR, 'package.json'));
|
|
47
|
+
return packageJson.version;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
51
|
+
console.log(`🚀 Safe Publish Script - ${publishType.toUpperCase()}`);
|
|
52
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
53
|
+
|
|
54
|
+
// Step 0: Check git status
|
|
55
|
+
checkGitStatus();
|
|
56
|
+
|
|
57
|
+
// Step 1: Clean dist directory
|
|
58
|
+
console.log('\n🧹 Cleaning dist directory...');
|
|
59
|
+
if (fs.existsSync(DIST_DIR)) {
|
|
60
|
+
fs.rmSync(DIST_DIR, { recursive: true, force: true });
|
|
61
|
+
console.log('✅ Dist directory cleaned');
|
|
62
|
+
} else {
|
|
63
|
+
console.log('✅ Dist directory already clean');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Step 2: Build package with sentinel key (no key injection)
|
|
67
|
+
run('npm run build', 'Building package with sentinel key');
|
|
68
|
+
|
|
69
|
+
// Step 3: Verify build
|
|
70
|
+
run('node scripts/verify-publish.js', 'Verifying build integrity');
|
|
71
|
+
|
|
72
|
+
// Step 4: Get version and confirm
|
|
73
|
+
const version = getPackageVersion();
|
|
74
|
+
console.log(`\n📦 Package version: ${version}`);
|
|
75
|
+
|
|
76
|
+
if (publishType === 'beta' && !version.includes('beta')) {
|
|
77
|
+
console.error(`❌ Version ${version} is not a beta version. Use publish:stable instead.`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (publishType === 'stable' && version.includes('beta')) {
|
|
82
|
+
console.error(`❌ Version ${version} is a beta version. Use publish:beta instead.`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Step 5: Publish
|
|
87
|
+
const publishTag = publishType === 'beta' ? '--tag beta' : '';
|
|
88
|
+
run(`npm publish ${publishTag}`, `Publishing to npm with ${publishType} tag`);
|
|
89
|
+
|
|
90
|
+
// Step 6: Create git tag
|
|
91
|
+
const gitTag = `v${version}`;
|
|
92
|
+
console.log(`\n🏷️ Creating git tag: ${gitTag}...`);
|
|
93
|
+
try {
|
|
94
|
+
execSync(`git tag ${gitTag}`, { cwd: ROOT_DIR, stdio: 'inherit' });
|
|
95
|
+
console.log(`✅ Git tag created: ${gitTag}`);
|
|
96
|
+
|
|
97
|
+
console.log('\n💡 Don\'t forget to push the tag:');
|
|
98
|
+
console.log(` git push origin ${gitTag}`);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.log(`⚠️ Git tag might already exist: ${gitTag}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log('\n═══════════════════════════════════════════════════════════');
|
|
104
|
+
console.log('✅ PUBLISH SUCCESSFUL!');
|
|
105
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
106
|
+
console.log(`\n📦 Published: fpscanner@${version}`);
|
|
107
|
+
console.log(`🏷️ Tag: ${publishType}`);
|
|
108
|
+
console.log('\n📝 Next steps:');
|
|
109
|
+
console.log(` 1. Push git tag: git push origin ${gitTag}`);
|
|
110
|
+
console.log(` 2. Test installation: npm install fpscanner@${publishType}`);
|
|
111
|
+
console.log('═══════════════════════════════════════════════════════════\n');
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Fingerprint } from "../types";
|
|
2
|
+
|
|
3
|
+
export function hasBotUserAgent(fingerprint: Fingerprint) {
|
|
4
|
+
const userAgents = [
|
|
5
|
+
fingerprint.signals.browser.userAgent,
|
|
6
|
+
fingerprint.signals.contexts.iframe.userAgent,
|
|
7
|
+
fingerprint.signals.contexts.webWorker.userAgent,
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
return userAgents.some(userAgent => /bot|headless/i.test(userAgent.toLowerCase()));
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Fingerprint } from "../types";
|
|
2
|
+
|
|
3
|
+
// For the moment, we only detect GPU mismatches related to Apple OS/GPU
|
|
4
|
+
|
|
5
|
+
export function hasGPUMismatch(fingerprint: Fingerprint) {
|
|
6
|
+
const gpu = fingerprint.signals.graphics.webgpu;
|
|
7
|
+
const webGL = fingerprint.signals.graphics.webGL;
|
|
8
|
+
const userAgent = fingerprint.signals.browser.userAgent;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// Inconsistencies around Apple OS/GPU
|
|
12
|
+
if ((webGL.vendor.includes('Apple') || webGL.renderer.includes('Apple')) && !userAgent.includes('Mac')) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (gpu.vendor.includes('apple') && !userAgent.includes('Mac')) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (gpu.vendor.includes('apple') && !webGL.renderer.includes('Apple')) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
@@ -2,9 +2,6 @@ import { Fingerprint } from '../types';
|
|
|
2
2
|
|
|
3
3
|
export function hasHeadlessChromeScreenResolution(fingerprint: Fingerprint) {
|
|
4
4
|
const screen = fingerprint.signals.device.screenResolution;
|
|
5
|
-
if (typeof screen.width !== 'number' || typeof screen.height !== 'number') {
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
5
|
|
|
9
6
|
return (screen.width === 600 && screen.height === 800) || (screen.availableWidth === 600 && screen.availableHeight === 800) || (screen.innerWidth === 600 && screen.innerHeight === 800);
|
|
10
7
|
}
|
|
@@ -5,5 +5,5 @@ export function hasImpossibleDeviceMemory(fingerprint: Fingerprint) {
|
|
|
5
5
|
return false;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
return (fingerprint.signals.device.memory >
|
|
8
|
+
return (fingerprint.signals.device.memory > 32 || fingerprint.signals.device.memory < 0.25);
|
|
9
9
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Fingerprint } from "../types";
|
|
2
|
+
|
|
3
|
+
export function hasInconsistentEtsl(fingerprint: Fingerprint) {
|
|
4
|
+
|
|
5
|
+
// On Chromium-based browsers, ETSL should be 33
|
|
6
|
+
if (fingerprint.signals.browser.features.chrome && fingerprint.signals.browser.etsl !== 33) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// On Safari, ETSL should be 37
|
|
11
|
+
if (fingerprint.signals.browser.features.safari && fingerprint.signals.browser.etsl !== 37) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// On Firefox, ETSL should be 37
|
|
16
|
+
if (fingerprint.signals.browser.userAgent.includes('Firefox') && fingerprint.signals.browser.etsl !== 37) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Fingerprint } from "../types";
|
|
2
|
+
|
|
3
|
+
export function hasMismatchLanguages(fingerprint: Fingerprint) {
|
|
4
|
+
const languages = fingerprint.signals.locale.languages.languages;
|
|
5
|
+
const language = fingerprint.signals.locale.languages.language;
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
if (language && languages && Array.isArray(languages) && languages.length > 0) {
|
|
9
|
+
return languages[0] !== language;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Fingerprint } from "../types";
|
|
2
|
+
import { ERROR, NA } from "../signals/utils";
|
|
3
|
+
|
|
4
|
+
export function hasPlatformMismatch(fingerprint: Fingerprint) {
|
|
5
|
+
const platform = fingerprint.signals.device.platform;
|
|
6
|
+
const userAgent = fingerprint.signals.browser.userAgent;
|
|
7
|
+
const highEntropyPlatform = fingerprint.signals.browser.highEntropyValues.platform;
|
|
8
|
+
|
|
9
|
+
if (userAgent.includes('Mac') && !platform.includes('Mac')) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (userAgent.includes('Windows') && !platform.includes('Win')) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (userAgent.includes('Linux') && !platform.includes('Linux')) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// Check applied only if highEntropyPlatform is not ERROR or NA
|
|
23
|
+
if (highEntropyPlatform !== ERROR && highEntropyPlatform !== NA) {
|
|
24
|
+
if (highEntropyPlatform.includes('Mac') && !platform.includes('Mac')) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (highEntropyPlatform.includes('Windows') && !platform.includes('Win')) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (highEntropyPlatform.includes('Linux') && !platform.includes('Linux')) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (highEntropyPlatform.includes('Android') && !platform.includes('Android')) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (highEntropyPlatform.includes('iOS') && !platform.includes('iOS')) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return false;
|
|
46
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,11 @@ import { hasMismatchPlatformIframe } from './detections/hasMismatchPlatformIfram
|
|
|
49
49
|
import { hasWebdriverWritable } from './detections/hasWebdriverWritable';
|
|
50
50
|
import { hasSwiftshaderRenderer } from './detections/hasSwiftshaderRenderer';
|
|
51
51
|
import { hasUTCTimezone } from './detections/hasUTCTimezone';
|
|
52
|
+
import { hasMismatchLanguages } from './detections/hasMismatchLanguages';
|
|
53
|
+
import { hasInconsistentEtsl } from './detections/hasInconsistentEtsl';
|
|
54
|
+
import { hasBotUserAgent } from './detections/hasBotUserAgent';
|
|
55
|
+
import { hasGPUMismatch } from './detections/hasGPUMismatch';
|
|
56
|
+
import { hasPlatformMismatch } from './detections/hasPlatformMismatch';
|
|
52
57
|
|
|
53
58
|
import { ERROR, HIGH, INIT, LOW, MEDIUM, SKIPPED, hashCode } from './signals/utils';
|
|
54
59
|
import { encryptString } from './crypto-helpers';
|
|
@@ -118,6 +123,24 @@ class FingerprintScanner {
|
|
|
118
123
|
webAssembly: INIT,
|
|
119
124
|
buffer: INIT,
|
|
120
125
|
showModalDialog: INIT,
|
|
126
|
+
safari: INIT,
|
|
127
|
+
webkitPrefixedFunction: INIT,
|
|
128
|
+
mozPrefixedFunction: INIT,
|
|
129
|
+
usb: INIT,
|
|
130
|
+
browserCapture: INIT,
|
|
131
|
+
paymentRequestUpdateEvent: INIT,
|
|
132
|
+
pressureObserver: INIT,
|
|
133
|
+
audioSession: INIT,
|
|
134
|
+
selectAudioOutput: INIT,
|
|
135
|
+
barcodeDetector: INIT,
|
|
136
|
+
battery: INIT,
|
|
137
|
+
devicePosture: INIT,
|
|
138
|
+
documentPictureInPicture: INIT,
|
|
139
|
+
eyeDropper: INIT,
|
|
140
|
+
editContext: INIT,
|
|
141
|
+
fencedFrame: INIT,
|
|
142
|
+
sanitizer: INIT,
|
|
143
|
+
otpCredential: INIT,
|
|
121
144
|
},
|
|
122
145
|
plugins: {
|
|
123
146
|
isValidPluginArray: INIT,
|
|
@@ -229,6 +252,11 @@ class FingerprintScanner {
|
|
|
229
252
|
hasMismatchPlatformWorker: { detected: false, severity: 'high' },
|
|
230
253
|
hasSwiftshaderRenderer: { detected: false, severity: 'low' },
|
|
231
254
|
hasUTCTimezone: { detected: false, severity: 'medium' },
|
|
255
|
+
hasMismatchLanguages: { detected: false, severity: 'low' },
|
|
256
|
+
hasInconsistentEtsl: { detected: false, severity: 'high' },
|
|
257
|
+
hasBotUserAgent: { detected: false, severity: 'high' },
|
|
258
|
+
hasGPUMismatch: { detected: false, severity: 'high' },
|
|
259
|
+
hasPlatformMismatch: { detected: false, severity: 'high' },
|
|
232
260
|
},
|
|
233
261
|
};
|
|
234
262
|
}
|
|
@@ -250,10 +278,11 @@ class FingerprintScanner {
|
|
|
250
278
|
* Bitmasks are extensible - new boolean fields are appended without breaking existing positions.
|
|
251
279
|
*
|
|
252
280
|
* Sections:
|
|
253
|
-
* - det: fastBotDetectionDetails bitmask (
|
|
281
|
+
* - det: fastBotDetectionDetails bitmask (21 bits: headlessChromeScreenResolution, hasWebdriver,
|
|
254
282
|
* hasWebdriverWritable, hasSeleniumProperty, hasCDP, hasPlaywright, hasImpossibleDeviceMemory,
|
|
255
283
|
* hasHighCPUCount, hasMissingChromeObject, hasWebdriverIframe, hasWebdriverWorker,
|
|
256
|
-
* hasMismatchWebGLInWorker, hasMismatchPlatformIframe, hasMismatchPlatformWorker
|
|
284
|
+
* hasMismatchWebGLInWorker, hasMismatchPlatformIframe, hasMismatchPlatformWorker,
|
|
285
|
+
* hasMismatchLanguages, hasInconsistentEtsl, hasBotUserAgent, hasGPUMismatch, hasPlatformMismatch)
|
|
257
286
|
* - auto: automation bitmask (5 bits: webdriver, webdriverWritable, selenium, cdp, playwright) + hash
|
|
258
287
|
* - dev: WIDTHxHEIGHT + cpu + mem + device bitmask + hash of all device signals
|
|
259
288
|
* - brw: features.bitmask + extensions.bitmask + plugins bitmask (3 bits) + hash of browser signals
|
|
@@ -270,7 +299,7 @@ class FingerprintScanner {
|
|
|
270
299
|
// Section 1: Version
|
|
271
300
|
const version = 'FS1';
|
|
272
301
|
|
|
273
|
-
// Section 2: Detection bitmask - all
|
|
302
|
+
// Section 2: Detection bitmask - all 21 fastBotDetectionDetails booleans
|
|
274
303
|
// Order matches FastBotDetectionDetails interface for consistency
|
|
275
304
|
const detBitmask = [
|
|
276
305
|
det.headlessChromeScreenResolution.detected,
|
|
@@ -289,6 +318,11 @@ class FingerprintScanner {
|
|
|
289
318
|
det.hasMismatchPlatformWorker.detected,
|
|
290
319
|
det.hasSwiftshaderRenderer.detected,
|
|
291
320
|
det.hasUTCTimezone.detected,
|
|
321
|
+
det.hasMismatchLanguages.detected,
|
|
322
|
+
det.hasInconsistentEtsl.detected,
|
|
323
|
+
det.hasBotUserAgent.detected,
|
|
324
|
+
det.hasGPUMismatch.detected,
|
|
325
|
+
det.hasPlatformMismatch.detected,
|
|
292
326
|
// Add other detection rules output here
|
|
293
327
|
].map(b => b ? '1' : '0').join('');
|
|
294
328
|
const detSection = detBitmask;
|
|
@@ -471,8 +505,7 @@ class FingerprintScanner {
|
|
|
471
505
|
|
|
472
506
|
/**
|
|
473
507
|
* Detection rules with name and severity.
|
|
474
|
-
|
|
475
|
-
*/
|
|
508
|
+
*/
|
|
476
509
|
private getDetectionRules(): DetectionRule[] {
|
|
477
510
|
return [
|
|
478
511
|
{ name: 'headlessChromeScreenResolution', severity: HIGH, test: hasHeadlessChromeScreenResolution },
|
|
@@ -491,6 +524,11 @@ class FingerprintScanner {
|
|
|
491
524
|
{ name: 'hasMismatchPlatformWorker', severity: HIGH, test: hasMismatchPlatformWorker },
|
|
492
525
|
{ name: 'hasSwiftshaderRenderer', severity: LOW, test: hasSwiftshaderRenderer },
|
|
493
526
|
{ name: 'hasUTCTimezone', severity: MEDIUM, test: hasUTCTimezone },
|
|
527
|
+
{ name: 'hasMismatchLanguages', severity: LOW, test: hasMismatchLanguages },
|
|
528
|
+
{ name: 'hasInconsistentEtsl', severity: HIGH, test: hasInconsistentEtsl },
|
|
529
|
+
{ name: 'hasBotUserAgent', severity: HIGH, test: hasBotUserAgent },
|
|
530
|
+
{ name: 'hasGPUMismatch', severity: HIGH, test: hasGPUMismatch },
|
|
531
|
+
{ name: 'hasPlatformMismatch', severity: HIGH, test: hasPlatformMismatch },
|
|
494
532
|
];
|
|
495
533
|
}
|
|
496
534
|
|
|
@@ -513,6 +551,11 @@ class FingerprintScanner {
|
|
|
513
551
|
hasMismatchPlatformWorker: { detected: false, severity: 'high' },
|
|
514
552
|
hasSwiftshaderRenderer: { detected: false, severity: 'low' },
|
|
515
553
|
hasUTCTimezone: { detected: false, severity: 'medium' },
|
|
554
|
+
hasMismatchLanguages: { detected: false, severity: 'low' },
|
|
555
|
+
hasInconsistentEtsl: { detected: false, severity: 'high' },
|
|
556
|
+
hasBotUserAgent: { detected: false, severity: 'high' },
|
|
557
|
+
hasGPUMismatch: { detected: false, severity: 'high' },
|
|
558
|
+
hasPlatformMismatch: { detected: false, severity: 'high' },
|
|
516
559
|
};
|
|
517
560
|
|
|
518
561
|
for (const rule of rules) {
|
|
@@ -1,19 +1,45 @@
|
|
|
1
1
|
import { INIT } from "./utils";
|
|
2
2
|
|
|
3
|
+
function safeCheck(check: () => boolean): boolean {
|
|
4
|
+
try {
|
|
5
|
+
return check();
|
|
6
|
+
} catch {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
export function browserFeatures() {
|
|
4
12
|
const browserFeaturesData = {
|
|
5
13
|
bitmask: INIT,
|
|
6
|
-
chrome: 'chrome' in window,
|
|
7
|
-
brave: 'brave' in navigator,
|
|
8
|
-
applePaySupport: 'ApplePaySetup' in window,
|
|
9
|
-
opera: (typeof (window as any).opr !== "undefined") ||
|
|
10
|
-
|
|
11
|
-
serial: (window.navigator as any).serial !== undefined,
|
|
12
|
-
attachShadow: !!Element.prototype.attachShadow,
|
|
13
|
-
caches: !!window.caches,
|
|
14
|
-
webAssembly: !!window.WebAssembly && !!window.WebAssembly.instantiate,
|
|
15
|
-
buffer: 'Buffer' in window,
|
|
16
|
-
showModalDialog: 'showModalDialog' in window,
|
|
14
|
+
chrome: safeCheck(() => 'chrome' in window),
|
|
15
|
+
brave: safeCheck(() => 'brave' in navigator),
|
|
16
|
+
applePaySupport: safeCheck(() => 'ApplePaySetup' in window),
|
|
17
|
+
opera: safeCheck(() => (typeof (window as any).opr !== "undefined") ||
|
|
18
|
+
(typeof (window as any).onoperadetachedviewchange === "object")),
|
|
19
|
+
serial: safeCheck(() => (window.navigator as any).serial !== undefined),
|
|
20
|
+
attachShadow: safeCheck(() => !!Element.prototype.attachShadow),
|
|
21
|
+
caches: safeCheck(() => !!window.caches),
|
|
22
|
+
webAssembly: safeCheck(() => !!window.WebAssembly && !!window.WebAssembly.instantiate),
|
|
23
|
+
buffer: safeCheck(() => 'Buffer' in window),
|
|
24
|
+
showModalDialog: safeCheck(() => 'showModalDialog' in window),
|
|
25
|
+
safari: safeCheck(() => 'safari' in window),
|
|
26
|
+
webkitPrefixedFunction: safeCheck(() => 'webkitCancelAnimationFrame' in window),
|
|
27
|
+
mozPrefixedFunction: safeCheck(() => 'mozGetUserMedia' in navigator),
|
|
28
|
+
usb: safeCheck(() => typeof (window as any).USB === 'function'),
|
|
29
|
+
browserCapture: safeCheck(() => typeof (window as any).BrowserCaptureMediaStreamTrack === 'function'),
|
|
30
|
+
paymentRequestUpdateEvent: safeCheck(() => typeof (window as any).PaymentRequestUpdateEvent === 'function'),
|
|
31
|
+
pressureObserver: safeCheck(() => typeof (window as any).PressureObserver === 'function'),
|
|
32
|
+
audioSession: safeCheck(() => 'audioSession' in navigator),
|
|
33
|
+
selectAudioOutput: safeCheck(() => typeof navigator !== 'undefined' && typeof navigator.mediaDevices !== 'undefined' && typeof (navigator.mediaDevices as any).selectAudioOutput === 'function'),
|
|
34
|
+
barcodeDetector: safeCheck(() => 'BarcodeDetector' in window),
|
|
35
|
+
battery: safeCheck(() => 'getBattery' in navigator),
|
|
36
|
+
devicePosture: safeCheck(() => 'DevicePosture' in window),
|
|
37
|
+
documentPictureInPicture: safeCheck(() => 'documentPictureInPicture' in window),
|
|
38
|
+
eyeDropper: safeCheck(() => 'EyeDropper' in window),
|
|
39
|
+
editContext: safeCheck(() => 'EditContext' in window),
|
|
40
|
+
fencedFrame: safeCheck(() => 'FencedFrameConfig' in window),
|
|
41
|
+
sanitizer: safeCheck(() => 'Sanitizer' in window),
|
|
42
|
+
otpCredential: safeCheck(() => 'OTPCredential' in window),
|
|
17
43
|
};
|
|
18
44
|
|
|
19
45
|
// set bitmask to 0/1 string based on browserFeaturesData, exclude bitmask property itself (you need to filter on the key)
|
package/src/signals/iframe.ts
CHANGED
|
@@ -10,11 +10,13 @@ export function iframe() {
|
|
|
10
10
|
language: INIT,
|
|
11
11
|
};
|
|
12
12
|
const iframe = document.createElement('iframe');
|
|
13
|
+
let iframeAdded = false;
|
|
13
14
|
|
|
14
15
|
try {
|
|
15
16
|
iframe.style.display = 'none';
|
|
16
17
|
iframe.src = 'about:blank';
|
|
17
18
|
document.body.appendChild(iframe);
|
|
19
|
+
iframeAdded = true;
|
|
18
20
|
|
|
19
21
|
const iframeWindowNavigator = (iframe.contentWindow?.navigator as any);
|
|
20
22
|
|
|
@@ -27,7 +29,13 @@ export function iframe() {
|
|
|
27
29
|
} catch (e) {
|
|
28
30
|
setObjectValues(iframeData, ERROR);
|
|
29
31
|
} finally {
|
|
30
|
-
|
|
32
|
+
if (iframeAdded) {
|
|
33
|
+
try {
|
|
34
|
+
document.body.removeChild(iframe);
|
|
35
|
+
} catch (_) {
|
|
36
|
+
// Ignore removal errors
|
|
37
|
+
}
|
|
38
|
+
}
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
return iframeData;
|