1- // Enhanced fingerprinting with FingerprintJS and backward compatibility
1+ // Enhanced fingerprinting with CLI-only approach and backward compatibility
22// Modified from: https://github.com/andsmedeiros/hw-fingerprint
33
44import { createHash , randomBytes } from 'node:crypto'
5+ import { networkInterfaces } from 'node:os'
56import {
67 bios ,
78 cpu ,
@@ -11,23 +12,82 @@ import {
1112 system ,
1213 // @ts -ignore
1314} from 'systeminformation'
15+ import { machineId } from 'node-machine-id'
1416
15- import { findChrome } from './browser-runner'
17+ import { detectShell } from './utils/detect-shell'
18+ import { getSystemInfo } from './utils/system-info'
1619import { logger } from './utils/logger'
1720
18- // Type declaration for FingerprintJS result
19- declare global {
20- interface Window {
21- fingerprintResult ?: {
22- visitorId : string
23- confidence : { score : number }
24- components : Record < string , any >
25- }
26- }
21+ // Enhanced CLI fingerprint implementation using multiple Node.js data sources
22+ const getEnhancedFingerprintInfo = async ( ) => {
23+ // Get essential system information efficiently
24+ const [
25+ systemInfo ,
26+ cpuInfo ,
27+ osInfo_ ,
28+ machineIdValue ,
29+ systemInfoBasic ,
30+ shell ,
31+ networkInfo
32+ ] = await Promise . all ( [
33+ system ( ) ,
34+ cpu ( ) ,
35+ osInfo ( ) ,
36+ machineId ( ) . catch ( ( ) => 'unknown' ) ,
37+ getSystemInfo ( ) ,
38+ detectShell ( ) ,
39+ Promise . resolve ( networkInterfaces ( ) )
40+ ] )
41+
42+ // Extract MAC addresses for additional uniqueness
43+ const macAddresses = Object . values ( networkInfo )
44+ . flat ( )
45+ . filter ( iface => iface && ! iface . internal && iface . mac && iface . mac !== '00:00:00:00:00:00' )
46+ . map ( iface => iface ! . mac )
47+ . sort ( )
48+
49+ return {
50+ // Hardware identifiers
51+ system : {
52+ manufacturer : systemInfo . manufacturer ,
53+ model : systemInfo . model ,
54+ serial : systemInfo . serial ,
55+ uuid : systemInfo . uuid ,
56+ } ,
57+ cpu : {
58+ manufacturer : cpuInfo . manufacturer ,
59+ brand : cpuInfo . brand ,
60+ cores : cpuInfo . cores ,
61+ physicalCores : cpuInfo . physicalCores ,
62+ } ,
63+ os : {
64+ platform : osInfo_ . platform ,
65+ distro : osInfo_ . distro ,
66+ arch : osInfo_ . arch ,
67+ hostname : osInfo_ . hostname ,
68+ } ,
69+ // CLI-specific identifiers
70+ runtime : {
71+ nodeVersion : systemInfoBasic . nodeVersion ,
72+ platform : systemInfoBasic . platform ,
73+ arch : systemInfoBasic . arch ,
74+ shell,
75+ cpuCount : systemInfoBasic . cpus ,
76+ } ,
77+ // Network identifiers
78+ network : {
79+ macAddresses,
80+ interfaceCount : Object . keys ( networkInfo ) . length ,
81+ } ,
82+ // Machine ID (OS-specific unique identifier)
83+ machineId : machineIdValue ,
84+ // Timestamp for version tracking
85+ fingerprintVersion : '2.0' ,
86+ } as Record < string , any >
2787}
2888
2989// Legacy fingerprint implementation (for backward compatibility)
30- const getFingerprintInfo = async ( ) => {
90+ const getLegacyFingerprintInfo = async ( ) => {
3191 const { manufacturer, model, serial, uuid } = await system ( )
3292 const { vendor, version : biosVersion , releaseDate } = await bios ( )
3393 const { manufacturer : cpuManufacturer , brand, speed, cores } = await cpu ( )
@@ -70,111 +130,56 @@ const getFingerprintInfo = async () => {
70130 } as Record < string , any >
71131}
72132
73- // Legacy implementation with random suffix (still needed for collision avoidance)
74- async function calculateLegacyFingerprint ( ) {
75- const fingerprintInfo = await getFingerprintInfo ( )
76- const fingerprintString = JSON . stringify ( fingerprintInfo )
77- const fingerprintHash = createHash ( 'sha256' )
78- . update ( fingerprintString )
79- . digest ( )
80- . toString ( 'base64url' )
81-
82- // Add 8 random characters to make the fingerprint unique even on identical hardware
83- const randomSuffix = randomBytes ( 6 ) . toString ( 'base64url' ) . substring ( 0 , 8 )
84-
85- return `legacy-${ fingerprintHash } -${ randomSuffix } `
86- }
87-
88- // Enhanced FingerprintJS implementation using headless browser
133+ // Enhanced CLI-only fingerprint (deterministic, no browser required)
89134async function calculateEnhancedFingerprint ( ) : Promise < string > {
90135 try {
91- const puppeteer = await import ( 'puppeteer-core' )
92-
93- const browser = await puppeteer . default . launch ( {
94- headless : true ,
95- args : [ '--no-sandbox' , '--disable-setuid-sandbox' ] ,
96- executablePath : findChrome ( ) ,
97- } )
98-
99- const page = await browser . newPage ( )
100-
101- // Create a minimal HTML page with FingerprintJS
102- const html = `
103- <!DOCTYPE html>
104- <html>
105- <head>
106- <script src="https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@4/dist/fp.min.js"></script>
107- </head>
108- <body>
109- <script>
110- (async () => {
111- // Initialize FingerprintJS
112- const fp = await FingerprintJS.load()
113-
114- // Get the visitor identifier
115- const result = await fp.get()
116-
117- // Make the result available to Node.js
118- window.fingerprintResult = {
119- visitorId: result.visitorId,
120- confidence: result.confidence,
121- components: result.components
122- }
123- })()
124- </script>
125- </body>
126- </html>
127- `
128-
129- await page . setContent ( html )
130-
131- // Wait for FingerprintJS to complete
132- await page . waitForFunction ( ( ) => window . fingerprintResult , { timeout : 10000 } )
133-
134- // Extract the fingerprint result
135- const result = await page . evaluate ( ( ) => window . fingerprintResult )
136-
137- await browser . close ( )
138-
139- // Combine FingerprintJS result with system info for enhanced uniqueness
140- const systemInfo = await getFingerprintInfo ( )
141- const combinedData = {
142- fingerprintjs : result ! . visitorId ,
143- confidence : result ! . confidence ,
144- system : systemInfo ,
145- }
146-
147- const combinedString = JSON . stringify ( combinedData )
148- const combinedHash = createHash ( 'sha256' )
149- . update ( combinedString )
136+ const fingerprintInfo = await getEnhancedFingerprintInfo ( )
137+ const fingerprintString = JSON . stringify ( fingerprintInfo )
138+ const fingerprintHash = createHash ( 'sha256' )
139+ . update ( fingerprintString )
150140 . digest ( )
151141 . toString ( 'base64url' )
152-
153- // No random suffix needed - FingerprintJS provides sufficient uniqueness
154- return `fp -${ combinedHash } `
142+
143+ // No random suffix needed - comprehensive system data provides sufficient uniqueness
144+ return `enhanced -${ fingerprintHash } `
155145 } catch ( error ) {
156146 logger . warn (
157147 {
158148 errorMessage : error instanceof Error ? error . message : String ( error ) ,
159149 fingerprintType : 'enhanced_failed' ,
160150 } ,
161- 'Enhanced fingerprinting failed, falling back to legacy'
151+ 'Enhanced CLI fingerprinting failed, falling back to legacy'
162152 )
163153 throw error
164154 }
165155}
166156
167- // Main fingerprint function with hybrid approach
157+ // Legacy implementation with random suffix (still needed for collision avoidance)
158+ async function calculateLegacyFingerprint ( ) {
159+ const fingerprintInfo = await getLegacyFingerprintInfo ( )
160+ const fingerprintString = JSON . stringify ( fingerprintInfo )
161+ const fingerprintHash = createHash ( 'sha256' )
162+ . update ( fingerprintString )
163+ . digest ( )
164+ . toString ( 'base64url' )
165+
166+ // Add 8 random characters to make the fingerprint unique even on identical hardware
167+ const randomSuffix = randomBytes ( 6 ) . toString ( 'base64url' ) . substring ( 0 , 8 )
168+
169+ return `legacy-${ fingerprintHash } -${ randomSuffix } `
170+ }
171+
172+ // Main fingerprint function with CLI-only approach
168173export async function calculateFingerprint ( ) : Promise < string > {
169174 try {
170- // Try enhanced fingerprinting first
175+ // Try enhanced CLI fingerprinting first
171176 const fingerprint = await calculateEnhancedFingerprint ( )
172177 logger . info (
173178 {
174- fingerprintType : 'enhanced ' ,
179+ fingerprintType : 'enhanced_cli ' ,
175180 fingerprintId : fingerprint ,
176181 } ,
177- 'Enhanced fingerprint generated successfully'
182+ 'Enhanced CLI fingerprint generated successfully'
178183 )
179184 return fingerprint
180185 } catch ( enhancedError ) {
@@ -183,7 +188,7 @@ export async function calculateFingerprint(): Promise<string> {
183188 errorMessage : enhancedError instanceof Error ? enhancedError . message : String ( enhancedError ) ,
184189 fingerprintType : 'enhanced_failed_fallback' ,
185190 } ,
186- 'Enhanced fingerprinting failed, using legacy fallback'
191+ 'Enhanced CLI fingerprinting failed, using legacy fallback'
187192 )
188193
189194 try {
0 commit comments