@anonympins/fingerprint 0.3.7 → 0.4.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +331 -244
  2. package/README.md +63 -1201
  3. package/composer.json +38 -38
  4. package/index.js +4 -4
  5. package/package.json +103 -103
  6. package/phpunit.xml +20 -20
  7. package/public/fp.js +1 -1
  8. package/public/fp.wasm +0 -0
  9. package/src/js/build-client.js +1 -1
  10. package/src/js/fingerprint.client.js +2 -0
  11. package/src/js/fingerprint.js +4429 -4220
  12. package/src/js/mongodb-store.js +79 -79
  13. package/src/js/pow.solver.inline.js +31 -0
  14. package/src/js/pow.solver.js +31 -0
  15. package/src/js/tests/fingerprint.builder.test.js +79 -0
  16. package/src/js/tests/fingerprint.client.init.test.js +120 -0
  17. package/src/js/tests/fingerprint.client.test.js +105 -0
  18. package/src/js/tests/fingerprint.engine.test.js +371 -0
  19. package/src/js/tests/fingerprint.isMalicious.test.js +117 -0
  20. package/src/js/tests/fingerprint.test.js +2319 -0
  21. package/src/js/tests/ip-reputation.test.js +132 -0
  22. package/src/js/tests/ja3AnomalyDetector.test.js +135 -0
  23. package/src/js/tests/library.test.js +96 -0
  24. package/src/js/tests/metrics.test.js +104 -0
  25. package/src/js/tests/pow.solver.test.js +198 -0
  26. package/src/js/tests/problem-manager.test.js +323 -0
  27. package/src/js/tests/stores.test.js +118 -0
  28. package/src/php/Challenge/ChallengeUtils.php +361 -305
  29. package/src/php/Config/SecurityProfiles.php +271 -266
  30. package/src/php/FingerprintBuilder.php +185 -185
  31. package/src/php/FingerprintClient.php +131 -131
  32. package/src/php/FingerprintEngine.php +1006 -1006
  33. package/src/php/Ja3AnomalyDetector.php +227 -227
  34. package/src/php/Optimization/FunctionRegistry.php +62 -62
  35. package/src/php/Optimization/Optimization.php +255 -255
  36. package/src/php/Optimization/OptimizationOperators.php +304 -304
  37. package/src/php/Store/InMemoryStore.php +66 -66
  38. package/src/php/Store/MongoDbStore.php +104 -104
  39. package/src/php/Store/RedisStore.php +53 -53
  40. package/src/php/Tests/ChallengeUtilsTest.php +81 -81
  41. package/src/php/Tests/FingerprintBuilderTest.php +57 -57
  42. package/src/php/Tests/FingerprintClientTest.php +71 -0
  43. package/src/php/Tests/FingerprintEngineTest.php +299 -299
  44. package/src/php/Tests/IpReputationTest.php +156 -156
  45. package/src/php/Tests/Ja3AnomalyDetectorTest.php +179 -179
  46. package/src/php/Tests/MetricsTest.php +45 -45
  47. package/src/php/Tests/PowTest.php +39 -39
  48. package/src/php/Tests/ProblemManagerTest.php +296 -296
  49. package/src/php/Tests/RequestUtilsTest.php +253 -145
  50. package/src/php/Tests/TLSClientHelloParserTest.php +118 -0
  51. package/src/php/Tests/problems.config.json +8 -8
  52. package/src/php/Utils/BigInt.php +144 -144
  53. package/src/php/Utils/Logger.php +29 -29
  54. package/src/php/Utils/MaliciousPatterns.php +58 -58
  55. package/src/php/Utils/MetricsManager.php +166 -166
  56. package/src/php/Utils/RequestUtils.php +31 -4
  57. package/src/php/Utils/TLSClientHelloParser.php +117 -0
  58. package/src/php/bin/auto-tune.php +117 -117
