@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,157 +1,157 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Tests;
6
-
7
- use Anonympins\Fingerprint\RequestContext;
8
- use Anonympins\Fingerprint\Store\IStore;
9
- use Anonympins\Fingerprint\Store\StoreManager;
10
- use Anonympins\Fingerprint\Utils\RequestUtils;
11
- use PHPUnit\Framework\TestCase;
12
-
13
- class IpReputationTest extends TestCase
14
- {
15
- private $store;
16
-
17
- protected function setUp(): void
18
- {
19
- parent::setUp();
20
-
21
- // Un mock anonyme simple et en mémoire du Store pour isoler les tests
22
- $this->store = new class implements IStore {
23
- private array $data = [];
24
- public function get(string $key) { return $this->data[$key] ?? null; }
25
- public function set(string $key, $value, ?int $ttl = null): void { $this->data[$key] = $value; }
26
- public function has(string $key): bool { return isset($this->data[$key]); }
27
- public function delete(string $key): void { unset($this->data[$key]); }
28
- };
29
-
30
- StoreManager::setStore($this->store);
31
- }
32
-
33
- public function testGetScoreReturnsZeroForUnknownIp(): void
34
- {
35
- $score = RequestUtils::getIpReputationScore('8.8.8.8');
36
- $this->assertEquals(0.0, $score);
37
- }
38
-
39
- public function testUpdateScoreChangesValueCorrectly(): void
40
- {
41
- $ip = '8.8.4.4';
42
- RequestUtils::updateIpReputationScore($ip, 45.0);
43
- $this->assertEquals(45.0, RequestUtils::getIpReputationScore($ip));
44
-
45
- RequestUtils::updateIpReputationScore($ip, -15.0);
46
- $this->assertEquals(30.0, RequestUtils::getIpReputationScore($ip));
47
- }
48
-
49
- public function testBoundsAreClampedBetween0And100(): void
50
- {
51
- $ip = '1.1.1.1';
52
- RequestUtils::updateIpReputationScore($ip, 120.0);
53
- $this->assertEquals(100.0, RequestUtils::getIpReputationScore($ip));
54
-
55
- RequestUtils::updateIpReputationScore($ip, -150.0);
56
- $this->assertEquals(0.0, RequestUtils::getIpReputationScore($ip));
57
- }
58
-
59
- public function testTimeDecayCalculatesLossOfTwoPointsPerHour(): void
60
- {
61
- $ip = '2.2.2.2';
62
- $now = time();
63
-
64
- // Simulation d'un score de 80 vieux de 4 heures (4 * 2 = 8 points de perte)
65
- $this->store->set("ip-reputation:{$ip}", [
66
- 'score' => 80.0,
67
- 'lastUpdate' => $now - 14400
68
- ]);
69
-
70
- $score = RequestUtils::getIpReputationScore($ip);
71
- $this->assertEquals(72.0, $score);
72
- }
73
-
74
- public function testIpReputationScoreIsIntegratedIntoFinalScore(): void
75
- {
76
- $ip = '1.2.3.4';
77
- RequestUtils::updateIpReputationScore($ip, 60.0);
78
-
79
- $weights = [
80
- 'ipReputationScore' => 0.5,
81
- 'historyScore' => 0.0,
82
- 'rotationScore' => 0.0,
83
- 'headerAnomalyScore' => 0.0,
84
- 'requestPatternScore' => 0.0,
85
- 'inconsistencyScore' => 0.0,
86
- 'honeypotScore' => 0.0,
87
- 'behaviorScore' => 0.0,
88
- 'botScore' => 0.0,
89
- 'crossLayerInconsistencyScore' => 0.0,
90
- 'tlsSpoofingScore' => 0.0,
91
- 'timeInconsistencyScore' => 0.0,
92
- 'clickVarianceScore' => 0.0,
93
- 'clientHintsInconsistencyScore' => 0.0,
94
- 'subnetScore' => 0.0,
95
- ];
96
-
97
- $ipRepScore = RequestUtils::getIpReputationScore($ip);
98
- $this->assertEquals(60.0, $ipRepScore);
99
- $score = $ipRepScore * $weights['ipReputationScore'];
100
- $this->assertEquals(30.0, $score);
101
- }
102
-
103
- public function testGetIpSubnetIpv4AndIpv6(): void
104
- {
105
- $ipv4Subnet = RequestUtils::getIpSubnet('192.168.1.50', 24, 48);
106
- $this->assertEquals('192.168.1.0/24', $ipv4Subnet);
107
-
108
- $ipv6Subnet = RequestUtils::getIpSubnet('2001:db8:abcd:0012::1', 24, 48);
109
- $this->assertEquals('2001:db8:abcd::/48', $ipv6Subnet);
110
- }
111
-
112
- public function testGetClientHintsInconsistencyScoreMismatch(): void
113
- {
114
- $context = $this->createMock(RequestContext::class);
115
- $context->method('getHeader')->willReturnCallback(function($name) {
116
- if ($name === 'user-agent') {
117
- return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/117.0';
118
- }
119
- if ($name === 'sec-ch-ua') {
120
- return '"Google Chrome";v="117"';
121
- }
122
- return null;
123
- });
124
-
125
- $score = RequestUtils::getClientHintsInconsistencyScore($context);
126
- $this->assertEquals(90.0, $score['clientHintsInconsistencyScore']);
127
- }
128
-
129
- public function testGetSubnetScoreCalculations(): void
130
- {
131
- $context = $this->createMock(RequestContext::class);
132
- $context->clientIp = '192.168.1.50';
133
-
134
- $score = RequestUtils::getSubnetScore($context, 'device-1');
135
- $this->assertEquals(0.0, $score['subnetScore']);
136
-
137
- for ($i = 1; $i <= 12; $i++) {
138
- RequestUtils::updateSubnetMetrics($context, "device-{$i}", 30.0);
139
- }
140
-
141
- $scoreWithHistory = RequestUtils::getSubnetScore($context, 'device-1');
142
- $this->assertGreaterThan(0.0, $scoreWithHistory['subnetScore']);
143
- }
144
-
145
- public function testTlsSpoofingScoreWithJa4(): void
146
- {
147
- $context = $this->createMock(RequestContext::class);
148
- $context->ja4 = 't13d1517h2_8daaf61527d5';
149
- $context->ja3 = null;
150
- $context->method('getHeader')->willReturnCallback(function($name) {
151
- return $name === 'user-agent' ? 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Firefox/117.0' : null;
152
- });
153
-
154
- $score = RequestUtils::getTlsSpoofingScore($context);
155
- $this->assertEquals(90.0, $score['tlsSpoofingScore']);
156
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Tests;
6
+
7
+ use Anonympins\Fingerprint\RequestContext;
8
+ use Anonympins\Fingerprint\Store\IStore;
9
+ use Anonympins\Fingerprint\Store\StoreManager;
10
+ use Anonympins\Fingerprint\Utils\RequestUtils;
11
+ use PHPUnit\Framework\TestCase;
12
+
13
+ class IpReputationTest extends TestCase
14
+ {
15
+ private $store;
16
+
17
+ protected function setUp(): void
18
+ {
19
+ parent::setUp();
20
+
21
+ // Un mock anonyme simple et en mémoire du Store pour isoler les tests
22
+ $this->store = new class implements IStore {
23
+ private array $data = [];
24
+ public function get(string $key) { return $this->data[$key] ?? null; }
25
+ public function set(string $key, $value, ?int $ttl = null): void { $this->data[$key] = $value; }
26
+ public function has(string $key): bool { return isset($this->data[$key]); }
27
+ public function delete(string $key): void { unset($this->data[$key]); }
28
+ };
29
+
30
+ StoreManager::setStore($this->store);
31
+ }
32
+
33
+ public function testGetScoreReturnsZeroForUnknownIp(): void
34
+ {
35
+ $score = RequestUtils::getIpReputationScore('8.8.8.8');
36
+ $this->assertEquals(0.0, $score);
37
+ }
38
+
39
+ public function testUpdateScoreChangesValueCorrectly(): void
40
+ {
41
+ $ip = '8.8.4.4';
42
+ RequestUtils::updateIpReputationScore($ip, 45.0);
43
+ $this->assertEquals(45.0, RequestUtils::getIpReputationScore($ip));
44
+
45
+ RequestUtils::updateIpReputationScore($ip, -15.0);
46
+ $this->assertEquals(30.0, RequestUtils::getIpReputationScore($ip));
47
+ }
48
+
49
+ public function testBoundsAreClampedBetween0And100(): void
50
+ {
51
+ $ip = '1.1.1.1';
52
+ RequestUtils::updateIpReputationScore($ip, 120.0);
53
+ $this->assertEquals(100.0, RequestUtils::getIpReputationScore($ip));
54
+
55
+ RequestUtils::updateIpReputationScore($ip, -150.0);
56
+ $this->assertEquals(0.0, RequestUtils::getIpReputationScore($ip));
57
+ }
58
+
59
+ public function testTimeDecayCalculatesLossOfTwoPointsPerHour(): void
60
+ {
61
+ $ip = '2.2.2.2';
62
+ $now = time();
63
+
64
+ // Simulation d'un score de 80 vieux de 4 heures (4 * 2 = 8 points de perte)
65
+ $this->store->set("ip-reputation:{$ip}", [
66
+ 'score' => 80.0,
67
+ 'lastUpdate' => $now - 14400
68
+ ]);
69
+
70
+ $score = RequestUtils::getIpReputationScore($ip);
71
+ $this->assertEquals(72.0, $score);
72
+ }
73
+
74
+ public function testIpReputationScoreIsIntegratedIntoFinalScore(): void
75
+ {
76
+ $ip = '1.2.3.4';
77
+ RequestUtils::updateIpReputationScore($ip, 60.0);
78
+
79
+ $weights = [
80
+ 'ipReputationScore' => 0.5,
81
+ 'historyScore' => 0.0,
82
+ 'rotationScore' => 0.0,
83
+ 'headerAnomalyScore' => 0.0,
84
+ 'requestPatternScore' => 0.0,
85
+ 'inconsistencyScore' => 0.0,
86
+ 'honeypotScore' => 0.0,
87
+ 'behaviorScore' => 0.0,
88
+ 'botScore' => 0.0,
89
+ 'crossLayerInconsistencyScore' => 0.0,
90
+ 'tlsSpoofingScore' => 0.0,
91
+ 'timeInconsistencyScore' => 0.0,
92
+ 'clickVarianceScore' => 0.0,
93
+ 'clientHintsInconsistencyScore' => 0.0,
94
+ 'subnetScore' => 0.0,
95
+ ];
96
+
97
+ $ipRepScore = RequestUtils::getIpReputationScore($ip);
98
+ $this->assertEquals(60.0, $ipRepScore);
99
+ $score = $ipRepScore * $weights['ipReputationScore'];
100
+ $this->assertEquals(30.0, $score);
101
+ }
102
+
103
+ public function testGetIpSubnetIpv4AndIpv6(): void
104
+ {
105
+ $ipv4Subnet = RequestUtils::getIpSubnet('192.168.1.50', 24, 48);
106
+ $this->assertEquals('192.168.1.0/24', $ipv4Subnet);
107
+
108
+ $ipv6Subnet = RequestUtils::getIpSubnet('2001:db8:abcd:0012::1', 24, 48);
109
+ $this->assertEquals('2001:db8:abcd::/48', $ipv6Subnet);
110
+ }
111
+
112
+ public function testGetClientHintsInconsistencyScoreMismatch(): void
113
+ {
114
+ $context = $this->createMock(RequestContext::class);
115
+ $context->method('getHeader')->willReturnCallback(function($name) {
116
+ if ($name === 'user-agent') {
117
+ return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/117.0';
118
+ }
119
+ if ($name === 'sec-ch-ua') {
120
+ return '"Google Chrome";v="117"';
121
+ }
122
+ return null;
123
+ });
124
+
125
+ $score = RequestUtils::getClientHintsInconsistencyScore($context);
126
+ $this->assertEquals(90.0, $score['clientHintsInconsistencyScore']);
127
+ }
128
+
129
+ public function testGetSubnetScoreCalculations(): void
130
+ {
131
+ $context = $this->createMock(RequestContext::class);
132
+ $context->clientIp = '192.168.1.50';
133
+
134
+ $score = RequestUtils::getSubnetScore($context, 'device-1');
135
+ $this->assertEquals(0.0, $score['subnetScore']);
136
+
137
+ for ($i = 1; $i <= 12; $i++) {
138
+ RequestUtils::updateSubnetMetrics($context, "device-{$i}", 30.0);
139
+ }
140
+
141
+ $scoreWithHistory = RequestUtils::getSubnetScore($context, 'device-1');
142
+ $this->assertGreaterThan(0.0, $scoreWithHistory['subnetScore']);
143
+ }
144
+
145
+ public function testTlsSpoofingScoreWithJa4(): void
146
+ {
147
+ $context = $this->createMock(RequestContext::class);
148
+ $context->ja4 = 't13d1517h2_8daaf61527d5';
149
+ $context->ja3 = null;
150
+ $context->method('getHeader')->willReturnCallback(function($name) {
151
+ return $name === 'user-agent' ? 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Firefox/117.0' : null;
152
+ });
153
+
154
+ $score = RequestUtils::getTlsSpoofingScore($context);
155
+ $this->assertEquals(90.0, $score['tlsSpoofingScore']);
156
+ }
157
157
  }