fpscanner 0.2.0 → 0.9.2

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.
Files changed (168) hide show
  1. package/README.md +639 -55
  2. package/bin/cli.js +216 -0
  3. package/dist/crypto-helpers.d.ts +19 -0
  4. package/dist/crypto-helpers.d.ts.map +1 -0
  5. package/dist/detections/hasCDP.d.ts +3 -0
  6. package/dist/detections/hasCDP.d.ts.map +1 -0
  7. package/dist/detections/hasContextMismatch.d.ts +3 -0
  8. package/dist/detections/hasContextMismatch.d.ts.map +1 -0
  9. package/dist/detections/hasHeadlessChromeScreenResolution.d.ts +3 -0
  10. package/dist/detections/hasHeadlessChromeScreenResolution.d.ts.map +1 -0
  11. package/dist/detections/hasHighCPUCount.d.ts +3 -0
  12. package/dist/detections/hasHighCPUCount.d.ts.map +1 -0
  13. package/dist/detections/hasImpossibleDeviceMemory.d.ts +3 -0
  14. package/dist/detections/hasImpossibleDeviceMemory.d.ts.map +1 -0
  15. package/dist/detections/hasMismatchPlatformIframe.d.ts +3 -0
  16. package/dist/detections/hasMismatchPlatformIframe.d.ts.map +1 -0
  17. package/dist/detections/hasMismatchPlatformWorker.d.ts +3 -0
  18. package/dist/detections/hasMismatchPlatformWorker.d.ts.map +1 -0
  19. package/dist/detections/hasMismatchWebGLInWorker.d.ts +3 -0
  20. package/dist/detections/hasMismatchWebGLInWorker.d.ts.map +1 -0
  21. package/dist/detections/hasMissingChromeObject.d.ts +3 -0
  22. package/dist/detections/hasMissingChromeObject.d.ts.map +1 -0
  23. package/dist/detections/hasPlaywright.d.ts +3 -0
  24. package/dist/detections/hasPlaywright.d.ts.map +1 -0
  25. package/dist/detections/hasSeleniumProperty.d.ts +3 -0
  26. package/dist/detections/hasSeleniumProperty.d.ts.map +1 -0
  27. package/dist/detections/hasSwiftshaderRenderer.d.ts +3 -0
  28. package/dist/detections/hasSwiftshaderRenderer.d.ts.map +1 -0
  29. package/dist/detections/hasUTCTimezone.d.ts +3 -0
  30. package/dist/detections/hasUTCTimezone.d.ts.map +1 -0
  31. package/dist/detections/hasWebdriver.d.ts +3 -0
  32. package/dist/detections/hasWebdriver.d.ts.map +1 -0
  33. package/dist/detections/hasWebdriverIframe.d.ts +3 -0
  34. package/dist/detections/hasWebdriverIframe.d.ts.map +1 -0
  35. package/dist/detections/hasWebdriverWorker.d.ts +3 -0
  36. package/dist/detections/hasWebdriverWorker.d.ts.map +1 -0
  37. package/dist/detections/hasWebdriverWritable.d.ts +3 -0
  38. package/dist/detections/hasWebdriverWritable.d.ts.map +1 -0
  39. package/dist/fpScanner.cjs.js +31 -0
  40. package/dist/fpScanner.es.js +1066 -0
  41. package/dist/index.d.ts +39 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/signals/browserExtensions.d.ts +5 -0
  44. package/dist/signals/browserExtensions.d.ts.map +1 -0
  45. package/dist/signals/browserFeatures.d.ts +14 -0
  46. package/dist/signals/browserFeatures.d.ts.map +1 -0
  47. package/dist/signals/canvas.d.ts +6 -0
  48. package/dist/signals/canvas.d.ts.map +1 -0
  49. package/dist/signals/cdp.d.ts +2 -0
  50. package/dist/signals/cdp.d.ts.map +1 -0
  51. package/dist/signals/cpuCount.d.ts +2 -0
  52. package/dist/signals/cpuCount.d.ts.map +1 -0
  53. package/dist/signals/etsl.d.ts +2 -0
  54. package/dist/signals/etsl.d.ts.map +1 -0
  55. package/dist/signals/highEntropyValues.d.ts +11 -0
  56. package/dist/signals/highEntropyValues.d.ts.map +1 -0
  57. package/dist/signals/iframe.d.ts +9 -0
  58. package/dist/signals/iframe.d.ts.map +1 -0
  59. package/dist/signals/internationalization.d.ts +5 -0
  60. package/dist/signals/internationalization.d.ts.map +1 -0
  61. package/dist/signals/languages.d.ts +5 -0
  62. package/dist/signals/languages.d.ts.map +1 -0
  63. package/dist/signals/maths.d.ts +2 -0
  64. package/dist/signals/maths.d.ts.map +1 -0
  65. package/dist/signals/mediaCodecs.d.ts +11 -0
  66. package/dist/signals/mediaCodecs.d.ts.map +1 -0
  67. package/dist/signals/mediaQueries.d.ts +13 -0
  68. package/dist/signals/mediaQueries.d.ts.map +1 -0
  69. package/dist/signals/memory.d.ts +2 -0
  70. package/dist/signals/memory.d.ts.map +1 -0
  71. package/dist/signals/multimediaDevices.d.ts +2 -0
  72. package/dist/signals/multimediaDevices.d.ts.map +1 -0
  73. package/dist/signals/navigatorPropertyDescriptors.d.ts +2 -0
  74. package/dist/signals/navigatorPropertyDescriptors.d.ts.map +1 -0
  75. package/dist/signals/nonce.d.ts +2 -0
  76. package/dist/signals/nonce.d.ts.map +1 -0
  77. package/dist/signals/platform.d.ts +2 -0
  78. package/dist/signals/platform.d.ts.map +1 -0
  79. package/dist/signals/playwright.d.ts +2 -0
  80. package/dist/signals/playwright.d.ts.map +1 -0
  81. package/dist/signals/plugins.d.ts +9 -0
  82. package/dist/signals/plugins.d.ts.map +1 -0
  83. package/dist/signals/screenResolution.d.ts +12 -0
  84. package/dist/signals/screenResolution.d.ts.map +1 -0
  85. package/dist/signals/seleniumProperties.d.ts +2 -0
  86. package/dist/signals/seleniumProperties.d.ts.map +1 -0
  87. package/dist/signals/time.d.ts +2 -0
  88. package/dist/signals/time.d.ts.map +1 -0
  89. package/dist/signals/toSourceError.d.ts +5 -0
  90. package/dist/signals/toSourceError.d.ts.map +1 -0
  91. package/dist/signals/url.d.ts +2 -0
  92. package/dist/signals/url.d.ts.map +1 -0
  93. package/dist/signals/userAgent.d.ts +2 -0
  94. package/dist/signals/userAgent.d.ts.map +1 -0
  95. package/dist/signals/utils.d.ts +11 -0
  96. package/dist/signals/utils.d.ts.map +1 -0
  97. package/dist/signals/webGL.d.ts +5 -0
  98. package/dist/signals/webGL.d.ts.map +1 -0
  99. package/dist/signals/webdriver.d.ts +2 -0
  100. package/dist/signals/webdriver.d.ts.map +1 -0
  101. package/dist/signals/webdriverWritable.d.ts +2 -0
  102. package/dist/signals/webdriverWritable.d.ts.map +1 -0
  103. package/dist/signals/webgpu.d.ts +7 -0
  104. package/dist/signals/webgpu.d.ts.map +1 -0
  105. package/dist/signals/worker.d.ts +2 -0
  106. package/dist/signals/worker.d.ts.map +1 -0
  107. package/dist/types.d.ts +207 -0
  108. package/dist/types.d.ts.map +1 -0
  109. package/package.json +58 -15
  110. package/scripts/build-custom.js +246 -0
  111. package/src/crypto-helpers.ts +50 -0
  112. package/src/detections/hasCDP.ts +5 -0
  113. package/src/detections/hasContextMismatch.ts +19 -0
  114. package/src/detections/hasHeadlessChromeScreenResolution.ts +10 -0
  115. package/src/detections/hasHighCPUCount.ts +9 -0
  116. package/src/detections/hasImpossibleDeviceMemory.ts +9 -0
  117. package/src/detections/hasMismatchPlatformIframe.ts +10 -0
  118. package/src/detections/hasMismatchPlatformWorker.ts +10 -0
  119. package/src/detections/hasMismatchWebGLInWorker.ts +13 -0
  120. package/src/detections/hasMissingChromeObject.ts +6 -0
  121. package/src/detections/hasPlaywright.ts +5 -0
  122. package/src/detections/hasSeleniumProperty.ts +5 -0
  123. package/src/detections/hasSwiftshaderRenderer.ts +5 -0
  124. package/src/detections/hasUTCTimezone.ts +5 -0
  125. package/src/detections/hasWebdriver.ts +5 -0
  126. package/src/detections/hasWebdriverIframe.ts +5 -0
  127. package/src/detections/hasWebdriverWorker.ts +5 -0
  128. package/src/detections/hasWebdriverWritable.ts +5 -0
  129. package/src/globals.d.ts +10 -0
  130. package/src/index.ts +644 -0
  131. package/src/signals/browserExtensions.ts +57 -0
  132. package/src/signals/browserFeatures.ts +24 -0
  133. package/src/signals/canvas.ts +84 -0
  134. package/src/signals/cdp.ts +18 -0
  135. package/src/signals/cpuCount.ts +5 -0
  136. package/src/signals/etsl.ts +3 -0
  137. package/src/signals/highEntropyValues.ts +48 -0
  138. package/src/signals/iframe.ts +34 -0
  139. package/src/signals/internationalization.ts +24 -0
  140. package/src/signals/languages.ts +6 -0
  141. package/src/signals/maths.ts +30 -0
  142. package/src/signals/mediaCodecs.ts +120 -0
  143. package/src/signals/mediaQueries.ts +85 -0
  144. package/src/signals/memory.ts +5 -0
  145. package/src/signals/multimediaDevices.ts +34 -0
  146. package/src/signals/navigatorPropertyDescriptors.ts +17 -0
  147. package/src/signals/nonce.ts +3 -0
  148. package/src/signals/platform.ts +3 -0
  149. package/src/signals/playwright.ts +3 -0
  150. package/src/signals/plugins.ts +70 -0
  151. package/src/signals/screenResolution.ts +15 -0
  152. package/src/signals/seleniumProperties.ts +40 -0
  153. package/src/signals/time.ts +3 -0
  154. package/src/signals/toSourceError.ts +27 -0
  155. package/src/signals/url.ts +3 -0
  156. package/src/signals/userAgent.ts +3 -0
  157. package/src/signals/utils.ts +29 -0
  158. package/src/signals/webGL.ts +28 -0
  159. package/src/signals/webdriver.ts +3 -0
  160. package/src/signals/webdriverWritable.ts +15 -0
  161. package/src/signals/webgpu.ts +28 -0
  162. package/src/signals/worker.ts +77 -0
  163. package/src/types.ts +237 -0
  164. package/.babelrc +0 -3
  165. package/.travis.yml +0 -17
  166. package/src/fpScanner.js +0 -222
  167. package/test/test.html +0 -11
  168. package/test/test.js +0 -116
