@@ -302,6 +302,16 @@ let sync_metrics = {
302302 // Payment APIs
303303 'ApplePaySession\.canMakePayments' ,
304304
305+ // User Agent and Platform fingerprinting
306+ 'navigator\.userAgent' ,
307+ 'navigator\.platform' ,
308+ 'navigator\.oscpu' ,
309+ 'navigator\.vendor' ,
310+ 'navigator\.vendorSub' ,
311+ 'navigator\.product' ,
312+ 'navigator\.productSub' ,
313+ 'navigator\.buildID' ,
314+
305315 // Audio fingerprinting
306316 'createAnalyser' ,
307317 'createOscillator' ,
@@ -310,6 +320,19 @@ let sync_metrics = {
310320 'getFloatFrequencyData' ,
311321 'getByteFrequencyData' ,
312322 'OscillatorNode' ,
323+ 'AudioContext' ,
324+ 'webkitAudioContext' ,
325+ 'AnalyserNode' ,
326+ 'createDynamicsCompressor' ,
327+ 'channelCount' ,
328+ 'channelCountMode' ,
329+ 'channelInterpretation' ,
330+ 'fftSize' ,
331+ 'frequencyBinCount' ,
332+ 'maxDecibels' ,
333+ 'minDecibels' ,
334+ 'smoothingTimeConstant' ,
335+ 'sampleRate' ,
313336
314337 // Canvas fingerprinting
315338 'canvas\.getContext' ,
@@ -318,6 +341,8 @@ let sync_metrics = {
318341 'canvasRenderingContext2D\.strokeText' ,
319342 'canvasRenderingContext2D\.getImageData' ,
320343 'HTMLCanvasElement\.toBlob' ,
344+ 'getContext\(.*2d.*\)' ,
345+ 'getContext\(.*webgl.*\)' ,
321346
322347 // CSS media queries for fingerprinting
323348 '@media.*color-gamut' ,
@@ -345,50 +370,163 @@ let sync_metrics = {
345370
346371 // PDF and plugins
347372 'pdfViewerEnabled' ,
373+ 'navigator\.plugins' ,
374+ 'navigator\.mimeTypes' ,
375+ 'Plugin\s' ,
376+ 'MimeType' ,
348377
349378 // Attribution and tracking
350379 'attributionSourceId' ,
351380
352- // Time zone fingerprinting
381+ // Time zone and language fingerprinting
353382 'resolvedOptions\(\)\.timeZone' ,
354383 'getTimezoneOffset' ,
384+ 'navigator\.language' ,
385+ 'navigator\.languages' ,
386+ 'Intl\.DateTimeFormat' ,
387+ 'Intl\.Collator' ,
355388
356389 // WebGL fingerprinting
357390 'vendorUnmasked' ,
358391 'rendererUnmasked' ,
359392 'shadingLanguageVersion' ,
360393 'WEBGL_debug_renderer_info' ,
361394 'getShaderPrecisionFormat' ,
395+ 'WebGLRenderingContext' ,
396+ 'getParameter' ,
397+ 'getSupportedExtensions' ,
398+ 'getExtension' ,
399+ 'VENDOR' ,
400+ 'RENDERER' ,
401+ 'VERSION' ,
402+ 'SHADING_LANGUAGE_VERSION' ,
362403
363404 // Screen properties
364405 'availWidth' ,
365406 'availHeight' ,
366407 'screen\.width' ,
367408 'screen\.height' ,
368409 'screen\.colorDepth' ,
369-
370- // Navigator properties
371- 'navigator\.platform' ,
372- 'navigator\.plugins' ,
373- 'navigator\.language' ,
374- 'navigator\.oscpu' ,
375- 'navigator\.vendor' ,
376- 'navigator\.getBattery' ,
377- 'navigator\.getGamepads' ,
410+ 'screen\.pixelDepth' ,
411+ 'screen\.availTop' ,
412+ 'screen\.availLeft' ,
413+ 'outerWidth' ,
414+ 'outerHeight' ,
415+ 'innerWidth' ,
416+ 'innerHeight' ,
417+ 'devicePixelRatio' ,
418+
419+ // Window and browser chrome fingerprinting
420+ 'locationbar' ,
421+ 'menubar' ,
422+ 'personalbar' ,
423+ 'scrollbars' ,
424+ 'statusbar' ,
425+ 'toolbar' ,
426+ 'history\.length' ,
378427
379428 // Geolocation API: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API
380429 'getCurrentPosition' ,
381430 'watchPosition' ,
431+ 'navigator\.geolocation' ,
382432
383- // Media devices: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
433+ // Media devices and capabilities
384434 'enumerateDevices' ,
385435 'getUserMedia' ,
386436 'getDisplayMedia' ,
437+ 'navigator\.mediaDevices' ,
438+ 'canPlayType' ,
439+ 'HTMLVideoElement\.canPlayType' ,
440+ 'HTMLAudioElement\.canPlayType' ,
441+
442+ // Permissions API
443+ 'navigator\.permissions' ,
444+ 'permissions\.query' ,
445+
446+ // Battery API
447+ 'navigator\.battery' ,
448+ 'navigator\.getBattery' ,
449+ 'charging' ,
450+ 'chargingTime' ,
451+ 'dischargingTime' ,
452+ // 'level',
453+
454+ // Connection API
455+ 'navigator\.connection' ,
456+ 'navigator\.mozConnection' ,
457+ 'navigator\.webkitConnection' ,
458+ 'downlink' ,
459+ 'effectiveType' ,
460+ // 'rtt',
461+ // 'saveData',
462+
463+ // Sensors APIs
464+ 'Accelerometer' ,
465+ 'Gyroscope' ,
466+ 'LinearAccelerationSensor' ,
467+ 'AbsoluteOrientationSensor' ,
468+ 'RelativeOrientationSensor' ,
469+ 'AmbientLightSensor' ,
470+ 'ProximitySensor' ,
471+
472+ // Touch and input
473+ 'TouchEvent' ,
474+ 'createTouch' ,
475+ 'createTouchList' ,
476+
477+ // Font detection
478+ 'document\.fonts' ,
479+ 'FontFace' ,
480+
481+ // Do Not Track
482+ 'navigator\.doNotTrack' ,
483+ 'window\.doNotTrack' ,
484+
485+ // Cookie detection
486+ 'navigator\.cookieEnabled' ,
487+
488+ // Java detection
489+ 'navigator\.javaEnabled' ,
387490
388491 // Additional modern fingerprinting vectors
389492 'RTCPeerConnection' ,
390- 'document\.fonts' ,
493+ 'webkitRTCPeerConnection' ,
494+ 'mozRTCPeerConnection' ,
391495 'performance\.memory' ,
496+ 'performance\.timing' ,
497+ 'Notification\.permission' ,
498+
499+ // Keyboard layout detection
500+ 'KeyboardLayoutMap' ,
501+ 'navigator\.keyboard' ,
502+ 'getLayoutMap' ,
503+
504+ // Gamepad API
505+ 'navigator\.getGamepads' ,
506+ 'GamepadEvent' ,
507+
508+ // Storage quota
509+ 'navigator\.storage' ,
510+ 'navigator\.webkitTemporaryStorage' ,
511+ 'navigator\.webkitPersistentStorage' ,
512+ 'estimate' ,
513+
514+ // Speech APIs
515+ 'SpeechSynthesis' ,
516+ 'SpeechRecognition' ,
517+
518+ // WebRTC Data Channel
519+ 'RTCDataChannel' ,
520+ 'createDataChannel' ,
521+
522+ // Crypto subtle fingerprinting
523+ 'crypto\.subtle' ,
524+ 'SubtleCrypto' ,
525+
526+ // Worker capabilities
527+ 'Worker' ,
528+ 'SharedWorker' ,
529+ 'ServiceWorker'
392530 ] ;
393531
394532 // Pre-compile regexes - handle already escaped patterns
@@ -402,6 +540,12 @@ let sync_metrics = {
402540 try {
403541 let detectedApis = [ ] ;
404542 let totalOccurrences = 0 ;
543+ let body = req . response_body ;
544+
545+ // Validate response body
546+ if ( ! body || typeof body !== 'string' ) {
547+ return ; // Skip invalid response bodies
548+ }
405549
406550 compiledRegexes . forEach ( ( { api, regex } ) => {
407551 try {
@@ -411,7 +555,7 @@ let sync_metrics = {
411555 // Use a more memory-efficient counting approach
412556 let match ;
413557 let matches = 0 ;
414- while ( ( match = regex . exec ( req . response_body ) ) !== null ) {
558+ while ( ( match = regex . exec ( body ) ) !== null ) {
415559 matches ++ ;
416560 // Prevent infinite loops on zero-length matches
417561 if ( match . index === regex . lastIndex ) {
@@ -428,23 +572,18 @@ let sync_metrics = {
428572 }
429573 } ) ;
430574
431- // Track scripts with significant fingerprinting API usage (threshold: 4+ APIs or high occurrence count)
432- const suspicionScore = Math . round ( ( detectedApis . length * 2 + totalOccurrences ) / 3 ) ;
433- if ( detectedApis . length >= 4 || suspicionScore >= 8 ) {
575+ // Track scripts with significant fingerprinting API usage
576+ if ( detectedApis . length >= 5 ) {
434577 likelyFingerprintingScripts . push ( {
435578 url : req . url ,
436- detectedApis : detectedApis ,
437- suspicionScore : suspicionScore
579+ detectedApis
438580 } ) ;
439581 }
440582 } catch ( error ) {
441583 // Skip this request on error - avoid console.warn in WebPageTest
442584 }
443585 } ) ;
444586
445- // Sort by suspicion score (highest first)
446- likelyFingerprintingScripts . sort ( ( a , b ) => b . suspicionScore - a . suspicionScore ) ;
447-
448587 return likelyFingerprintingScripts ;
449588 } ) ( ) ,
450589
0 commit comments