@anonympins/fingerprint 0.7.0 → 0.7.1

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 (61) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -1
  3. package/composer.json +8 -1
  4. package/package.json +1 -1
  5. package/public/fingerprint-wordpress.zip +0 -0
  6. package/src/js/build-client.js +8 -10
  7. package/src/js/fingerprint.builder.js +37 -38
  8. package/src/js/fingerprint.client.js +1531 -1463
  9. package/src/js/fingerprint.client.obfuscated.js +1 -0
  10. package/src/js/fingerprint.js +152 -10
  11. package/src/js/fingerprint.utils.js +213 -213
  12. package/src/js/library.js +340 -376
  13. package/src/js/mongodb-store.js +1 -1
  14. package/src/js/optimization.worker.js +27 -27
  15. package/src/js/pow.solver.inline.js +154 -56
  16. package/src/js/pow.solver.js +180 -92
  17. package/src/js/pow.worker.js +48 -48
  18. package/src/js/redis-store.js +1 -1
  19. package/src/js/tests/fingerprint.client.init.test.js +146 -143
  20. package/src/js/tests/fingerprint.test.js +34 -7
  21. package/src/js/tests/ip-reputation.test.js +1 -0
  22. package/src/js/upow-model-task.js +14 -15
  23. package/src/php/AutoTuner.php +34 -34
  24. package/src/php/Challenge/ChallengeUtils.php +56 -10
  25. package/src/php/Config/SecurityProfiles.php +20 -0
  26. package/src/php/DirectFingerprint.php +190 -145
  27. package/src/php/FingerprintBuilder.php +29 -30
  28. package/src/php/FingerprintClient.php +147 -148
  29. package/src/php/FingerprintEngine.php +7 -5
  30. package/src/php/Ja3AnomalyDetector.php +33 -32
  31. package/src/php/Optimization/FunctionRegistry.php +7 -7
  32. package/src/php/Optimization/Optimization.php +24 -24
  33. package/src/php/Optimization/OptimizationOperators.php +26 -25
  34. package/src/php/Optimization/ProblemInitializers.php +52 -52
  35. package/src/php/ProblemManager.php +31 -34
  36. package/src/php/RequestContext.php +8 -1
  37. package/src/php/Store/IStore.php +7 -8
  38. package/src/php/Store/InMemoryStore.php +66 -66
  39. package/src/php/Store/MongoDbStore.php +5 -5
  40. package/src/php/Store/RedisStore.php +5 -5
  41. package/src/php/Store/StoreManager.php +35 -35
  42. package/src/php/Tests/ChallengeUtilsTest.php +453 -453
  43. package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
  44. package/src/php/Tests/FingerprintEngineTest.php +662 -662
  45. package/src/php/Tests/IpReputationTest.php +10 -0
  46. package/src/php/Tests/MaliciousPatternsTest.php +103 -103
  47. package/src/php/Tests/MetricsTest.php +97 -97
  48. package/src/php/Tests/ProblemManagerTest.php +376 -376
  49. package/src/php/Tests/QuicFingerprintTest.php +53 -53
  50. package/src/php/Tests/RequestUtilsTest.php +471 -471
  51. package/src/php/Tests/TLSClientHelloParserTest.php +177 -177
  52. package/src/php/Tests/config/ed25519_key.json +3 -3
  53. package/src/php/Utils/Env.php +49 -49
  54. package/src/php/Utils/MaliciousPatterns.php +74 -74
  55. package/src/php/Utils/MetricsManager.php +209 -209
  56. package/src/php/Utils/RequestUtils.php +129 -7
  57. package/src/php/Utils/TLSClientHelloParser.php +369 -369
  58. package/src/php/WordPress/WpDbStore.php +155 -0
  59. package/src/php/WordPress/fingerprint-wordpress.php +217 -0
  60. package/src/php/WordPress/package.php +123 -0
  61. package/src/php/bin/auto-tune.php +118 -118