@@ -0,0 +1,57 @@
1
+ import { INIT } from "./utils";
2
+
3
+ export function browserExtensions() {
4
+ const browserExtensionsData = {
5
+ bitmask: INIT,
6
+ extensions: [] as string[],
7
+ };
8
+
9
+ const hasGrammarly = document.body.hasAttribute('data-gr-ext-installed');
10
+ const hasMetamask = typeof (window as any).ethereum !=='undefined';
11
+ const hasCouponBirds = document.getElementById('coupon-birds-drop-div') !== null;
12
+ const hasDeepL = document.querySelector('deepl-input-controller') !== null;
13
+ const hasMonicaAI = document.getElementById('monica-content-root') !== null;
14
+ const hasSiderAI = document.querySelector('chatgpt-sidebar') !== null;
15
+ const hasRequestly = typeof (window as any).__REQUESTLY__ !== 'undefined';
16
+ const hasVeepn = Array.from(document.querySelectorAll('*'))
17
+ .filter(el => el.tagName.toLowerCase().startsWith('veepn-')).length > 0;
18
+
19
+ browserExtensionsData.bitmask = [
20
+ hasGrammarly ? '1' : '0',
21
+ hasMetamask ? '1' : '0',
22
+ hasCouponBirds ? '1' : '0',
23
+ hasDeepL ? '1' : '0',
24
+ hasMonicaAI ? '1' : '0',
25
+ hasSiderAI ? '1' : '0',
26
+ hasRequestly ? '1' : '0',
27
+ hasVeepn ? '1' : '0',
28
+ ].join('');
29
+
30
+
31
+ if (hasGrammarly) {
32
+ browserExtensionsData.extensions.push('grammarly');
33
+ }
34
+ if (hasMetamask) {
35
+ browserExtensionsData.extensions.push('metamask');
36
+ }
37
+ if (hasCouponBirds) {
38
+ browserExtensionsData.extensions.push('coupon-birds');
39
+ }
40
+ if (hasDeepL) {
41
+ browserExtensionsData.extensions.push('deepl');
42
+ }
43
+ if (hasMonicaAI) {
44
+ browserExtensionsData.extensions.push('monica-ai');
45
+ }
46
+ if (hasSiderAI) {
47
+ browserExtensionsData.extensions.push('sider-ai');
48
+ }
49
+ if (hasRequestly) {
50
+ browserExtensionsData.extensions.push('requestly');
51
+ }
52
+ if (hasVeepn) {
53
+ browserExtensionsData.extensions.push('veepn');
54
+ }
55
+
56
+ return browserExtensionsData;
57
+ }
@@ -0,0 +1,24 @@
1
+ import { INIT } from "./utils";
2
+
3
+ export function browserFeatures() {
4
+ const browserFeaturesData = {
5
+ 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
+ (typeof (window as any).onoperadetachedviewchange === "object"),
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,
17
+ };
18
+
19
+ // set bitmask to 0/1 string based on browserFeaturesData, exclude bitmask property itself (you need to filter on the key)
20
+ // use the filter function to exclude the bitmask property itself
21
+ const bitmask = Object.keys(browserFeaturesData).filter((key) => key !== 'bitmask').map(key => (browserFeaturesData as any)[key] ? '1' : '0').join('');
22
+ browserFeaturesData.bitmask = bitmask;
23
+ return browserFeaturesData;
24
+ }
@@ -0,0 +1,84 @@
1
+ import { ERROR, INIT, hashCode } from './utils';
2
+ import { SignalValue } from '../types';
3
+
4
+ async function hasModifiedCanvas(): Promise<SignalValue<boolean>> {
5
+ return new Promise((resolve) => {
6
+
7
+ try {
8
+ const img = new Image();
9
+ const ctx = document.createElement('canvas').getContext('2d') as CanvasRenderingContext2D;
10
+ img.onload = () => {
11
+ ctx.drawImage(img, 0, 0);
12
+ resolve(ctx.getImageData(0, 0, 1, 1).data.filter(x => x === 0).length != 4);
13
+ };
14
+
15
+ img.onerror = () => {
16
+ resolve(ERROR);
17
+ };
18
+ img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';
19
+ } catch (e) {
20
+ resolve(ERROR);
21
+ }
22
+ });
23
+ }
24
+
25
+
26
+ function getCanvasFingerprint(): SignalValue<string> {
27
+ var canvas = document.createElement('canvas');
28
+ canvas.width = 400;
29
+ canvas.height = 200;
30
+ canvas.style.display = "inline";
31
+ var context = canvas.getContext("2d") as CanvasRenderingContext2D;
32
+
33
+ try {
34
+ context.rect(0, 0, 10, 10);
35
+ context.rect(2, 2, 6, 6);
36
+ context.textBaseline = "alphabetic";
37
+ context.fillStyle = "#f60";
38
+ context.fillRect(125, 1, 62, 20);
39
+ context.fillStyle = "#069";
40
+ context.font = "11pt no-real-font-123";
41
+ context.fillText("Cwm fjordbank glyphs vext quiz, 😃", 2, 15);
42
+ context.fillStyle = "rgba(102, 204, 0, 0.2)";
43
+ context.font = "18pt Arial";
44
+ context.fillText("Cwm fjordbank glyphs vext quiz, 😃", 4, 45);
45
+
46
+ context.globalCompositeOperation = "multiply";
47
+ context.fillStyle = "rgb(255,0,255)";
48
+ context.beginPath();
49
+ context.arc(50, 50, 50, 0, 2 * Math.PI, !0);
50
+ context.closePath();
51
+ context.fill();
52
+ context.fillStyle = "rgb(0,255,255)";
53
+ context.beginPath();
54
+ context.arc(100, 50, 50, 0, 2 * Math.PI, !0);
55
+ context.closePath();
56
+ context.fill();
57
+ context.fillStyle = "rgb(255,255,0)";
58
+ context.beginPath();
59
+ context.arc(75, 100, 50, 0, 2 * Math.PI, !0);
60
+ context.closePath();
61
+ context.fill();
62
+ context.fillStyle = "rgb(255,0,255)";
63
+ context.arc(75, 75, 75, 0, 2 * Math.PI, !0);
64
+ context.arc(75, 75, 25, 0, 2 * Math.PI, !0);
65
+ context.fill("evenodd");
66
+ return hashCode(canvas.toDataURL());
67
+
68
+ } catch (e) {
69
+ return ERROR;
70
+ }
71
+ }
72
+
73
+ export async function canvas() {
74
+ const canvasData = {
75
+ hasModifiedCanvas: INIT as SignalValue<boolean>,
76
+ canvasFingerprint: INIT as SignalValue<string>,
77
+ };
78
+
79
+ canvasData.hasModifiedCanvas = await hasModifiedCanvas();
80
+
81
+ canvasData.canvasFingerprint = getCanvasFingerprint();
82
+
83
+ return canvasData;
84
+ }
@@ -0,0 +1,18 @@
1
+ import { ERROR } from './utils';
2
+
3
+ export function cdp() {
4
+ try {
5
+ let wasAccessed = false;
6
+ const originalPrepareStackTrace = (Error as any).prepareStackTrace;
7
+ (Error as any).prepareStackTrace = function () {
8
+ wasAccessed = true;
9
+ return originalPrepareStackTrace;
10
+ };
11
+ const err = new Error('');
12
+ console.log(err);
13
+
14
+ return wasAccessed;
15
+ } catch (e) {
16
+ return ERROR;
17
+ }
18
+ }
@@ -0,0 +1,5 @@
1
+ import { NA } from './utils';
2
+
3
+ export function cpuCount() {
4
+ return navigator.hardwareConcurrency || NA;
5
+ }
@@ -0,0 +1,3 @@
1
+ export function etsl() {
2
+ return eval.toString().length;
3
+ }
@@ -0,0 +1,48 @@
1
+ import { ERROR, INIT, NA, setObjectValues } from "./utils";
2
+
3
+ export async function highEntropyValues() {
4
+ const navigator = window.navigator as any;
5
+ const highEntropyValues = {
6
+ architecture: INIT,
7
+ bitness: INIT,
8
+ brands: INIT,
9
+ mobile: INIT,
10
+ model: INIT,
11
+ platform: INIT,
12
+ platformVersion: INIT,
13
+ uaFullVersion: INIT,
14
+ };
15
+
16
+ if ('userAgentData' in navigator) {
17
+ try {
18
+ const ua = await navigator.userAgentData.getHighEntropyValues([
19
+ "architecture",
20
+ "bitness",
21
+ "brands",
22
+ "mobile",
23
+ "model",
24
+ "platform",
25
+ "platformVersion",
26
+ "uaFullVersion"
27
+ ]);
28
+
29
+ highEntropyValues.architecture = ua.architecture;
30
+ highEntropyValues.bitness = ua.bitness;
31
+ highEntropyValues.brands = ua.brands;
32
+ highEntropyValues.mobile = ua.mobile;
33
+ highEntropyValues.model = ua.model;
34
+ highEntropyValues.platform = ua.platform;
35
+ highEntropyValues.platformVersion = ua.platformVersion;
36
+ highEntropyValues.uaFullVersion = ua.uaFullVersion;
37
+
38
+
39
+ } catch (e) {
40
+ setObjectValues(highEntropyValues, ERROR);
41
+ }
42
+
43
+ } else {
44
+ setObjectValues(highEntropyValues, NA);
45
+ }
46
+
47
+ return highEntropyValues;
48
+ }
@@ -0,0 +1,34 @@
1
+ import { ERROR, INIT, NA, setObjectValues } from './utils';
2
+
3
+ export function iframe() {
4
+ const iframeData = {
5
+ webdriver: INIT,
6
+ userAgent: INIT,
7
+ platform: INIT,
8
+ memory: INIT,
9
+ cpuCount: INIT,
10
+ language: INIT,
11
+ };
12
+ const iframe = document.createElement('iframe');
13
+
14
+ try {
15
+ iframe.style.display = 'none';
16
+ iframe.src = 'about:blank';
17
+ document.body.appendChild(iframe);
18
+
19
+ const iframeWindowNavigator = (iframe.contentWindow?.navigator as any);
20
+
21
+ iframeData.webdriver = iframeWindowNavigator.webdriver ?? false;
22
+ iframeData.userAgent = iframeWindowNavigator.userAgent ?? NA;
23
+ iframeData.platform = iframeWindowNavigator.platform ?? NA;
24
+ iframeData.memory = iframeWindowNavigator.deviceMemory ?? NA;
25
+ iframeData.cpuCount = iframeWindowNavigator.hardwareConcurrency ?? NA;
26
+ iframeData.language = iframeWindowNavigator.language ?? NA;
27
+ } catch (e) {
28
+ setObjectValues(iframeData, ERROR);
29
+ } finally {
30
+ document.body.removeChild(iframe);
31
+ }
32
+
33
+ return iframeData;
34
+ }
@@ -0,0 +1,24 @@
1
+ import { INIT, ERROR, NA } from "./utils";
2
+
3
+ export function internationalization() {
4
+ const internationalizationData = {
5
+ timezone: INIT,
6
+ localeLanguage: INIT,
7
+ };
8
+
9
+ try {
10
+ if (typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined') {
11
+ const dtfOptions = Intl.DateTimeFormat().resolvedOptions();
12
+ internationalizationData.timezone = dtfOptions.timeZone;
13
+ internationalizationData.localeLanguage = dtfOptions.locale;
14
+ } else {
15
+ internationalizationData.timezone = NA;
16
+ internationalizationData.localeLanguage = NA;
17
+ }
18
+ } catch (e) {
19
+ internationalizationData.timezone = ERROR;
20
+ internationalizationData.localeLanguage = ERROR;
21
+ }
22
+
23
+ return internationalizationData;
24
+ }
@@ -0,0 +1,6 @@
1
+ export function languages() {
2
+ return {
3
+ languages: navigator.languages,
4
+ language: navigator.language,
5
+ }
6
+ }
@@ -0,0 +1,30 @@
1
+ import { hashCode } from './utils';
2
+
3
+ export function maths() {
4
+ const results: number[] = [];
5
+ const testValue = 0.123456789;
6
+
7
+ // Math constants
8
+ const constants = ["E", "LN10", "LN2", "LOG10E", "LOG2E", "PI", "SQRT1_2", "SQRT2"];
9
+ constants.forEach(function (name) {
10
+ try {
11
+ results.push((Math as any)[name]);
12
+ } catch (e) {
13
+ results.push(-1);
14
+ }
15
+ });
16
+
17
+ // Math functions (can reveal VM/browser differences)
18
+ const mathFunctions = ["tan", "sin", "exp", "atan", "acosh", "asinh", "atanh", "expm1", "log1p", "sinh"];
19
+
20
+
21
+ mathFunctions.forEach(function (name) {
22
+ try {
23
+ results.push((Math as any)[name](testValue));
24
+ } catch (e) {
25
+ results.push(-1);
26
+ }
27
+ });
28
+
29
+ return hashCode(results.map(String).join(","));
30
+ }
@@ -0,0 +1,120 @@
1
+ import { ERROR, NA, hashCode, setObjectValues } from './utils';
2
+
3
+
4
+ const AUDIO_CODECS = [
5
+ 'audio/mp4; codecs="mp4a.40.2"',
6
+ 'audio/mpeg;',
7
+ 'audio/webm; codecs="vorbis"',
8
+ 'audio/ogg; codecs="vorbis"',
9
+ 'audio/wav; codecs="1"',
10
+ 'audio/ogg; codecs="speex"',
11
+ 'audio/ogg; codecs="flac"',
12
+ 'audio/3gpp; codecs="samr"',
13
+ ];
14
+
15
+ const VIDEO_CODECS = [
16
+ 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
17
+ 'video/mp4; codecs="avc1.42E01E"',
18
+ 'video/mp4; codecs="avc1.58A01E"',
19
+ 'video/mp4; codecs="avc1.4D401E"',
20
+ 'video/mp4; codecs="avc1.64001E"',
21
+ 'video/mp4; codecs="mp4v.20.8"',
22
+ 'video/mp4; codecs="mp4v.20.240"',
23
+ 'video/webm; codecs="vp8"',
24
+ 'video/ogg; codecs="theora"',
25
+ 'video/ogg; codecs="dirac"',
26
+ 'video/3gpp; codecs="mp4v.20.8"',
27
+ 'video/x-matroska; codecs="theora"',
28
+ ];
29
+
30
+
31
+ function getCanPlayTypeSupport(codecs: string[], mediaType: 'audio' | 'video'): Record<string, string | null> {
32
+ const result: Record<string, string | null> = {};
33
+ try {
34
+ const element = document.createElement(mediaType);
35
+ for (const codec of codecs) {
36
+ try {
37
+ result[codec] = element.canPlayType(codec) || null;
38
+ } catch {
39
+ result[codec] = null;
40
+ }
41
+ }
42
+ } catch {
43
+ for (const codec of codecs) {
44
+ result[codec] = null;
45
+ }
46
+ }
47
+ return result;
48
+ }
49
+
50
+ function getMediaSourceSupport(codecs: string[]): Record<string, boolean | null> {
51
+ const result: Record<string, boolean | null> = {};
52
+ const MediaSource = window.MediaSource;
53
+
54
+ if (!MediaSource || typeof MediaSource.isTypeSupported !== 'function') {
55
+ for (const codec of codecs) {
56
+ result[codec] = null;
57
+ }
58
+ return result;
59
+ }
60
+
61
+ for (const codec of codecs) {
62
+ try {
63
+ result[codec] = MediaSource.isTypeSupported(codec);
64
+ } catch {
65
+ result[codec] = null;
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+
71
+ function getRtcCapabilities(kind: 'audio' | 'video'): string | typeof NA | typeof ERROR {
72
+ try {
73
+ const RTCRtpReceiver = window.RTCRtpReceiver;
74
+ if (RTCRtpReceiver && typeof RTCRtpReceiver.getCapabilities === 'function') {
75
+ const capabilities = RTCRtpReceiver.getCapabilities(kind);
76
+ return hashCode(JSON.stringify(capabilities));
77
+ }
78
+ return NA;
79
+ } catch (e) {
80
+ return ERROR;
81
+ }
82
+ }
83
+
84
+ export function mediaCodecs() {
85
+ const mediaCodecsData = {
86
+ audioCanPlayTypeHash: NA as string | typeof NA | typeof ERROR,
87
+ videoCanPlayTypeHash: NA as string | typeof NA | typeof ERROR,
88
+ audioMediaSourceHash: NA as string | typeof NA | typeof ERROR,
89
+ videoMediaSourceHash: NA as string | typeof NA | typeof ERROR,
90
+ rtcAudioCapabilitiesHash: NA as string | typeof NA | typeof ERROR,
91
+ rtcVideoCapabilitiesHash: NA as string | typeof NA | typeof ERROR,
92
+ hasMediaSource: false,
93
+ };
94
+
95
+ try {
96
+ // Check MediaSource availability
97
+ mediaCodecsData.hasMediaSource = !!window.MediaSource;
98
+
99
+ // canPlayType support - hash the results
100
+ const audioCanPlayType = getCanPlayTypeSupport(AUDIO_CODECS, 'audio');
101
+ const videoCanPlayType = getCanPlayTypeSupport(VIDEO_CODECS, 'video');
102
+ mediaCodecsData.audioCanPlayTypeHash = hashCode(JSON.stringify(audioCanPlayType));
103
+ mediaCodecsData.videoCanPlayTypeHash = hashCode(JSON.stringify(videoCanPlayType));
104
+
105
+ // MediaSource.isTypeSupported - hash the results
106
+ const audioMediaSource = getMediaSourceSupport(AUDIO_CODECS);
107
+ const videoMediaSource = getMediaSourceSupport(VIDEO_CODECS);
108
+ mediaCodecsData.audioMediaSourceHash = hashCode(JSON.stringify(audioMediaSource));
109
+ mediaCodecsData.videoMediaSourceHash = hashCode(JSON.stringify(videoMediaSource));
110
+
111
+ // RTCRtpReceiver.getCapabilities - already returns hash
112
+ mediaCodecsData.rtcAudioCapabilitiesHash = getRtcCapabilities('audio');
113
+ mediaCodecsData.rtcVideoCapabilitiesHash = getRtcCapabilities('video');
114
+
115
+ } catch (e) {
116
+ setObjectValues(mediaCodecsData, ERROR);
117
+ }
118
+
119
+ return mediaCodecsData;
120
+ }
@@ -0,0 +1,85 @@
1
+ import { ERROR, INIT, setObjectValues } from './utils';
2
+
3
+ export function mediaQueries() {
4
+ const mediaQueriesData = {
5
+ prefersColorScheme: INIT as string | null | typeof INIT | typeof ERROR,
6
+ prefersReducedMotion: INIT as boolean | typeof INIT | typeof ERROR,
7
+ prefersReducedTransparency: INIT as boolean | typeof INIT | typeof ERROR,
8
+ colorGamut: INIT as string | null | typeof INIT | typeof ERROR,
9
+ pointer: INIT as string | null | typeof INIT | typeof ERROR,
10
+ anyPointer: INIT as string | null | typeof INIT | typeof ERROR,
11
+ hover: INIT as boolean | typeof INIT | typeof ERROR,
12
+ anyHover: INIT as boolean | typeof INIT | typeof ERROR,
13
+ colorDepth: INIT as number | typeof INIT | typeof ERROR,
14
+ };
15
+
16
+ try {
17
+ // Prefers color scheme
18
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
19
+ mediaQueriesData.prefersColorScheme = 'dark';
20
+ } else if (window.matchMedia('(prefers-color-scheme: light)').matches) {
21
+ mediaQueriesData.prefersColorScheme = 'light';
22
+ } else {
23
+ mediaQueriesData.prefersColorScheme = null;
24
+ }
25
+
26
+ // Prefers reduced motion
27
+ mediaQueriesData.prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
28
+
29
+ // Prefers reduced transparency
30
+ mediaQueriesData.prefersReducedTransparency = window.matchMedia('(prefers-reduced-transparency: reduce)').matches;
31
+
32
+ // Color gamut
33
+ if (window.matchMedia('(color-gamut: rec2020)').matches) {
34
+ mediaQueriesData.colorGamut = 'rec2020';
35
+ } else if (window.matchMedia('(color-gamut: p3)').matches) {
36
+ mediaQueriesData.colorGamut = 'p3';
37
+ } else if (window.matchMedia('(color-gamut: srgb)').matches) {
38
+ mediaQueriesData.colorGamut = 'srgb';
39
+ } else {
40
+ mediaQueriesData.colorGamut = null;
41
+ }
42
+
43
+ // Pointer
44
+ if (window.matchMedia('(pointer: fine)').matches) {
45
+ mediaQueriesData.pointer = 'fine';
46
+ } else if (window.matchMedia('(pointer: coarse)').matches) {
47
+ mediaQueriesData.pointer = 'coarse';
48
+ } else if (window.matchMedia('(pointer: none)').matches) {
49
+ mediaQueriesData.pointer = 'none';
50
+ } else {
51
+ mediaQueriesData.pointer = null;
52
+ }
53
+
54
+ // Any pointer
55
+ if (window.matchMedia('(any-pointer: fine)').matches) {
56
+ mediaQueriesData.anyPointer = 'fine';
57
+ } else if (window.matchMedia('(any-pointer: coarse)').matches) {
58
+ mediaQueriesData.anyPointer = 'coarse';
59
+ } else if (window.matchMedia('(any-pointer: none)').matches) {
60
+ mediaQueriesData.anyPointer = 'none';
61
+ } else {
62
+ mediaQueriesData.anyPointer = null;
63
+ }
64
+
65
+ // Hover
66
+ mediaQueriesData.hover = window.matchMedia('(hover: hover)').matches;
67
+
68
+ // Any hover
69
+ mediaQueriesData.anyHover = window.matchMedia('(any-hover: hover)').matches;
70
+
71
+ // Color depth - find the maximum supported color depth
72
+ let maxColorDepth = 0;
73
+ for (let c = 0; c <= 16; c++) {
74
+ if (window.matchMedia(`(color: ${c})`).matches) {
75
+ maxColorDepth = c;
76
+ }
77
+ }
78
+ mediaQueriesData.colorDepth = maxColorDepth;
79
+
80
+ } catch (e) {
81
+ setObjectValues(mediaQueriesData, ERROR);
82
+ }
83
+
84
+ return mediaQueriesData;
85
+ }
@@ -0,0 +1,5 @@
1
+ import { NA } from "./utils";
2
+
3
+ export function memory() {
4
+ return (navigator as any).deviceMemory || NA;
5
+ }
@@ -0,0 +1,34 @@
1
+ import { NA, setObjectValues } from "./utils";
2
+
3
+ export async function multimediaDevices() {
4
+ return new Promise(async function (resolve) {
5
+ var deviceToCount = {
6
+ "audiooutput": 0,
7
+ "audioinput": 0,
8
+ "videoinput": 0
9
+ };
10
+
11
+ if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
12
+ const devices = await navigator.mediaDevices.enumerateDevices();
13
+ if (typeof devices !== "undefined") {
14
+ for (var i = 0; i < devices.length; i++) {
15
+ var name = devices[i].kind as keyof typeof deviceToCount;
16
+ deviceToCount[name] = deviceToCount[name] + 1;
17
+ }
18
+
19
+ return resolve({
20
+ speakers: deviceToCount.audiooutput,
21
+ microphones: deviceToCount.audioinput,
22
+ webcams: deviceToCount.videoinput
23
+ });
24
+ } else {
25
+ setObjectValues(deviceToCount, NA);
26
+ return resolve(deviceToCount);
27
+ }
28
+
29
+ } else {
30
+ setObjectValues(deviceToCount, NA);
31
+ return resolve(deviceToCount);
32
+ }
33
+ });
34
+ }
@@ -0,0 +1,17 @@
1
+ export function navigatorPropertyDescriptors() {
2
+ const properties = ['deviceMemory', 'hardwareConcurrency', 'language', 'languages', 'platform'];
3
+
4
+ const results = [];
5
+
6
+ for (const property of properties) {
7
+ const res = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(navigator), property);
8
+
9
+ if (res && res.value) {
10
+ results.push('1');
11
+ } else {
12
+ results.push('0');
13
+ }
14
+ }
15
+
16
+ return results.join('');
17
+ }
@@ -0,0 +1,3 @@
1
+ export function nonce() {
2
+ return Math.random().toString(36).substring(2, 15);
3
+ }
@@ -0,0 +1,3 @@
1
+ export function platform() {
2
+ return navigator.platform;
3
+ }
@@ -0,0 +1,3 @@
1
+ export function playwright() {
2
+ return '__pwInitScripts' in window || '__playwright__binding__' in window;
3
+ }