@anonympins/fingerprint 0.7.2 → 0.7.4

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 (41) hide show
  1. package/CHANGELOG.md +36 -9
  2. package/README.md +1 -1
  3. package/package.json +109 -107
  4. package/public/fingerprint-anti-bot.zip +0 -0
  5. package/src/js/fingerprint.client.js +252 -45
  6. package/src/js/fingerprint.client.obfuscated.js +1 -1
  7. package/src/js/fingerprint.js +265 -185
  8. package/src/js/fingerprint.utils.js +32 -5
  9. package/src/js/fingerprint.worker.js +81 -0
  10. package/src/js/library.js +6 -2
  11. package/src/js/pow.solver.js +2 -3
  12. package/src/js/problem-manager.js +113 -113
  13. package/src/js/tests/fingerprint.client.init.test.js +30 -0
  14. package/src/js/tests/fingerprint.test.js +136 -4
  15. package/src/js/tests/threatIntel.test.js +76 -0
  16. package/src/php/AutoTuner.php +36 -18
  17. package/src/php/Challenge/ChallengeUtils.php +121 -240
  18. package/src/php/DirectFingerprint.php +16 -4
  19. package/src/php/FingerprintClient.php +17 -19
  20. package/src/php/FingerprintEngine.php +77 -39
  21. package/src/php/Ja3AnomalyDetector.php +20 -12
  22. package/src/php/Optimization/Optimization.php +5 -1
  23. package/src/php/Optimization/OptimizationOperators.php +5 -1
  24. package/src/php/Optimization/ProblemInitializers.php +16 -4
  25. package/src/php/ProblemManager.php +20 -14
  26. package/src/php/Tests/ChallengeUtilsTest.php +469 -453
  27. package/src/php/Tests/FingerprintEngineTest.php +6 -1
  28. package/src/php/Tests/RequestUtilsTest.php +1 -1
  29. package/src/php/Tests/ThreatIntelTest.php +64 -0
  30. package/src/php/Utils/Env.php +51 -49
  31. package/src/php/Utils/MaliciousPatterns.php +74 -74
  32. package/src/php/Utils/RequestUtils.php +231 -132
  33. package/src/php/WordPress/WpDbStore.php +4 -0
  34. package/src/php/WordPress/{fingerprint-wordpress.php → fingerprint-anti-bot.php} +131 -126
  35. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
  36. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +1 -1
  37. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
  38. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +1 -1
  39. package/src/php/WordPress/package.php +6 -3
  40. package/src/php/WordPress/readme.txt +36 -0
  41. package/public/fingerprint-wordpress.zip +0 -0
@@ -392,125 +392,145 @@ class RequestUtils
392
392
 
393
393
  /**
394
394
  * Calcule un score basé sur les métriques comportementales envoyées par le client.
395
- * @return array{'behaviorScore': float}
395
+ * @param RequestContext $context Le contexte de la requête.
396
+ * @return array{behaviorScore: float}
396
397
  */
397
398
  public static function getBehaviorScore(RequestContext $context): array