@@ -35,7 +35,7 @@ export function createRedisStore(redisClient) {
35
35
  reconnecting = false;
36
36
  clearInterval(interval);
37
37
  } catch (err) {
38
- // Continue de tenter la reconnexion toutes les 5 secondes
38
+ // Keep retrying connection every 5 seconds
39
39
  }
40
40
  }, 5000);
41
41
  if (interval.unref) interval.unref();
@@ -1,144 +1,147 @@
1
- import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
2
- import {JSDOM} from 'jsdom';
3
- import ClientLibrary from '../fingerprint.client.js';
4
-
5
- // --- Setup JSDOM Environment ---
6
- // Vitest peut être configuré pour le faire automatiquement, mais le faire manuellement
7
- // ici rend le test explicite et portable.
8
- const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
9
- url: 'http://localhost',
10
- });
11
-
12
- // In recent Node.js versions, some global properties like 'navigator', 'performance',
13
- // and 'screen' are read-only. To ensure our JSDOM environment works correctly across
14
- // all versions, we use Object.defineProperty to make these properties writable.
15
- Object.defineProperty(global, 'window', { value: dom.window, writable: true });
16
- Object.defineProperty(global, 'document', { value: dom.window.document, writable: true });
17
- Object.defineProperty(global, 'navigator', { value: dom.window.navigator, writable: true });
18
- Object.defineProperty(global, 'screen', { value: dom.window.screen, writable: true });
19
- Object.defineProperty(global, 'performance', { value: dom.window.performance, writable: true });
20
-
21
- global.fetch = vi.fn(); // Mock global fetch
22
- global.Headers = dom.window.Headers;
23
- global.Request = dom.window.Request;
24
- global.URL = dom.window.URL;
25
- global.TextEncoder = dom.window.TextEncoder;
26
-
27
-
28
- describe('ClientLibrary.initializeClient', () => {
29
-
30
- // On utilise des espions (spies) pour vérifier si les méthodes internes sont appelées.
31
- let startMouseSpy, startKeystrokeSpy, startClickSpy, startTouchSpy, startRenderingSpy, initWasmSpy, initHoneypotsSpy, injectTrapsSpy, initFetchSpy, injectPhantomTrapsSpy;
32
-
33
- beforeEach(() => {
34
- // Réinitialiser l'état du client avant chaque test
35
- ClientLibrary._resetCache();
36
-
37
- // Créer les espions sur les méthodes internes
38
- startMouseSpy = vi.spyOn(ClientLibrary, 'startMouseEntropyTracker');
39
- startKeystrokeSpy = vi.spyOn(ClientLibrary, 'startKeystrokeDynamicsTracker');
40
- startClickSpy = vi.spyOn(ClientLibrary, 'startClickTracker');
41
- startTouchSpy = vi.spyOn(ClientLibrary, 'startTouchEventTracker');
42
- startRenderingSpy = vi.spyOn(ClientLibrary, 'startRenderingTracker');
43
- initWasmSpy = vi.spyOn(ClientLibrary, 'initializeWasm').mockResolvedValue(undefined);
44
- initHoneypotsSpy = vi.spyOn(ClientLibrary, 'initializeHoneypots');
45
- injectTrapsSpy = vi.spyOn(ClientLibrary, 'injectTrapLinks');
46
- initFetchSpy = vi.spyOn(ClientLibrary, 'initializeFetch');
47
- injectPhantomTrapsSpy = vi.spyOn(ClientLibrary, 'injectPhantomTraps');
48
- });
49
-
50
- afterEach(() => {
51
- // Restaurer les espions après chaque test pour ne pas affecter les autres tests
52
- vi.restoreAllMocks();
53
- });
54
-
55
- it('should enable all trackers by default', () => {
56
- ClientLibrary.initializeClient();
57
-
58
- expect(startMouseSpy).toHaveBeenCalled();
59
- expect(startKeystrokeSpy).toHaveBeenCalled();
60
- expect(startClickSpy).toHaveBeenCalled();
61
- expect(startTouchSpy).toHaveBeenCalled();
62
- expect(startRenderingSpy).toHaveBeenCalled();
63
- expect(injectPhantomTrapsSpy).toHaveBeenCalled();
64
- });
65
-
66
- it('should disable trackers when configured', () => {
67
- ClientLibrary.initializeClient({ mouse: false, keystrokes: false, clicks: false, touches: false, rendering: false });
68
-
69
- expect(startMouseSpy).not.toHaveBeenCalled();
70
- expect(startKeystrokeSpy).not.toHaveBeenCalled();
71
- expect(startClickSpy).not.toHaveBeenCalled();
72
- expect(startTouchSpy).not.toHaveBeenCalled();
73
- expect(startRenderingSpy).not.toHaveBeenCalled();
74
- });
75
-
76
- it('should initialize WASM if wasmPath is configured', () => {
77
- ClientLibrary.initializeClient({ wasmPath: '/fp.js' });
78
-
79
- expect(initWasmSpy).toHaveBeenCalledWith('/fp.js');
80
- });
81
-
82
- it('should disable phantom traps when configured', () => {
83
- ClientLibrary.initializeClient({ phantomTraps: false });
84
-
85
- expect(injectPhantomTrapsSpy).not.toHaveBeenCalled();
86
- });
87
-
88
- it('should initialize honeypots with the provided field names', () => {
89
- const honeypotFields = ['email_confirm', 'user_nickname'];
90
- ClientLibrary.initializeClient({ honeypots: honeypotFields });
91
-
92
- expect(initHoneypotsSpy).toHaveBeenCalledWith(honeypotFields);
93
- });
94
-
95
- it('should not initialize honeypots if the array is empty', () => {
96
- ClientLibrary.initializeClient({ honeypots: [] });
97
-
98
- expect(initHoneypotsSpy).not.toHaveBeenCalled();
99
- });
100
-
101
- it('should inject trap URLs when provided', () => {
102
- const urls = ['/trap1?sig=123', '/trap2?sig=456'];
103
- ClientLibrary.initializeClient({ trapUrls: urls });
104
-
105
- expect(injectTrapsSpy).toHaveBeenCalledWith(urls);
106
- });
107
-
108
- it('should not inject trap URLs if the array is empty', () => {
109
- ClientLibrary.initializeClient({ trapUrls: [] });
110
-
111
- expect(injectTrapsSpy).not.toHaveBeenCalled();
112
- });
113
-
114
- it('should initialize fetch interception when fetch config is present', () => {
115
- const targetDomains = ['api.example.com'];
116
- ClientLibrary.initializeClient({ fetch: { targetDomains } });
117
-
118
- expect(initFetchSpy).toHaveBeenCalledWith(targetDomains);
119
- });
120
-
121
- it('should not initialize fetch interception if fetch config is absent', () => {
122
- ClientLibrary.initializeClient(); // No fetch config
123
-
124
- expect(initFetchSpy).not.toHaveBeenCalled();
125
- });
126
-
127
- it('should call all initializers correctly when a full config is provided', () => {
128
- const config = {
129
- mouse: true,
130
- keystrokes: true,
131
- honeypots: ['field1'],
132
- trapUrls: ['/trap1'],
133
- fetch: { targetDomains: ['api.com'] }
134
- };
135
- ClientLibrary.initializeClient(config);
136
-
137
- expect(startMouseSpy).toHaveBeenCalled();
138
- expect(startKeystrokeSpy).toHaveBeenCalled();
139
- expect(initHoneypotsSpy).toHaveBeenCalledWith(config.honeypots);
140
- expect(injectTrapsSpy).toHaveBeenCalledWith(config.trapUrls);
141
- expect(initFetchSpy).toHaveBeenCalledWith(config.fetch.targetDomains);
142
- });
143
-
1
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
2
+ import {JSDOM} from 'jsdom';
3
+ import ClientLibrary from '../fingerprint.client.js';
4
+
5
+ // --- Setup JSDOM Environment ---
6
+ // Vitest peut être configuré pour le faire automatiquement, mais le faire manuellement
7
+ // ici rend le test explicite et portable.
8
+ const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
9
+ url: 'http://localhost',
10
+ });
11
+
12
+ // In recent Node.js versions, some global properties like 'navigator', 'performance',
13
+ // and 'screen' are read-only. To ensure our JSDOM environment works correctly across
14
+ // all versions, we use Object.defineProperty to make these properties writable.
15
+ Object.defineProperty(global, 'window', { value: dom.window, writable: true });
16
+ Object.defineProperty(global, 'document', { value: dom.window.document, writable: true });
17
+ Object.defineProperty(global, 'navigator', { value: dom.window.navigator, writable: true });
18
+ Object.defineProperty(global, 'screen', { value: dom.window.screen, writable: true });
19
+ Object.defineProperty(global, 'performance', { value: dom.window.performance, writable: true });
20
+
21
+ global.fetch = vi.fn(); // Mock global fetch
22
+ global.Headers = dom.window.Headers;
23
+ global.Request = dom.window.Request;
24
+ global.URL = dom.window.URL;
25
+ global.TextEncoder = dom.window.TextEncoder;
26
+
27
+
28
+ describe('ClientLibrary.initializeClient', () => {
29
+
30
+ // On utilise des espions (spies) pour vérifier si les méthodes internes sont appelées.
31
+ let startMouseSpy, startKeystrokeSpy, startClickSpy, startTouchSpy, startMotionSpy, startRenderingSpy, initWasmSpy, initHoneypotsSpy, injectTrapsSpy, initFetchSpy, injectPhantomTrapsSpy;
32
+
33
+ beforeEach(() => {
34
+ // Réinitialiser l'état du client avant chaque test
35
+ ClientLibrary._resetCache();
36
+
37
+ // Créer les espions sur les méthodes internes
38
+ startMouseSpy = vi.spyOn(ClientLibrary, 'startMouseEntropyTracker');
39
+ startKeystrokeSpy = vi.spyOn(ClientLibrary, 'startKeystrokeDynamicsTracker');
40
+ startClickSpy = vi.spyOn(ClientLibrary, 'startClickTracker');
41
+ startTouchSpy = vi.spyOn(ClientLibrary, 'startTouchEventTracker');
42
+ startMotionSpy = vi.spyOn(ClientLibrary, 'startMotionTracker');
43
+ startRenderingSpy = vi.spyOn(ClientLibrary, 'startRenderingTracker');
44
+ initWasmSpy = vi.spyOn(ClientLibrary, 'initializeWasm').mockResolvedValue(undefined);
45
+ initHoneypotsSpy = vi.spyOn(ClientLibrary, 'initializeHoneypots');
46
+ injectTrapsSpy = vi.spyOn(ClientLibrary, 'injectTrapLinks');
47
+ initFetchSpy = vi.spyOn(ClientLibrary, 'initializeFetch');
48
+ injectPhantomTrapsSpy = vi.spyOn(ClientLibrary, 'injectPhantomTraps');
49
+ });
50
+
51
+ afterEach(() => {
52
+ // Restaurer les espions après chaque test pour ne pas affecter les autres tests
53
+ vi.restoreAllMocks();
54
+ });
55
+
56
+ it('should enable all trackers by default', () => {
57
+ ClientLibrary.initializeClient();
58
+
59
+ expect(startMouseSpy).toHaveBeenCalled();
60
+ expect(startKeystrokeSpy).toHaveBeenCalled();
61
+ expect(startClickSpy).toHaveBeenCalled();
62
+ expect(startTouchSpy).toHaveBeenCalled();
63
+ expect(startMotionSpy).toHaveBeenCalled();
64
+ expect(startRenderingSpy).toHaveBeenCalled();
65
+ expect(injectPhantomTrapsSpy).toHaveBeenCalled();
66
+ });
67
+
68
+ it('should disable trackers when configured', () => {
69
+ ClientLibrary.initializeClient({ mouse: false, keystrokes: false, clicks: false, touches: false, motion: false, rendering: false });
70
+
71
+ expect(startMouseSpy).not.toHaveBeenCalled();
72
+ expect(startKeystrokeSpy).not.toHaveBeenCalled();
73
+ expect(startClickSpy).not.toHaveBeenCalled();
74
+ expect(startTouchSpy).not.toHaveBeenCalled();
75
+ expect(startMotionSpy).not.toHaveBeenCalled();
76
+ expect(startRenderingSpy).not.toHaveBeenCalled();
77
+ });
78
+
79
+ it('should initialize WASM if wasmPath is configured', () => {
80
+ ClientLibrary.initializeClient({ wasmPath: '/fp.js' });
81
+
82
+ expect(initWasmSpy).toHaveBeenCalledWith('/fp.js');
83
+ });
84
+
85
+ it('should disable phantom traps when configured', () => {
86
+ ClientLibrary.initializeClient({ phantomTraps: false });
87
+
88
+ expect(injectPhantomTrapsSpy).not.toHaveBeenCalled();
89
+ });
90
+
91
+ it('should initialize honeypots with the provided field names', () => {
92
+ const honeypotFields = ['email_confirm', 'user_nickname'];
93
+ ClientLibrary.initializeClient({ honeypots: honeypotFields });
94
+
95
+ expect(initHoneypotsSpy).toHaveBeenCalledWith(honeypotFields);
96
+ });
97
+
98
+ it('should not initialize honeypots if the array is empty', () => {
99
+ ClientLibrary.initializeClient({ honeypots: [] });
100
+
101
+ expect(initHoneypotsSpy).not.toHaveBeenCalled();
102
+ });
103
+
104
+ it('should inject trap URLs when provided', () => {
105
+ const urls = ['/trap1?sig=123', '/trap2?sig=456'];
106
+ ClientLibrary.initializeClient({ trapUrls: urls });
107
+
108
+ expect(injectTrapsSpy).toHaveBeenCalledWith(urls);
109
+ });
110
+
111
+ it('should not inject trap URLs if the array is empty', () => {
112
+ ClientLibrary.initializeClient({ trapUrls: [] });
113
+
114
+ expect(injectTrapsSpy).not.toHaveBeenCalled();
115
+ });
116
+
117
+ it('should initialize fetch interception when fetch config is present', () => {
118
+ const targetDomains = ['api.example.com'];
119
+ ClientLibrary.initializeClient({ fetch: { targetDomains } });
120
+
121
+ expect(initFetchSpy).toHaveBeenCalledWith(targetDomains);
122
+ });
123
+
124
+ it('should not initialize fetch interception if fetch config is absent', () => {
125
+ ClientLibrary.initializeClient(); // No fetch config
126
+
127
+ expect(initFetchSpy).not.toHaveBeenCalled();
128
+ });
129
+
130
+ it('should call all initializers correctly when a full config is provided', () => {
131
+ const config = {
132
+ mouse: true,
133
+ keystrokes: true,
134
+ honeypots: ['field1'],
135
+ trapUrls: ['/trap1'],
136
+ fetch: { targetDomains: ['api.com'] }
137
+ };
138
+ ClientLibrary.initializeClient(config);
139
+
140
+ expect(startMouseSpy).toHaveBeenCalled();
141
+ expect(startKeystrokeSpy).toHaveBeenCalled();
142
+ expect(initHoneypotsSpy).toHaveBeenCalledWith(config.honeypots);
143
+ expect(injectTrapsSpy).toHaveBeenCalledWith(config.trapUrls);
144
+ expect(initFetchSpy).toHaveBeenCalledWith(config.fetch.targetDomains);
145
+ });
146
+
144
147
  });