@@ -1,305 +1,305 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Optimization;
6
-
7
- /**
8
- * Opérateurs pour les problèmes d'optimisation.
9
- */
10
- class OptimizationOperators
11
- {
12
- /**
13
- * Génère un nombre flottant aléatoire cryptographiquement sûr entre 0 (inclus) et 1 (exclus).
14
- */
15
- private static function secureRandom(): float
16
- {
17
- try {
18
- return random_int(0, PHP_INT_MAX - 1) / PHP_INT_MAX;
19
- } catch (\Exception $e) {
20
- return (float)mt_rand() / (float)mt_getrandmax(); // Fallback
21
- }
22
- }
23
-
24
- /**
25
- * Crée un évaluateur pour l'optimisation de portefeuille.
26
- * @param array $config
27
- * @return callable
28
- */
29
- public static function createPortfolioAllocator(array $config): callable
30
- {
31
- // Cette fonction est un placeholder. Une implémentation complète nécessiterait
32
- // une logique de calcul de rendement et de volatilité de portefeuille.
33
- return function (array $weights) use ($config): float {
34
- // Minimiser le rendement négatif (donc maximiser le rendement)
35
- return -array_sum($weights);
36
- };
37
- }
38
-
39
- /**
40
- * Crée un évaluateur pour l'auto-tuning complet de la configuration de sécurité.
41
- * @param array $context
42
- * @return callable
43
- */
44
- public static function createFullSecurityConfigEvaluator(array $context): callable
45
- {
46
- $trafficData = $context['trafficData'];
47
-
48
- return function (array $config) use ($trafficData): array {
49
- $falsePositives = 0;
50
- $falseNegatives = 0;
51
- $totalHumans = 0;
52
- $totalBots = 0;
53
-
54
- $calculateScore = function (array $log) use ($config): float {
55
- $score = 0.0;
56
- foreach ($config['weights'] as $key => $weight) {
57
- $score += ($log['vector'][$key] ?? 0) * $weight;
58
- }
59
- return $score;
60
- };
61
-
62
- $confidenceWeights = [
63
- 'request_passed' => 0.7,
64
- 'challenge_issued' => 1.0,
65
- 'request_blocked' => 1.0,
66
- 'challenge_solved' => 1.5,
67
- 'trap_triggered' => 2.0,
68
- ];
69
-
70
- foreach ($trafficData as $log) {
71
- $confidence = $confidenceWeights[$log['type']] ?? 1.0;
72
- $isLikelyBot = in_array($log['type'], ['challenge_issued', 'request_blocked', 'trap_triggered']);
73
- $isLikelyHuman = in_array($log['type'], ['request_passed', 'challenge_solved']);
74
-
75
- if ($isLikelyBot) {
76
- $totalBots += $confidence;
77
- $score = $calculateScore($log);
78
- if ($score < $config['thresholds']['low']) {
79
- $falseNegatives += $confidence;
80
- }
81
- } elseif ($isLikelyHuman) {
82
- $totalHumans += $confidence;
83
- $score = $calculateScore($log);
84
- if ($score >= $config['thresholds']['low']) {
85
- $falsePositives += $confidence;
86
- }
87
- }
88
- }
89
-
90
- $falsePositiveRate = $totalHumans > 0 ? $falsePositives / $totalHumans : 0;
91
- $falseNegativeRate = $totalBots > 0 ? $falseNegatives / $totalBots : 0;
92
-
93
- return [$falsePositiveRate, $falseNegativeRate];
94
- };
95
- }
96
-
97
- /**
98
- * Résout le problème de l'auto-tuning complet de la configuration de sécurité.
99
- * @param array $context
100
- * @param array $options
101
- * @return array
102
- */
103
- public static function solveFullSecurityTuning(array $context, array $options = []): array
104
- {
105
- $fitnessFunction = self::createFullSecurityConfigEvaluator($context);
106
-
107
- $createIndividual = function (): array {
108
- return [
109
- 'thresholds' => [
110
- 'low' => 15 + self::secureRandom() * 20,
111
- 'medium' => 40 + self::secureRandom() * 25,
112
- 'high' => 70 + self::secureRandom() * 20,
113
- ],
114
- 'weights' => [
115
- 'historyScore' => self::secureRandom(),
116
- 'rotationScore' => self::secureRandom(),
117
- 'headerAnomalyScore' => self::secureRandom(),
118
- 'requestPatternScore' => 0.5 + self::secureRandom(),
119
- 'inconsistencyScore' => self::secureRandom(),
120
- 'honeypotScore' => 1.0,
121
- 'behaviorScore' => self::secureRandom(),
122
- 'crossLayerInconsistencyScore' => self::secureRandom(),
123
- 'timeInconsistencyScore' => self::secureRandom(),
124
- 'tlsSpoofingScore' => self::secureRandom(),
125
- 'botScore' => self::secureRandom(),
126
- 'cookieDroppingScore' => self::secureRandom(),
127
- 'threatIntelScore' => self::secureRandom(),
128
- ],
129
- 'patterns' => [
130
- 'velocityThreshold' => 100 + self::secureRandom() * 400,
131
- 'burstThreshold' => 300 + self::secureRandom() * 700,
132
- 'scrapeThreshold' => 500 + self::secureRandom() * 1000,
133
- 'regularityThreshold' => 50 + self::secureRandom() * 200,
134
- 'decayFactor' => 0.85 + self::secureRandom() * 0.14,
135
- 'inactivityReset' => 15000 + self::secureRandom() * 45000,
136
- ]
137
- ];
138
- };
139
-
140
- $crossover = function (array $c1, array $c2): array {
141
- $child = $c1;
142
- foreach (['thresholds', 'weights', 'patterns'] as $section) {
143
- foreach ($child[$section] as $key => $value) {
144
- if ($key !== 'honeypotScore') {
145
- $child[$section][$key] = ($c1[$section][$key] + $c2[$section][$key]) / 2;
146
- }
147
- }
148
- }
149
- return $child;
150
- };
151
-
152
- $mutate = function (array $c): array {
153
- $newConfig = $c;
154
- $sections = [
155
- ['name' => 'patterns', 'weight' => 0.5],
156
- ['name' => 'weights', 'weight' => 0.35],
157
- ['name' => 'thresholds', 'weight' => 0.15]
158
- ];
159
- $rand = self::secureRandom();
160
- $cumulativeWeight = 0;
161
- $sectionToMutate = 'patterns';
162
- foreach ($sections as $section) {
163
- $cumulativeWeight += $section['weight'];
164
- if ($rand < $cumulativeWeight) {
165
- $sectionToMutate = $section['name'];
166
- break;
167
- }
168
- }
169
-
170
- $keys = array_keys($newConfig[$sectionToMutate]);
171
- $keyToMutate = $keys[random_int(0, count($keys) - 1)];
172
-
173
- if ($keyToMutate === 'honeypotScore') return $newConfig;
174
-
175
- $mutationAmount = (self::secureRandom() - 0.5) * 0.4;
176
- $newConfig[$sectionToMutate][$keyToMutate] *= (1 + $mutationAmount);
177
-
178
- if ($sectionToMutate === 'weights') {
179
- $newConfig[$sectionToMutate][$keyToMutate] = max(0, min(1.5, $newConfig[$sectionToMutate][$keyToMutate]));
180
- }
181
-
182
- return $newConfig;
183
- };
184
-
185
- return Optimization::geneticAlgorithmMultiObjective(
186
- $createIndividual,
187
- $fitnessFunction,
188
- $crossover,
189
- $mutate,
190
- array_merge(['generations' => 50, 'populationSize' => 50], $options)
191
- );
192
- }
193
-
194
- /**
195
- * Crée un évaluateur pour trouver les seuils de détection de fraude optimaux.
196
- * @param array $context
197
- * @return callable
198
- */
199
- public static function createFraudThresholdEvaluator(array $context): callable
200
- {
201
- $legitimateClicks = $context['legitimateClicks'] ?? [];
202
- $fraudulentClicks = $context['fraudulentClicks'] ?? [];
203
-
204
- return function (array $solution) use ($legitimateClicks, $fraudulentClicks): array {
205
- [$minTimeToClick, $maxClickVariance, $minMouseEntropy, $minScrollEvents] = $solution;
206
-
207
- $truePositives = 0;
208
- $falsePositives = 0;
209
-
210
- foreach ($fraudulentClicks as $click) {
211
- if (
212
- ($click['timeToClick'] < $minTimeToClick) ||
213
- ($click['mouseEntropy'] < $minMouseEntropy)
214
- ) {
215
- $truePositives++;
216
- }
217
- }
218
-
219
- foreach ($legitimateClicks as $click) {
220
- if (
221
- ($click['timeToClick'] < $minTimeToClick) ||
222
- ($click['mouseEntropy'] < $minMouseEntropy)
223
- ) {
224
- $falsePositives++;
225
- }
226
- }
227
-
228
- $totalFraudulent = count($fraudulentClicks) ?: 1;
229
- $totalLegitimate = count($legitimateClicks) ?: 1;
230
-
231
- $objective1 = 1 - ($truePositives / $totalFraudulent);
232
- $objective2 = $falsePositives / $totalLegitimate;
233
-
234
- return [$objective1, $objective2];
235
- };
236
- }
237
-
238
- /**
239
- * Résout le problème de la détection de fraude.
240
- * @param array $context
241
- * @param array $options
242
- * @return array
243
- */
244
- public static function solveFraudDetection(array $context, array $options = []): array
245
- {
246
- $fitnessFunction = self::createFraudThresholdEvaluator($context);
247
-
248
- $createIndividual = function (): array {
249
- return [
250
- 100 + self::secureRandom() * 4900, // minTimeToClick
251
- 1 + self::secureRandom() * 9999, // maxClickVariance
252
- self::secureRandom() * 0.5, // minMouseEntropy
253
- floor(self::secureRandom() * 10) // minScrollEvents
254
- ];
255
- };
256
-
257
- $crossover = fn ($s1, $s2) => array_map(fn ($a, $b) => ($a + $b) / 2, $s1, $s2);
258
-
259
- $mutate = function (array $solution): array {
260
- $i = random_int(0, 3);
261
- $mutationFactors = [500, 1000, 0.1, 2];
262
- $solution[$i] += (self::secureRandom() - 0.5) * $mutationFactors[$i];
263
- return $solution;
264
- };
265
-
266
- return Optimization::geneticAlgorithmMultiObjective(
267
- $createIndividual,
268
- $fitnessFunction,
269
- $crossover,
270
- $mutate,
271
- array_merge(['generations' => 80, 'populationSize' => 60], $options)
272
- );
273
- }
274
-
275
- /**
276
- * Placeholder pour le solveur TSP.
277
- * @param array $cities
278
- * @param array $options
279
- * @return array
280
- */
281
- public static function solveTSP(array $cities, array $options = []): array
282
- {
283
- // Implémentation factice pour la complétude
284
- return ['solution' => array_keys($cities), 'energy' => 100];
285
- }
286
-
287
- /**
288
- * Placeholder pour le solveur de portefeuille.
289
- * @param array $assets
290
- * @param float $maxVolatility
291
- * @param array $options
292
- * @return array
293
- */
294
- public static function solvePortfolio(array $assets, float $maxVolatility, array $options = []): array
295
- {
296
- // Implémentation factice pour la complétude
297
- return ['solution' => array_fill(0, count($assets), 1 / count($assets)), 'fitness' => -0.1];
298
- }
299
-
300
- public static function solveFacilityLocation(array $customers, int $numFacilities, array $bounds, array $options = []): array
301
- {
302
- // Implémentation factice pour la complétude
303
- return ['solution' => [], 'energy' => 0];
304
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Optimization;
6
+
7
+ /**
8
+ * Opérateurs pour les problèmes d'optimisation.
9
+ */
10
+ class OptimizationOperators
11
+ {
12
+ /**
13
+ * Génère un nombre flottant aléatoire cryptographiquement sûr entre 0 (inclus) et 1 (exclus).
14
+ */
15
+ private static function secureRandom(): float
16
+ {
17
+ try {
18
+ return random_int(0, PHP_INT_MAX - 1) / PHP_INT_MAX;
19
+ } catch (\Exception $e) {
20
+ return (float)mt_rand() / (float)mt_getrandmax(); // Fallback
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Crée un évaluateur pour l'optimisation de portefeuille.
26
+ * @param array $config
27
+ * @return callable
28
+ */
29
+ public static function createPortfolioAllocator(array $config): callable
30
+ {
31
+ // Cette fonction est un placeholder. Une implémentation complète nécessiterait
32
+ // une logique de calcul de rendement et de volatilité de portefeuille.
33
+ return function (array $weights) use ($config): float {
34
+ // Minimiser le rendement négatif (donc maximiser le rendement)
35
+ return -array_sum($weights);
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Crée un évaluateur pour l'auto-tuning complet de la configuration de sécurité.
41
+ * @param array $context
42
+ * @return callable
43
+ */
44
+ public static function createFullSecurityConfigEvaluator(array $context): callable
45
+ {
46
+ $trafficData = $context['trafficData'];
47
+
48
+ return function (array $config) use ($trafficData): array {
49
+ $falsePositives = 0;
50
+ $falseNegatives = 0;
51
+ $totalHumans = 0;
52
+ $totalBots = 0;
53
+
54
+ $calculateScore = function (array $log) use ($config): float {
55
+ $score = 0.0;
56
+ foreach ($config['weights'] as $key => $weight) {
57
+ $score += ($log['vector'][$key] ?? 0) * $weight;
58
+ }
59
+ return $score;
60
+ };
61
+
62
+ $confidenceWeights = [
63
+ 'request_passed' => 0.7,
64
+ 'challenge_issued' => 1.0,
65
+ 'request_blocked' => 1.0,
66
+ 'challenge_solved' => 1.5,
67
+ 'trap_triggered' => 2.0,
68
+ ];
69
+
70
+ foreach ($trafficData as $log) {
71
+ $confidence = $confidenceWeights[$log['type']] ?? 1.0;
72
+ $isLikelyBot = in_array($log['type'], ['challenge_issued', 'request_blocked', 'trap_triggered']);
73
+ $isLikelyHuman = in_array($log['type'], ['request_passed', 'challenge_solved']);
74
+
75
+ if ($isLikelyBot) {
76
+ $totalBots += $confidence;
77
+ $score = $calculateScore($log);
78
+ if ($score < $config['thresholds']['low']) {
79
+ $falseNegatives += $confidence;
80
+ }
81
+ } elseif ($isLikelyHuman) {
82
+ $totalHumans += $confidence;
83
+ $score = $calculateScore($log);
84
+ if ($score >= $config['thresholds']['low']) {
85
+ $falsePositives += $confidence;
86
+ }
87
+ }
88
+ }
89
+
90
+ $falsePositiveRate = $totalHumans > 0 ? $falsePositives / $totalHumans : 0;
91
+ $falseNegativeRate = $totalBots > 0 ? $falseNegatives / $totalBots : 0;
92
+
93
+ return [$falsePositiveRate, $falseNegativeRate];
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Résout le problème de l'auto-tuning complet de la configuration de sécurité.
99
+ * @param array $context
100
+ * @param array $options
101
+ * @return array
102
+ */
103
+ public static function solveFullSecurityTuning(array $context, array $options = []): array
104
+ {
105
+ $fitnessFunction = self::createFullSecurityConfigEvaluator($context);
106
+
107
+ $createIndividual = function (): array {
108
+ return [
109
+ 'thresholds' => [
110
+ 'low' => 15 + self::secureRandom() * 20,
111
+ 'medium' => 40 + self::secureRandom() * 25,
112
+ 'high' => 70 + self::secureRandom() * 20,
113
+ ],
114
+ 'weights' => [
115
+ 'historyScore' => self::secureRandom(),
116
+ 'rotationScore' => self::secureRandom(),
117
+ 'headerAnomalyScore' => self::secureRandom(),
118
+ 'requestPatternScore' => 0.5 + self::secureRandom(),
119
+ 'inconsistencyScore' => self::secureRandom(),
120
+ 'honeypotScore' => 1.0,
121
+ 'behaviorScore' => self::secureRandom(),
122
+ 'crossLayerInconsistencyScore' => self::secureRandom(),
123
+ 'timeInconsistencyScore' => self::secureRandom(),
124
+ 'tlsSpoofingScore' => self::secureRandom(),
125
+ 'botScore' => self::secureRandom(),
126
+ 'cookieDroppingScore' => self::secureRandom(),
127
+ 'threatIntelScore' => self::secureRandom(),
128
+ ],
129
+ 'patterns' => [
130
+ 'velocityThreshold' => 100 + self::secureRandom() * 400,
131
+ 'burstThreshold' => 300 + self::secureRandom() * 700,
132
+ 'scrapeThreshold' => 500 + self::secureRandom() * 1000,
133
+ 'regularityThreshold' => 50 + self::secureRandom() * 200,
134
+ 'decayFactor' => 0.85 + self::secureRandom() * 0.14,
135
+ 'inactivityReset' => 15000 + self::secureRandom() * 45000,
136
+ ]
137
+ ];
138
+ };
139
+
140
+ $crossover = function (array $c1, array $c2): array {
141
+ $child = $c1;
142
+ foreach (['thresholds', 'weights', 'patterns'] as $section) {
143
+ foreach ($child[$section] as $key => $value) {
144
+ if ($key !== 'honeypotScore') {
145
+ $child[$section][$key] = ($c1[$section][$key] + $c2[$section][$key]) / 2;
146
+ }
147
+ }
148
+ }
149
+ return $child;
150
+ };
151
+
152
+ $mutate = function (array $c): array {
153
+ $newConfig = $c;
154
+ $sections = [
155
+ ['name' => 'patterns', 'weight' => 0.5],
156
+ ['name' => 'weights', 'weight' => 0.35],
157
+ ['name' => 'thresholds', 'weight' => 0.15]
158
+ ];
159
+ $rand = self::secureRandom();
160
+ $cumulativeWeight = 0;
161
+ $sectionToMutate = 'patterns';
162
+ foreach ($sections as $section) {
163
+ $cumulativeWeight += $section['weight'];
164
+ if ($rand < $cumulativeWeight) {
165
+ $sectionToMutate = $section['name'];
166
+ break;
167
+ }
168
+ }
169
+
170
+ $keys = array_keys($newConfig[$sectionToMutate]);
171
+ $keyToMutate = $keys[random_int(0, count($keys) - 1)];
172
+
173
+ if ($keyToMutate === 'honeypotScore') return $newConfig;
174
+
175
+ $mutationAmount = (self::secureRandom() - 0.5) * 0.4;
176
+ $newConfig[$sectionToMutate][$keyToMutate] *= (1 + $mutationAmount);
177
+
178
+ if ($sectionToMutate === 'weights') {
179
+ $newConfig[$sectionToMutate][$keyToMutate] = max(0, min(1.5, $newConfig[$sectionToMutate][$keyToMutate]));
180
+ }
181
+
182
+ return $newConfig;
183
+ };
184
+
185
+ return Optimization::geneticAlgorithmMultiObjective(
186
+ $createIndividual,
187
+ $fitnessFunction,
188
+ $crossover,
189
+ $mutate,
190
+ array_merge(['generations' => 50, 'populationSize' => 50], $options)
191
+ );
192
+ }
193
+
194
+ /**
195
+ * Crée un évaluateur pour trouver les seuils de détection de fraude optimaux.
196
+ * @param array $context
197
+ * @return callable
198
+ */
199
+ public static function createFraudThresholdEvaluator(array $context): callable
200
+ {
201
+ $legitimateClicks = $context['legitimateClicks'] ?? [];
202
+ $fraudulentClicks = $context['fraudulentClicks'] ?? [];
203
+
204
+ return function (array $solution) use ($legitimateClicks, $fraudulentClicks): array {
205
+ [$minTimeToClick, $maxClickVariance, $minMouseEntropy, $minScrollEvents] = $solution;
206
+
207
+ $truePositives = 0;
208
+ $falsePositives = 0;
209
+
210
+ foreach ($fraudulentClicks as $click) {
211
+ if (
212
+ ($click['timeToClick'] < $minTimeToClick) ||
213
+ ($click['mouseEntropy'] < $minMouseEntropy)
214
+ ) {
215
+ $truePositives++;
216
+ }
217
+ }
218
+
219
+ foreach ($legitimateClicks as $click) {
220
+ if (
221
+ ($click['timeToClick'] < $minTimeToClick) ||
222
+ ($click['mouseEntropy'] < $minMouseEntropy)
223
+ ) {
224
+ $falsePositives++;
225
+ }
226
+ }
227
+
228
+ $totalFraudulent = count($fraudulentClicks) ?: 1;
229
+ $totalLegitimate = count($legitimateClicks) ?: 1;
230
+
231
+ $objective1 = 1 - ($truePositives / $totalFraudulent);
232
+ $objective2 = $falsePositives / $totalLegitimate;
233
+
234
+ return [$objective1, $objective2];
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Résout le problème de la détection de fraude.
240
+ * @param array $context
241
+ * @param array $options
242
+ * @return array
243
+ */
244
+ public static function solveFraudDetection(array $context, array $options = []): array
245
+ {
246
+ $fitnessFunction = self::createFraudThresholdEvaluator($context);
247
+
248
+ $createIndividual = function (): array {
249
+ return [
250
+ 100 + self::secureRandom() * 4900, // minTimeToClick
251
+ 1 + self::secureRandom() * 9999, // maxClickVariance
252
+ self::secureRandom() * 0.5, // minMouseEntropy
253
+ floor(self::secureRandom() * 10) // minScrollEvents
254
+ ];
255
+ };
256
+
257
+ $crossover = fn ($s1, $s2) => array_map(fn ($a, $b) => ($a + $b) / 2, $s1, $s2);
258
+
259
+ $mutate = function (array $solution): array {
260
+ $i = random_int(0, 3);
261
+ $mutationFactors = [500, 1000, 0.1, 2];
262
+ $solution[$i] += (self::secureRandom() - 0.5) * $mutationFactors[$i];
263
+ return $solution;
264
+ };
265
+
266
+ return Optimization::geneticAlgorithmMultiObjective(
267
+ $createIndividual,
268
+ $fitnessFunction,
269
+ $crossover,
270
+ $mutate,
271
+ array_merge(['generations' => 80, 'populationSize' => 60], $options)
272
+ );
273
+ }
274
+
275
+ /**
276
+ * Placeholder pour le solveur TSP.
277
+ * @param array $cities
278
+ * @param array $options
279
+ * @return array
280
+ */
281
+ public static function solveTSP(array $cities, array $options = []): array
282
+ {
283
+ // Implémentation factice pour la complétude
284
+ return ['solution' => array_keys($cities), 'energy' => 100];
285
+ }
286
+
287
+ /**
288
+ * Placeholder pour le solveur de portefeuille.
289
+ * @param array $assets
290
+ * @param float $maxVolatility
291
+ * @param array $options
292
+ * @return array
293
+ */
294
+ public static function solvePortfolio(array $assets, float $maxVolatility, array $options = []): array
295
+ {
296
+ // Implémentation factice pour la complétude
297
+ return ['solution' => array_fill(0, count($assets), 1 / count($assets)), 'fitness' => -0.1];
298
+ }
299
+
300
+ public static function solveFacilityLocation(array $customers, int $numFacilities, array $bounds, array $options = []): array
301
+ {
302
+ // Implémentation factice pour la complétude
303
+ return ['solution' => [], 'energy' => 0];
304
+ }
305
305
  }