devicer.js 1.0.13 → 1.2.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/README.md +29 -20
- package/dist/libs/comparitors.js +17 -0
- package/dist/libs/confidence.js +101 -0
- package/dist/libs/registry.js +56 -0
- package/{src → dist}/libs/tlsh.js +2 -3
- package/dist/main.js +14 -0
- package/license.txt +26 -26
- package/package.json +9 -4
- package/src/libs/comparitors.ts +11 -0
- package/src/libs/confidence.ts +123 -143
- package/src/libs/defaults.ts +22 -0
- package/src/libs/registry.ts +81 -0
- package/src/libs/tlsh.ts +18 -18
- package/src/main.ts +27 -4
- package/src/tlsh.d.ts +13 -13
- package/src/types/data.ts +59 -24
- package/tests/confidence.test.ts +163 -110
- package/tests/tlsh.test.ts +69 -69
- package/tsconfig.json +114 -114
- package/src/libs/confidence.js +0 -106
- package/src/main.js +0 -7
- package/tests/comparisons.test.ts +0 -98
- package/tests/data.test.ts +0 -106
- /package/{src → dist}/types/data.js +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Comparator } from "../types/data";
|
|
2
|
+
import { initializeDefaultRegistry } from "./defaults";
|
|
3
|
+
|
|
4
|
+
interface RegistryState {
|
|
5
|
+
comparators: Record<string, Comparator>;
|
|
6
|
+
weights: Record<string, number>;
|
|
7
|
+
defaultWeight: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let registry: RegistryState = {
|
|
11
|
+
comparators: {},
|
|
12
|
+
weights: {},
|
|
13
|
+
defaultWeight: 5,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let defaultsInitialized = false;
|
|
17
|
+
|
|
18
|
+
/** Internal helper – called automatically on first use */
|
|
19
|
+
function ensureDefaults(): void {
|
|
20
|
+
if (!defaultsInitialized) {
|
|
21
|
+
initializeDefaultRegistry();
|
|
22
|
+
defaultsInitialized = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Register a custom similarity comparator for a field or nested path */
|
|
27
|
+
export function registerComparator(path: string, comparator: Comparator): void {
|
|
28
|
+
if (typeof comparator !== "function") {
|
|
29
|
+
throw new Error("Comparator must be a function returning a 0–1 similarity score");
|
|
30
|
+
}
|
|
31
|
+
registry.comparators[path] = comparator;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Register (or override) the weight for a field or nested path */
|
|
35
|
+
export function registerWeight(path: string, weight: number): void {
|
|
36
|
+
if (typeof weight !== "number" || weight < 0) {
|
|
37
|
+
throw new Error("Weight must be a non-negative number");
|
|
38
|
+
}
|
|
39
|
+
registry.weights[path] = weight;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Convenience: register weight + comparator in one call (most common pattern) */
|
|
43
|
+
export function registerPlugin(
|
|
44
|
+
path: string,
|
|
45
|
+
config: { weight?: number; comparator?: Comparator }
|
|
46
|
+
): void {
|
|
47
|
+
if (config.weight !== undefined) registerWeight(path, config.weight);
|
|
48
|
+
if (config.comparator !== undefined) registerComparator(path, config.comparator);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Change the fallback weight for any unregistered field */
|
|
52
|
+
export function setDefaultWeight(weight: number): void {
|
|
53
|
+
registry.defaultWeight = Math.max(0, weight);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Remove a registered comparator */
|
|
57
|
+
export function unregisterComparator(path: string): boolean {
|
|
58
|
+
return delete registry.comparators[path];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Remove a registered weight */
|
|
62
|
+
export function unregisterWeight(path: string): boolean {
|
|
63
|
+
return delete registry.weights[path];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Reset everything (perfect for tests) */
|
|
67
|
+
export function clearRegistry(): void {
|
|
68
|
+
registry = { comparators: {}, weights: {}, defaultWeight: 5 };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Internal only – used by createConfidenceCalculator
|
|
72
|
+
export function getGlobalRegistry(): Readonly<RegistryState> {
|
|
73
|
+
ensureDefaults();
|
|
74
|
+
return {
|
|
75
|
+
...registry,
|
|
76
|
+
comparators: { ...registry.comparators },
|
|
77
|
+
weights: { ...registry.weights },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { initializeDefaultRegistry };
|
package/src/libs/tlsh.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import hash from 'tlsh';
|
|
2
|
-
import DigestHashBuilder from 'tlsh/lib/digests/digest-hash-builder.js';
|
|
3
|
-
|
|
4
|
-
export function getHash(data: string): string {
|
|
5
|
-
// Convert the input data to a string if it's not already
|
|
6
|
-
const inputString = typeof data === 'string' ? data : JSON.stringify(data);
|
|
7
|
-
|
|
8
|
-
// Generate the TLSH hash
|
|
9
|
-
const tlshHash = hash(inputString);
|
|
10
|
-
|
|
11
|
-
// Return the hash as a string
|
|
12
|
-
return tlshHash;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function compareHashes(hash1: string, hash2: string): number {
|
|
16
|
-
const digest1 = DigestHashBuilder().withHash(hash1).build();
|
|
17
|
-
const digest2 = DigestHashBuilder().withHash(hash2).build();
|
|
18
|
-
return digest1.calculateDifference(digest2, true);
|
|
1
|
+
import hash from 'tlsh';
|
|
2
|
+
import DigestHashBuilder from 'tlsh/lib/digests/digest-hash-builder.js';
|
|
3
|
+
|
|
4
|
+
export function getHash(data: string): string {
|
|
5
|
+
// Convert the input data to a string if it's not already
|
|
6
|
+
const inputString = typeof data === 'string' ? data : JSON.stringify(data);
|
|
7
|
+
|
|
8
|
+
// Generate the TLSH hash
|
|
9
|
+
const tlshHash = hash(inputString);
|
|
10
|
+
|
|
11
|
+
// Return the hash as a string
|
|
12
|
+
return tlshHash;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function compareHashes(hash1: string, hash2: string): number {
|
|
16
|
+
const digest1 = DigestHashBuilder().withHash(hash1).build();
|
|
17
|
+
const digest2 = DigestHashBuilder().withHash(hash2).build();
|
|
18
|
+
return digest1.calculateDifference(digest2, true);
|
|
19
19
|
}
|
package/src/main.ts
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
import { FPUserDataSet, FPDataSet } from "./types/data";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { FPUserDataSet, FPDataSet } from "./types/data";
|
|
2
|
+
import { calculateConfidence, createConfidenceCalculator } from "./libs/confidence";
|
|
3
|
+
import {
|
|
4
|
+
registerComparator,
|
|
5
|
+
registerWeight,
|
|
6
|
+
registerPlugin,
|
|
7
|
+
unregisterComparator,
|
|
8
|
+
unregisterWeight,
|
|
9
|
+
setDefaultWeight,
|
|
10
|
+
clearRegistry,
|
|
11
|
+
initializeDefaultRegistry
|
|
12
|
+
} from "./libs/registry";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
type FPUserDataSet,
|
|
16
|
+
type FPDataSet,
|
|
17
|
+
calculateConfidence,
|
|
18
|
+
createConfidenceCalculator,
|
|
19
|
+
registerComparator,
|
|
20
|
+
registerWeight,
|
|
21
|
+
registerPlugin,
|
|
22
|
+
unregisterComparator,
|
|
23
|
+
unregisterWeight,
|
|
24
|
+
setDefaultWeight,
|
|
25
|
+
clearRegistry,
|
|
26
|
+
initializeDefaultRegistry
|
|
27
|
+
};
|
package/src/tlsh.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
declare module 'tlsh' {
|
|
2
|
-
function hash(data: string): string;
|
|
3
|
-
export default hash;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
declare module 'tlsh/lib/digests/digest-hash-builder.js' {
|
|
7
|
-
export default function DigestHashBuilder(): {
|
|
8
|
-
withHash: (hash: string) => {
|
|
9
|
-
build: () => {
|
|
10
|
-
calculateDifference: (other: any, normalize?: boolean) => number;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
};
|
|
1
|
+
declare module 'tlsh' {
|
|
2
|
+
function hash(data: string): string;
|
|
3
|
+
export default hash;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare module 'tlsh/lib/digests/digest-hash-builder.js' {
|
|
7
|
+
export default function DigestHashBuilder(): {
|
|
8
|
+
withHash: (hash: string) => {
|
|
9
|
+
build: () => {
|
|
10
|
+
calculateDifference: (other: any, normalize?: boolean) => number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
14
|
}
|
package/src/types/data.ts
CHANGED
|
@@ -1,25 +1,60 @@
|
|
|
1
|
-
export interface FPUserDataSet {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
export interface FPUserDataSet {
|
|
2
|
+
userAgent: string;
|
|
3
|
+
platform: string;
|
|
4
|
+
timezone: string;
|
|
5
|
+
language: string;
|
|
6
|
+
languages: string[];
|
|
7
|
+
cookieEnabled: boolean;
|
|
8
|
+
doNotTrack: string | boolean;
|
|
9
|
+
hardwareConcurrency: number;
|
|
10
|
+
deviceMemory: number | string;
|
|
11
|
+
product: string;
|
|
12
|
+
productSub: string;
|
|
13
|
+
vendor: string;
|
|
14
|
+
vendorSub: string;
|
|
15
|
+
appName: string;
|
|
16
|
+
appVersion: string;
|
|
17
|
+
appCodeName: string;
|
|
18
|
+
appMinorVersion: string;
|
|
19
|
+
buildID: string;
|
|
20
|
+
plugins: {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
}[];
|
|
24
|
+
mimeTypes: {
|
|
25
|
+
type: string;
|
|
26
|
+
suffixes: string;
|
|
27
|
+
description: string;
|
|
28
|
+
}[];
|
|
29
|
+
screen: {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
colorDepth: number;
|
|
33
|
+
pixelDepth: number;
|
|
34
|
+
orientation: {
|
|
35
|
+
type: string;
|
|
36
|
+
angle: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
fonts: string[];
|
|
40
|
+
highEntropyValues: Record<string, string | number | boolean>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type FPDataSet<T extends Record<string, any> = FPUserDataSet> = T;
|
|
44
|
+
|
|
45
|
+
export type Comparator = (value1: any, value2: any, path?: string) => number; // 0.0–1.0 similarity
|
|
46
|
+
|
|
47
|
+
export interface ComparisonOptions {
|
|
48
|
+
/** Field/path weights (higher = more important). Will be normalized automatically. */
|
|
49
|
+
weights?: Record<string, number>;
|
|
50
|
+
/** Custom similarity functions (your plugin system) */
|
|
51
|
+
comparators?: Record<string, Comparator>;
|
|
52
|
+
/** Fallback weight for any field without an explicit weight */
|
|
53
|
+
defaultWeight?: number;
|
|
54
|
+
/** How much weight to give the TLSH hash component (0–1) */
|
|
55
|
+
tlshWeight?: number;
|
|
56
|
+
/** Max recursion depth for nested objects/arrays */
|
|
57
|
+
maxDepth?: number;
|
|
58
|
+
/** Whether this calculator should use the global registry (default: true) */
|
|
59
|
+
useGlobalRegistry?: boolean;
|
|
25
60
|
}
|
package/tests/confidence.test.ts
CHANGED
|
@@ -1,110 +1,163 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { calculateConfidence } from '../src/libs/confidence';
|
|
3
|
+
import type { FPUserDataSet } from '../src/types/data';
|
|
4
|
+
|
|
5
|
+
const sampleData1: FPUserDataSet = {
|
|
6
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/121.0.0.0 Safari/537.36',
|
|
7
|
+
platform: 'Win32',
|
|
8
|
+
timezone: 'America/New_York',
|
|
9
|
+
language: 'en-US',
|
|
10
|
+
languages: ['en-US', 'en'],
|
|
11
|
+
cookieEnabled: true,
|
|
12
|
+
doNotTrack: '1',
|
|
13
|
+
hardwareConcurrency: 8,
|
|
14
|
+
deviceMemory: 8,
|
|
15
|
+
product: 'Gecko',
|
|
16
|
+
productSub: '20030107',
|
|
17
|
+
vendor: 'Google Inc.',
|
|
18
|
+
vendorSub: '',
|
|
19
|
+
appName: 'Netscape',
|
|
20
|
+
appVersion: '5.0 (Windows)',
|
|
21
|
+
appCodeName: 'Mozilla',
|
|
22
|
+
appMinorVersion: '0',
|
|
23
|
+
buildID: '20240101000000',
|
|
24
|
+
plugins: [
|
|
25
|
+
{ name: 'Chrome PDF Viewer', description: 'Portable Document Format' },
|
|
26
|
+
{ name: 'Widevine Content Decryption Module', description: 'Content Protection' },
|
|
27
|
+
],
|
|
28
|
+
mimeTypes: [
|
|
29
|
+
{ type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },
|
|
30
|
+
{ type: 'application/json', suffixes: 'json', description: 'JSON Data' },
|
|
31
|
+
],
|
|
32
|
+
screen: {
|
|
33
|
+
width: 1920,
|
|
34
|
+
height: 1080,
|
|
35
|
+
colorDepth: 24,
|
|
36
|
+
pixelDepth: 24,
|
|
37
|
+
orientation: {
|
|
38
|
+
type: 'landscape-primary',
|
|
39
|
+
angle: 0,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
fonts: ['Arial', 'Segoe UI', 'Times New Roman', 'Courier New'],
|
|
43
|
+
highEntropyValues: {
|
|
44
|
+
architecture: 'x86',
|
|
45
|
+
model: '',
|
|
46
|
+
platformVersion: '15.0.0',
|
|
47
|
+
uaFullVersion: '121.0.0.0',
|
|
48
|
+
wow64: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const sampleData2: FPUserDataSet = {
|
|
53
|
+
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
|
|
54
|
+
platform: 'Linux x86_64',
|
|
55
|
+
timezone: 'Asia/Tokyo',
|
|
56
|
+
language: 'ja-JP',
|
|
57
|
+
languages: ['ja-JP', 'ja', 'en-US'],
|
|
58
|
+
cookieEnabled: false,
|
|
59
|
+
doNotTrack: '0',
|
|
60
|
+
hardwareConcurrency: 2,
|
|
61
|
+
deviceMemory: 2,
|
|
62
|
+
product: 'Gecko',
|
|
63
|
+
productSub: '20100101',
|
|
64
|
+
vendor: 'Chromium',
|
|
65
|
+
vendorSub: 'beta',
|
|
66
|
+
appName: 'Netscape',
|
|
67
|
+
appVersion: '5.0 (X11)',
|
|
68
|
+
appCodeName: 'Mozilla',
|
|
69
|
+
appMinorVersion: '1',
|
|
70
|
+
buildID: '20240202000000',
|
|
71
|
+
plugins: [
|
|
72
|
+
{ name: 'VLC Web Plugin', description: 'VLC multimedia plugin' },
|
|
73
|
+
{ name: 'Java Plug-in', description: 'Java Runtime' },
|
|
74
|
+
],
|
|
75
|
+
mimeTypes: [
|
|
76
|
+
{ type: 'video/webm', suffixes: 'webm', description: 'WebM Video' },
|
|
77
|
+
{ type: 'application/xml', suffixes: 'xml', description: 'XML Data' },
|
|
78
|
+
],
|
|
79
|
+
screen: {
|
|
80
|
+
width: 1366,
|
|
81
|
+
height: 768,
|
|
82
|
+
colorDepth: 30,
|
|
83
|
+
pixelDepth: 30,
|
|
84
|
+
orientation: {
|
|
85
|
+
type: 'portrait-primary',
|
|
86
|
+
angle: 90,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
fonts: ['Noto Sans JP', 'Ubuntu', 'Monospace'],
|
|
90
|
+
highEntropyValues: {
|
|
91
|
+
architecture: 'arm',
|
|
92
|
+
model: 'Chromebook',
|
|
93
|
+
platformVersion: '6.1.0',
|
|
94
|
+
uaFullVersion: '122.0.0.0',
|
|
95
|
+
wow64: true,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
describe('Confidence Calculation', () => {
|
|
100
|
+
it('should calculate confidence between two user data objects', () => {
|
|
101
|
+
const confidence = calculateConfidence(sampleData1, sampleData2);
|
|
102
|
+
console.log('Confidence:', confidence);
|
|
103
|
+
expect(typeof confidence).toBe('number');
|
|
104
|
+
expect(confidence).toBeGreaterThanOrEqual(0);
|
|
105
|
+
expect(confidence).toBeLessThanOrEqual(100);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should return 100% confidence for identical user data', () => {
|
|
109
|
+
const confidence = calculateConfidence(sampleData1, sampleData1);
|
|
110
|
+
expect(confidence).toBe(100);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should return high confidence for similar user data', () => {
|
|
114
|
+
const similarData: FPUserDataSet = {
|
|
115
|
+
...sampleData1,
|
|
116
|
+
deviceMemory: 16,
|
|
117
|
+
};
|
|
118
|
+
const confidence = calculateConfidence(sampleData1, similarData);
|
|
119
|
+
console.log('Confidence for similar data:', confidence);
|
|
120
|
+
expect(confidence).toBeGreaterThan(80);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should return lower confidence for different user data', () => {
|
|
124
|
+
const confidence = calculateConfidence(sampleData1, sampleData2);
|
|
125
|
+
console.log('Confidence for different data:', confidence);
|
|
126
|
+
expect(confidence).toBeLessThan(10);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should return middling confidence for partially similar data', () => {
|
|
130
|
+
const partialData: FPUserDataSet = {
|
|
131
|
+
...sampleData1,
|
|
132
|
+
hardwareConcurrency: 4,
|
|
133
|
+
deviceMemory: 4,
|
|
134
|
+
timezone: 'Europe/London',
|
|
135
|
+
language: 'en-GB',
|
|
136
|
+
screen: {
|
|
137
|
+
...sampleData1.screen,
|
|
138
|
+
width: 1600,
|
|
139
|
+
height: 900,
|
|
140
|
+
},
|
|
141
|
+
highEntropyValues: {
|
|
142
|
+
...sampleData1.highEntropyValues,
|
|
143
|
+
architecture: 'arm',
|
|
144
|
+
platformVersion: '14.0.0',
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const confidence = calculateConfidence(sampleData1, partialData);
|
|
148
|
+
console.log('Confidence for partially similar data:', confidence);
|
|
149
|
+
expect(confidence).toBeGreaterThan(10);
|
|
150
|
+
expect(confidence).toBeLessThan(95);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should handle empty datasets and nonetypes gracefully', () => {
|
|
154
|
+
const incompleteData = {
|
|
155
|
+
...sampleData1,
|
|
156
|
+
plugins: [],
|
|
157
|
+
screen: null,
|
|
158
|
+
} as unknown as FPUserDataSet;
|
|
159
|
+
const confidence = calculateConfidence(sampleData1, incompleteData);
|
|
160
|
+
expect(confidence).toBeGreaterThan(0);
|
|
161
|
+
expect(confidence).toBeLessThan(100);
|
|
162
|
+
});
|
|
163
|
+
});
|