@@ -2369,20 +2369,47 @@ describe('Subnet Scoring (Node.js)', () => {
2369
2369
  // 2. Some high scores, few devices. The subnet is calculated from the context.
2370
2370
  // The key is hardcoded here to match what the implementation would store.
2371
2371
  await inMemoryStore.set('subnet:10.0.0.0/24', {
2372
- highScoreCount: 5, // score += 5 * 2 = 10
2373
- deviceIds: ['d1', 'd2'], // count < 10, score += 0
2372
+ highScoreCount: 5,
2373
+ deviceIds: ['d1', 'd2'],
2374
2374
  });
2375
2375
  ({ subnetScore } = await __internal.getSubnetScore(context, 'device-1'));
2376
- expect(subnetScore).toBe(58.6);
2376
+ expect(subnetScore).toBeGreaterThan(0);
2377
+ expect(subnetScore).toBeLessThanOrEqual(45.0); // Ambient score capped in Medium zone
2377
2378
 
2378
- // 3. Many devices and high scores
2379
+ // 3. Many devices and high scores, ambient score still capped at 45.0 (Medium asymptote)
2379
2380
  const deviceIds = Array.from({ length: 20 }, (_, i) => `d${i}`);
2380
2381
  await inMemoryStore.set('subnet:10.0.0.0/24', {
2381
- highScoreCount: 30, // score += min(40, 30 * 2) = 40
2382
- deviceIds: deviceIds, // count = 20. score += (20-10)*5 = 50
2382
+ highScoreCount: 30,
2383
+ deviceIds: deviceIds,
2383
2384
  });
2384
2385
  ({ subnetScore } = await __internal.getSubnetScore(context, 'device-1'));
2385
- expect(subnetScore).toBe(99.4);
2386
+ expect(subnetScore).toBeLessThanOrEqual(45.0);
2387
+ expect(subnetScore).toBeGreaterThanOrEqual(30.0);
2388
+
2389
+ // 4. Nearby attacker in /30 (+45 boost), escalates score into High territory
2390
+ await inMemoryStore.set('subnet:10.0.0.0/24', {
2391
+ highScoreCount: 30,
2392
+ deviceIds: deviceIds,
2393
+ attackerIps: [{ ip: '10.0.0.26', lastSeen: Date.now(), score: 85 }]
2394
+ });
2395
+ ({ subnetScore } = await __internal.getSubnetScore(context, 'device-1'));
2396
+ expect(subnetScore).toBeGreaterThanOrEqual(80.0);
2397
+
2398
+ // 5. Exact IP match (+55 boost), reaches 90-100
2399
+ await inMemoryStore.set('subnet:10.0.0.0/24', {
2400
+ highScoreCount: 30,
2401
+ deviceIds: deviceIds,
2402
+ attackerIps: [{ ip: '10.0.0.25', lastSeen: Date.now(), score: 95 }]
2403
+ });
2404
+ ({ subnetScore } = await __internal.getSubnetScore(context, 'device-1'));
2405
+ expect(subnetScore).toBeGreaterThanOrEqual(90.0);
2406
+ });
2407
+
2408
+ it('getIpCommonPrefixLength should accurately calculate common prefix bits', () => {
2409
+ const { getIpCommonPrefixLength } = __internal;
2410
+ expect(getIpCommonPrefixLength('10.0.0.25', '10.0.0.25')).toBe(32);
2411
+ expect(getIpCommonPrefixLength('10.0.0.24', '10.0.0.25')).toBe(31);
2412
+ expect(getIpCommonPrefixLength('192.168.1.10', '192.168.2.10')).toBe(22);
2386
2413
  });