398
399
  {
399
- $behaviorHeader = $context->getHeader('x-behavior-metrics');
400
- if (!$behaviorHeader) {
400
+ $header = $context->getHeader('x-behavior-metrics');
401
+ if (empty($header)) {
401
402
  return ['behaviorScore' => 0.0];
402
403
  }
403
404
 
404
- $metrics = json_decode($behaviorHeader, true);
405
- if (json_last_error() !== JSON_ERROR_NONE) {
406
- return ['behaviorScore' => 10.0]; // En-tête malformé
405
+ if ($header === '1' || $header === 1) {
406
+ return ['behaviorScore' => 0.0];
407
407
  }
408
-
409
- if (!empty($metrics['honeypotInteraction'])) {
408
+ if ($header === '0' || $header === 0) {
410
409
  return ['behaviorScore' => 100.0];
411
410
  }
412
411
 
413
- $score = 0.0;
414
- if (!empty($metrics['prototypeTampered'])) {
415
- $score += 80.0;
412
+ if (is_numeric($header)) {
413
+ $score = max(0.0, min(100.0, (float)$header));
414
+ return ['behaviorScore' => $score];
416
415
  }
417
416
 
418
- $mouseAnalysis = self::analyzeMouseMovements($metrics['mouseMovementsHistory'] ?? null);
419
- $touch = self::analyzeTouchMovements($metrics['touchMovementsHistory'] ?? null);
420
-
421
- if (isset($metrics['historyLength'])) {
422
- if ($metrics['historyLength'] === 1) $score += 15;
423
- elseif ($metrics['historyLength'] >= 5) $score -= 20;
424
- elseif ($metrics['historyLength'] >= 2) $score -= 10;
425
- } else {
426
- // Pénalité pour absence totale d'interaction si l'historique n'est pas dispo
427
- if ($mouseAnalysis['avgSpeed'] == 0 && $touch['avgSpeed'] == 0 && ($metrics['keystrokeLatency'] ?? 0) == 0) {
428
- $score += 40;
417
+ if (is_string($header)) {
418
+ $trimmed = trim($header);
419
+ if (is_numeric($trimmed)) {
420
+ if ($trimmed === '1') {
421
+ return ['behaviorScore' => 0.0];
422
+ }
423
+ if ($trimmed === '0') {
424
+ return ['behaviorScore' => 100.0];
425
+ }
426
+ $score = max(0.0, min(100.0, (float)$trimmed));
427
+ return ['behaviorScore' => $score];
429
428
  }
430
429
  }
431
430
 
432
- if ($mouseAnalysis['avgSpeed'] > 0) {
433
- if ($mouseAnalysis['avgSpeed'] > 3) $score += 25;
434
- if ($mouseAnalysis['avgAcceleration'] > 0.5) $score += 20;
435
- if ($mouseAnalysis['straightness'] > 0.95) $score += 30;
436
- if ($mouseAnalysis['pauses'] === 0 && count($mouseAnalysis['segments']) > 20) $score += 15;
431
+ $metrics = json_decode($header, true);
432
+ if (json_last_error() !== JSON_ERROR_NONE) {
433
+ return ['behaviorScore' => 10.0];
434
+ }
435
+
436
+ if ($metrics === 1) {
437
+ return ['behaviorScore' => 0.0];
438
+ }
439
+ if ($metrics === 0) {
440
+ return ['behaviorScore' => 100.0];
437
441
  }
438
442
 
439
- if (($metrics['keystrokeLatency'] ?? 0) > 0 && $metrics['keystrokeLatency'] < 40) $score += 25;
440
- if (($metrics['keystrokeLatency'] ?? 0) > 1000) $score += 15;
443
+ if (is_numeric($metrics)) {
444
+ $score = max(0.0, min(100.0, (float)$metrics));
445
+ return ['behaviorScore' => $score];
446
+ }
441
447
 
442
- // NOUVEAU: Analyse de digraphie/trigraphie (dwell & flight times)
443
- $dwellTimes = $metrics['keystrokeDwellTimes'] ?? [];
444
- $flightTimes = $metrics['keystrokeFlightTimes'] ?? [];
448
+ if (!is_array($metrics)) {
449
+ return ['behaviorScore' => 0.0];
450
+ }
445
451
 
446
- if (count($dwellTimes) >= 5) {
447
- $meanDwell = array_sum($dwellTimes) / count($dwellTimes);
448
- $varDwell = array_reduce($dwellTimes, fn($carry, $item) => $carry + pow($item - $meanDwell, 2), 0) / count($dwellTimes);
449
- $stdDevDwell = sqrt($varDwell);
452
+ if ($metrics['honeypotInteraction'] ?? false) {
453
+ return ['behaviorScore' => 100.0];
454
+ }
450
455
 
451
- if ($stdDevDwell < 2.0) {
452
- $score += 35.0;
453
- }
454
- if ($meanDwell < 15.0) {
455
- $score += 25.0;
456
- }
456
+ $score = 0.0;
457
+ if ($metrics['prototypeTampered'] ?? false) {
458
+ $score += 80.0;
457
459
  }
458
460
 
459
- if (count($flightTimes) >= 5) {
460
- $times = array_column($flightTimes, 'time');
461
- $meanFlight = array_sum($times) / count($times);
462
- $varFlight = array_reduce($times, fn($carry, $item) => $carry + pow($item - $meanFlight, 2), 0) / count($times);
463
- $stdDevFlight = sqrt($varFlight);
461
+ $mouseHistory = $metrics['mouseMovementsHistory'] ?? [];
462
+ $touchHistory = $metrics['touchMovementsHistory'] ?? [];
464
463
 
465
- if ($stdDevFlight < 3.0) {
466
- $score += 35.0;
467
- }
468
- if ($meanFlight < 25.0) {
469
- $score += 25.0;
470
- }
471
- $benfordDev = Optimization::benfordTest($times);
472
- if ($benfordDev > 0.18) {
473
- $score += 30.0;
464
+ $mouseAnalysis = RequestUtils::analyzeMouseMovements($mouseHistory);
465
+ $touchAnalysis = RequestUtils::analyzeTouchMovements($touchHistory);
466
+
467
+ $mouseAvgSpeed = (float)($mouseAnalysis['avgSpeed'] ?? 0.0);
468
+ $touchAvgSpeed = (float)($touchAnalysis['avgSpeed'] ?? 0.0);
469
+ $keystrokeLatency = (float)($metrics['keystrokeLatency'] ?? 0.0);
470
+
471
+ if (isset($metrics['historyLength'])) {
472
+ $historyLength = (int)$metrics['historyLength'];
473
+ if ($historyLength === 1) $score += 15;
474
+ elseif ($historyLength >= 5) $score -= 20;
475
+ elseif ($historyLength >= 2) $score -= 10;
476
+ } else {
477
+ if ($mouseAvgSpeed === 0.0 && $touchAvgSpeed === 0.0 && $keystrokeLatency === 0.0) {
478
+ $noInteractionPenalty = 5.0;
479
+ if ($metrics['rendering']['offscreenAnom'] ?? false) {
480
+ $noInteractionPenalty += 40.0;
481
+ }
482
+ $score += $noInteractionPenalty;
474
483
  }
475
484
  }
476
485
 
477
- // Analyse de Benford sur les segments de mouvement de la souris
478
- if (count($mouseAnalysis['segments']) > 10) {
479
- $benfordDeviation = Optimization::benfordTest($mouseAnalysis['segments']);
480
- if ($benfordDeviation > 0.18) {
481
- $score += 35;
482
- }
486
+ if ($mouseAvgSpeed > 0) {
487
+ if ($mouseAvgSpeed > 3.0) $score += 25;
488
+ if (($mouseAnalysis['avgAcceleration'] ?? 0.0) > 0.5) $score += 20;
489
+ if (($mouseAnalysis['straightness'] ?? 1.0) > 0.95) $score += 30;
490
+ if (($mouseAnalysis['pauses'] ?? 0) === 0 && count($mouseAnalysis['segments'] ?? []) > 20) $score += 15;
483
491
  }
484
- // Analyse comportementale des événements tactiles (Touch Move)
485
- $touchHistory = $metrics['touchMovementsHistory'] ?? null;
486
- if (!empty($touchHistory)) {
487
- if ($touch['avgSpeed'] > 0) {
488
- if ($touch['avgSpeed'] > 5) $score += 30;
489
- if ($touch['avgAcceleration'] > 0.8) $score += 20;
490
- if ($touch['straightness'] > 0.98) $score += 35;
491
- if ($touch['pauses'] === 0 && count($touch['segments']) > 25) $score += 15;
492
492
 
493
- // Détection de l'émulation (pression et rayon de contact constants)
494
- if ($touch['avgPressure'] > 0 && $touch['pressureVariance'] == 0) {
495
- $score += 30;
493
+ if ($touchAvgSpeed > 0 || !empty($touchHistory)) {
494
+ if (count($touchHistory) >= 3) {
495
+ if (($touchAnalysis['pressureVariance'] ?? 1.0) < 0.0001) {
496
+ $score += 35.0;
497
+ }
498
+ if (($touchAnalysis['radiusVariance'] ?? 1.0) < 0.0001) {
499
+ $score += 25.0;
496
500
  }
497
- if ($touch['avgRadius'] > 0 && $touch['radiusVariance'] == 0) {
498
- $score += 30;
501
+ if (($touchAnalysis['straightness'] ?? 0.0) > 0.98) {
502
+ $score += 20.0;
499
503
  }
500
504
  }
501
- if (count($touch['segments']) > 10) {
502
- $benfordDev = Optimization::benfordTest($touch['segments']);
503
- if ($benfordDev > 0.18) {
504
- $score += 35;
505
+ }
506
+
507
+ $dwellTimes = $metrics['keystrokeDwellTimes'] ?? [];
508
+ if (is_array($dwellTimes) && count($dwellTimes) >= 3) {
509
+ $dwellNumeric = array_filter($dwellTimes, 'is_numeric');
510
+ if (count($dwellNumeric) >= 3) {
511
+ $meanDwell = array_sum($dwellNumeric) / count($dwellNumeric);
512
+ $dwellVar = array_reduce($dwellNumeric, fn($carry, $val) => $carry + pow((float)$val - $meanDwell, 2), 0.0) / count($dwellNumeric);
513
+ if ($dwellVar < 2.0) {
514
+ $score += 40.0;
505
515
  }
506
516
  }
517
+ }
507
518
 
508
- // Détection de ferme mobile : Touch actif sur mobile sans aucune vibration physique (châssis/rack ADB)
509
- $ua = $context->getHeader('user-agent') ?? '';
510
- $isMobileDevice = str_contains($ua, 'Mobile');
511
- if ($isMobileDevice && count($touchHistory) >= 5 && isset($metrics['motionVariance']) && is_numeric($metrics['motionVariance'])) {
512
- if ((float)$metrics['motionVariance'] === 0.0) {
513
- $score += 50.0;
519
+ $flightTimes = $metrics['keystrokeFlightTimes'] ?? [];
520
+ if (is_array($flightTimes) && count($flightTimes) >= 3) {
521
+ $extractedFlightTimes = [];
522
+ foreach ($flightTimes as $ft) {
523
+ if (is_array($ft) && isset($ft['time']) && is_numeric($ft['time'])) {
524
+ $extractedFlightTimes[] = (float)$ft['time'];
525
+ } elseif (is_numeric($ft)) {
526
+ $extractedFlightTimes[] = (float)$ft;
527
+ }
528
+ }
529
+ if (count($extractedFlightTimes) >= 3) {
530
+ $meanFlight = array_sum($extractedFlightTimes) / count($extractedFlightTimes);
531
+ $flightVar = array_reduce($extractedFlightTimes, fn($carry, $val) => $carry + pow($val - $meanFlight, 2), 0.0) / count($extractedFlightTimes);
532
+ if ($flightVar < 2.0) {
533
+ $score += 40.0;
514
534
  }
515
535
  }
516
536
  }
@@ -518,6 +538,7 @@ class RequestUtils
518
538
  return ['behaviorScore' => min(100.0, $score)];
519
539
  }
520
540
 
541
+
521
542
  /**
522
543
  * Calcule un score basé sur la régularité d'affichage (V-Sync) et l'utilisation suspecte d'OffscreenCanvas.
523
544
  * @return array{'renderingAnomalyScore': float}
@@ -1038,19 +1059,34 @@ class RequestUtils
1038
1059
  public static function getThreatIntelScore(RequestContext $context, array $threatIntelConfig): array
1039
1060
  {
1040
1061
  $score = 0.0;
1062
+ $signals = [];
1063
+
1041
1064
  $zkpY = $context->zkpY;
1042
1065
  if (!empty($zkpY)) {
1043
1066
  $store = StoreManager::getStore();
1044
1067
  $isBanned = $store->has("banned-zkp-y:{$zkpY}");
1045
1068
  if ($isBanned) {
1046
1069
  $score = 100.0;
1070
+ $signals[] = [
1071
+ 'ruleId' => 'FEDERATED_ZKP_BANNED',
1072
+ 'confidence' => 1.0,
1073
+ 'score' => 100.0,
1074
+ 'rationale' => 'Cryptographic identity matched federated banned list consensus'
1075
+ ];
1047
1076
  }
1048
1077
  }
1049
1078
 
1050
- $rttProxyScore = \Anonympins\Fingerprint\Ja3AnomalyDetector::getRttProxyScore($context);
1051
- $score = max($score, (float)$rttProxyScore);
1079
+ $rttProxyScore = (float)\Anonympins\Fingerprint\Ja3AnomalyDetector::getRttProxyScore($context);
1080
+ if ($rttProxyScore > 0.0) {
1081
+ $score = max($score, $rttProxyScore);
1082
+ $signals[] = [
1083
+ 'ruleId' => 'RESIDENTIAL_PROXY_RTT_DISCREPANCY',
1084
+ 'score' => $rttProxyScore,
1085
+ 'rationale' => 'Physical discrepancy between TCP edge RTT and application transit latency'
1086
+ ];
1087
+ }
1052
1088
 
1053
- return ['threatIntelScore' => $score];
1089
+ return ['threatIntelScore' => $score, 'threatIntelSignals' => $signals];
1054
1090
  }
1055
1091
 
1056
1092
  /**
@@ -1145,7 +1181,8 @@ class RequestUtils
1145
1181
  */
1146
1182
  public static function cleanUrlFromPowParams(string $originalPath, array $incomingQuery): string
1147
1183
  {
1148
- $urlParts = parse_url($originalPath);
1184
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url -- Uses wp_parse_url when running inside WordPress
1185
+ $urlParts = function_exists('wp_parse_url') ? wp_parse_url($originalPath) : parse_url($originalPath);
1149
1186
  $path = $urlParts['path'] ?? '/';
1150
1187
  $finalQuery = $incomingQuery;
1151
1188
 
@@ -1326,7 +1363,8 @@ class RequestUtils
1326
1363
 
1327
1364
  // Utilisation de la partie stable du fingerprint matériel plutôt que l'ID de cookie volatil
1328
1365
  $currentDeviceHash = self::getCompositeDeviceHash($context);
1329
- $stableFpId = FingerprintBuilder::cyrb53(self::extractStablePart($currentDeviceHash));
1366
+ $stablePart = self::extractStablePart($currentDeviceHash);
1367
+ $stableFpId = !empty($stablePart) ? (string)FingerprintBuilder::cyrb53($stablePart) : (!empty($deviceId) ? $deviceId : (string)FingerprintBuilder::cyrb53($currentDeviceHash));
1330
1368
 
1331
1369
  $currentDeviceContributions = $subnetData['highScoreDevices'][$stableFpId] ?? 0;
1332
1370
  if ($currentDeviceContributions < 1 && $finalScore < 95) {
@@ -1991,6 +2029,21 @@ class RequestUtils
1991
2029
  return ['tcpAnomalyScore' => $tcpAnomalyScore];
1992
2030
  }
1993
2031
 
2032
+ /**
2033
+ * Helper to find the first key of a value from a list of needles in an array.
2034
+ * @param array<int, string> $haystack
2035
+ * @param array<int, string> $needles
2036
+ * @return int|null
2037
+ */
2038
+ private static function array_find_key(array $haystack, array $needles): ?int
2039
+ {
2040
+ foreach ($needles as $needle) {
2041
+ $key = array_search($needle, $haystack, true);
2042
+ if ($key !== false) return (int)$key;
2043
+ }
2044
+ return null;
2045
+ }
2046
+
1994
2047
  /**
1995
2048
  * Détecte les anomalies de protocole (HTTP/2 et QUIC/HTTP3) par rapport au User-Agent.
1996
2049
  * @param RequestContext $context
@@ -2005,52 +2058,96 @@ class RequestUtils
2005
2058
  $uaParts = self::parseUserAgent($ua);
2006
2059
  $browser = $uaParts['browser'] ?? null;
2007
2060
 
2008
- // HTTP/2 Anomaly logic
2009
- $h2Fp = $context->getHeader('x-http2-fingerprint') ?? $context->http2Fingerprint ?? null;
2010
- if ($h2Fp && is_string($h2Fp) && $browser) {
2011
- $parts = explode('|', $h2Fp);
2012
- if (count($parts) >= 4) {
2013
- $connWindow = (int)$parts[1];
2014
- $headerOrder = $parts[3];
2015
- $isChromium = str_starts_with($browser, 'Chrome') || str_starts_with($browser, 'Edge');
2016
- $isFirefox = str_starts_with($browser, 'Firefox');
2017
- $isSafari = str_starts_with($browser, 'Safari');
2018
-
2019
- if ($isChromium) {
2020
- if ($headerOrder && $headerOrder !== 'm,a,s,p') $http2Anomaly += 60.0;
2021
- if ($connWindow === 65535 || $connWindow === 65536) $http2Anomaly += 40.0;
2022
- } elseif ($isFirefox) {
2023
- if ($headerOrder && $headerOrder !== 'm,s,p,a') $http2Anomaly += 60.0;
2024
- } elseif ($isSafari) {
2025
- if ($headerOrder && $headerOrder !== 'm,s,p,a') $http2Anomaly += 60.0;
2061
+ if ($browser) {
2062
+ // HTTP/2 Anomaly logic
2063
+ $h2Fp = $context->getHeader('x-http2-fingerprint') ?? $context->http2Fingerprint ?? null;
2064
+ if ($h2Fp && is_string($h2Fp)) {
2065
+ $parts = explode('|', $h2Fp);
2066
+ if (count($parts) >= 3) {
2067
+ $connWindow = (int)$parts[1];
2068
+ $streamPriority = $parts[2] ?? '';
2069
+ $headerOrder = $parts[3] ?? '';
2070
+ $isChromium = str_starts_with($browser, 'Chrome') || str_starts_with($browser, 'Edge');
2071
+ $isFirefox = str_starts_with($browser, 'Firefox');
2072
+ $isSafari = str_starts_with($browser, 'Safari');
2073
+
2074
+ if ($isChromium) {
2075
+ if ($headerOrder && $headerOrder !== 'm,a,s,p') $http2Anomaly += 60.0;
2076
+ if ($connWindow === 65535 || $connWindow === 65536) $http2Anomaly += 40.0;
2077
+ if ($streamPriority === '0' || $streamPriority === '') $http2Anomaly += 50.0;
2078
+ } elseif ($isFirefox) {
2079
+ if ($headerOrder && $headerOrder !== 'm,s,p,a') $http2Anomaly += 60.0;
2080
+ } elseif ($isSafari) {
2081
+ if ($headerOrder && $headerOrder !== 'm,s,p,a') $http2Anomaly += 60.0;
2082
+ }
2026
2083
  }
2027
2084
  }
2028
- }
2029
2085
 
2030
- // QUIC Anomaly logic
2031
- $quicFp = $context->getHeader('x-quic-fp') ?? $context->quicFingerprint ?? null;
2032
- if ($quicFp && is_string($quicFp) && $browser) {
2033
- $parts = explode(';', $quicFp);
2034
- if (count($parts) >= 2) {
2035
- $params = [];
2036
- foreach (explode(',', $parts[1]) as $p) {
2037
- $kv = explode('=', $p, 2);
2038
- if (count($kv) === 2) {
2039
- $params[$kv[0]] = $kv[1];
2086
+ // QUIC Anomaly logic
2087
+ $quicFp = $context->getHeader('x-quic-fp') ?? $context->quicFingerprint ?? null;
2088
+ if ($quicFp && is_string($quicFp)) {
2089
+ $parts = explode(';', $quicFp);
2090
+ if (count($parts) >= 2) {
2091
+ $params = [];
2092
+ foreach (explode(',', $parts[1]) as $p) {
2093
+ $kv = explode('=', $p, 2);
2094
+ if (count($kv) === 2) {
2095
+ $params[$kv[0]] = $kv[1];
2096
+ }
2097
+ }
2098
+ $priorityOrder = $parts[2] ?? '';
2099
+ $frameOrderRaw = $parts[3] ?? $context->getHeader('x-quic-frame-order') ?? '';
2100
+ $frameOrder = array_values(array_filter(array_map('trim', explode(',', strtolower($frameOrderRaw)))));
2101
+
2102
+ $isChromium = str_starts_with($browser, 'Chrome') || str_starts_with($browser, 'Edge');
2103
+ $isFirefox = str_starts_with($browser, 'Firefox');
2104
+ $isSafari = str_starts_with($browser, 'Safari');
2105
+
2106
+ $maxData = (int)($params['1'] ?? $params['0x01'] ?? 0);
2107
+ $maxStreams = (int)($params['4'] ?? $params['8'] ?? $params['0x08'] ?? 0);
2108
+ $bidiLocal = (int)($params['5'] ?? $params['0x05'] ?? 0);
2109
+ $bidiRemote = (int)($params['6'] ?? $params['0x06'] ?? 0);
2110
+
2111
+ if ($isChromium) {
2112
+ if ($maxData > 0 && $maxData < 1048576) $quicAnomaly += 40.0;
2113
+ if ($maxStreams > 0 && $maxStreams !== 100) $quicAnomaly += 30.0;
2114
+ if (!empty($priorityOrder) && !str_contains($priorityOrder, 'u=')) $quicAnomaly += 30.0;
2115
+ if ($bidiLocal > 0 && ($bidiLocal < 524288 || $bidiLocal === 262144)) $quicAnomaly += 40.0;
2116
+ if ($bidiRemote > 0 && ($bidiRemote < 524288 || $bidiRemote === 262144)) $quicAnomaly += 30.0;
2117
+
2118
+ if (count($frameOrder) >= 2) {
2119
+ $sIdx = self::array_find_key($frameOrder, ['s', 'settings', '4']);
2120
+ $mIdx = self::array_find_key($frameOrder, ['m', 'max_streams', '18']);
2121
+ $pIdx = self::array_find_key($frameOrder, ['p', 'priority', 'priority_update', '15']);
2122
+
2123
+ if ($sIdx !== null && $sIdx !== 0) $quicAnomaly += 50.0;
2124
+ if ($mIdx !== null && $sIdx !== null && $mIdx < $sIdx) $quicAnomaly += 60.0;
2125
+ if ($pIdx !== null && $sIdx !== null && $pIdx < $sIdx) $quicAnomaly += 60.0;
2126
+ }
2127
+ } elseif ($isFirefox) {
2128
+ if ($maxData > 0 && $maxData > 5000000) $quicAnomaly += 40.0;
2129
+ if ($maxStreams === 100) $quicAnomaly += 50.0;
2130
+ if ($bidiLocal === 6291456) $quicAnomaly += 50.0;
2131
+
2132
+ if (count($frameOrder) >= 2) {
2133
+ $sIdx = self::array_find_key($frameOrder, ['s', 'settings', '4']);
2134
+ if ($sIdx !== null && $sIdx !== 0) $quicAnomaly += 50.0;
2135
+ }
2136
+ } elseif ($isSafari) {
2137
+ if ($maxStreams === 100 && $maxData === 1572864 && str_contains($priorityOrder, 'u=2,i')) {
2138
+ $quicAnomaly += 60.0;
2139
+ }
2140
+ if ($bidiLocal === 6291456) $quicAnomaly += 50.0;
2141
+
2142
+ if (count($frameOrder) >= 2) {
2143
+ $sIdx = self::array_find_key($frameOrder, ['s', 'settings', '4']);
2144
+ $mIdx = self::array_find_key($frameOrder, ['m', 'max_streams']);
2145
+ if ($mIdx !== null && ($mIdx === 0 || ($sIdx !== null && $mIdx < $sIdx))) {
2146
+ $quicAnomaly += 50.0;
2147
+ }
2148
+ }
2040
2149
  }
2041
2150
  }
2042
- $priorityOrder = $parts[2] ?? '';
2043
-
2044
- if (str_starts_with($browser, 'Chrome') || str_starts_with($browser, 'Edge')) {
2045
- $maxData = isset($params['1']) ? (int)$params['1'] : 0;
2046
- $maxStreams = isset($params['4']) ? (int)$params['4'] : 0;
2047
- if ($maxData > 0 && $maxData < 1048576) $quicAnomaly += 40.0;
2048
- if ($maxStreams > 0 && $maxStreams !== 100) $quicAnomaly += 30.0;
2049
- if (!empty($priorityOrder) && !str_contains($priorityOrder, 'u=')) $quicAnomaly += 30.0;
2050
- } elseif (str_starts_with($browser, 'Firefox')) {
2051
- $maxData = isset($params['1']) ? (int)$params['1'] : 0;
2052
- if ($maxData > 0 && $maxData > 5000000) $quicAnomaly += 40.0;
2053
- }
2054
2151
  }
2055
2152
  }
2056
2153
 
@@ -2059,7 +2156,9 @@ class RequestUtils
2059
2156
  0.0,
2060
2157
  min(100.0, $http2Anomaly),
2061
2158
  min(100.0, $quicAnomaly)
2062
- )
2159
+ ),
2160
+ 'http2AnomalyScore' => min(100.0, $http2Anomaly),
2161
+ 'quicAnomalyScore' => min(100.0, $quicAnomaly)
2063
2162
  ];
2064
2163
  }
2065
2164
 
@@ -4,6 +4,10 @@ declare(strict_types=1);
4
4
 
5
5
  namespace Anonympins\Fingerprint\WordPress;
6
6
 
7
+ if (!defined('ABSPATH')) {
8
+ exit;
9
+ }
10
+
7
11
  use Anonympins\Fingerprint\Store\IStore;
8
12
 
9
13
  // Polyfill pour ARRAY_A si chargé hors du cycle de vie standard de WordPress