2387
2414
  });
2388
2415
 
@@ -138,5 +138,6 @@ describe('IP Reputation Local System (Node.js)', () => {
138
138
  }
139
139
  score = await __internal.getSubnetScore(mockContext);
140
140
  expect(score.subnetScore).toBeGreaterThan(0);
141
+ expect(score.subnetScore).toBeLessThanOrEqual(45.0); // Ambient cap
141
142
  });
142
143
  });
@@ -1,8 +1,8 @@
1
1
  import crypto from "node:crypto";
2
2
 
3
3
  /**
4
- * Classe de base abstraite pour définir des tâches uPoW basées sur l'entraînement
5
- * ou l'évaluation de modèles IA (PyTorch exportés au format ONNX).
4
+ * Abstract base class defining useful Proof-of-Work (uPoW) tasks based on ML training
5
+ * or inference validation (e.g. PyTorch models exported to ONNX format).
6
6
  */
7
7
  export class UpowModelTask {
8
8
  constructor(problemId, modelPath, config = {}) {
@@ -20,7 +20,7 @@ export class UpowModelTask {
20
20
  }
21
21
 
22
22
  /**
23
- * Récupère l'identifiant du problème.
23
+ * Returns the unique problem identifier.
24
24
  * @returns {string}
25
25
  */
26
26
  getProblemId() {
@@ -28,7 +28,7 @@ export class UpowModelTask {
28
28
  }
29
29
 
30
30
  /**
31
- * Récupère le chemin réseau du modèle ONNX.
31
+ * Returns the network asset path of the model.
32
32
  * @returns {string}
33
33
  */
34
34
  getModelAssetPath() {
@@ -36,18 +36,18 @@ export class UpowModelTask {
36
36
  }
37
37
 
38
38
  /**
39
- * Génère la charge utile de travail pour le client.
40
- * Envoie l'état actuel des poids du modèle et un lot de données (mini-batch) à traiter.
39
+ * Dispatches task workload to the client.
40
+ * Transmits current model weights and mini-batch sample data.
41
41
  * @param {number} suspicionFactor
42
- * @returns {object} Données à envoyer au client
42
+ * @returns {object} Task payload dispatched to client
43
43
  */
44
44
  dispatchTask(suspicionFactor) {
45
45
  throw new Error("Method 'dispatchTask(suspicionFactor)' must be implemented.");
46
46
  }
47
47
 
48
48
  /**
49
- * Vérifie la validité de la solution retournée par le client.
50
- * Implémente des gardes-fous contre l'empoisonnement (ex: vérification des gradients).
49
+ * Validates solution returned by the client.
50
+ * Implements anti-poisoning guards and gradient bounds checks.
51
51
  * @param {object} taskContext
52
52
  * @param {object} solution
53
53
  * @returns {boolean}
@@ -57,8 +57,7 @@ export class UpowModelTask {
57
57
  }
58
58
 
59
59
  /**
60
- * Intègre les gradients ou résultats validés dans le modèle global.
61
- * Par exemple : algorithme FedAvg (Federated Averaging).
60
+ * Integrates validated gradients into global model weights (e.g. FedAvg).
62
61
  * @param {object} solution
63
62
  */
64
63
  integrateSolution(solution) {
@@ -67,17 +66,17 @@ export class UpowModelTask {
67
66
  }
68
67
 
69
68
  /**
70
- * Exemple d'implémentation d'une tâche d'entraînement (Classifieur de requêtes suspectes)
69
+ * Example training task implementation (suspicious request classifier).
71
70
  */
72
71
  export class RequestClassifierTask extends UpowModelTask {
73
72
  constructor(modelPath, config) {
74
73
  super("request_classifier_nn", modelPath, config);
75
- // Poids initiaux du modèle (Réseau de neurones linéaire simple)
74
+ // Initial model weights (linear neural network)
76
75
  this.weights = [0.1, -0.2, 0.8, 0.5];
77
76
  }
78
77
 
79
78
  dispatchTask(suspicionFactor) {
80
- // Génération de données d'entraînement anonymisées pour le client
79
+ // Anonymized sample training batch dispatched to client
81
80
  const inputs = [
82
81
  [1.0, 0.5, 0.0, 1.2],
83
82
  [0.0, 1.0, -0.5, 0.8]
@@ -95,7 +94,7 @@ export class RequestClassifierTask extends UpowModelTask {
95
94
  if (!Array.isArray(gradients) || gradients.length !== this.weights.length) {
96
95
  return false;
97
96
  }
98
- // Anti-poisoning : s'assurer que les gradients ne contiennent pas de valeurs aberrantes (clipping)
97
+ // Anti-poisoning guard: ensure gradients do not exceed threshold norms (gradient clipping)
99
98
  const maxGradientNorm = 10.0;
100
99
  for (const grad of gradients) {
101
100
  if (typeof grad !== 'number' || isNaN(grad) || Math.abs(grad) > maxGradientNorm) {