@anonympins/fingerprint 0.7.0 → 0.7.2
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.
- package/CHANGELOG.md +29 -0
- package/README.md +1 -1
- package/composer.json +8 -1
- package/package.json +1 -1
- package/public/fingerprint-wordpress.zip +0 -0
- package/src/js/build-client.js +8 -10
- package/src/js/fingerprint.builder.js +37 -38
- package/src/js/fingerprint.client.js +1531 -1463
- package/src/js/fingerprint.client.obfuscated.js +1 -0
- package/src/js/fingerprint.js +190 -16
- package/src/js/fingerprint.utils.js +213 -213
- package/src/js/library.js +340 -376
- package/src/js/mongodb-store.js +1 -1
- package/src/js/optimization.worker.js +27 -27
- package/src/js/pow.solver.inline.js +154 -56
- package/src/js/pow.solver.js +180 -92
- package/src/js/pow.worker.js +48 -48
- package/src/js/redis-store.js +1 -1
- package/src/js/tests/fingerprint.client.init.test.js +146 -143
- package/src/js/tests/fingerprint.test.js +124 -10
- package/src/js/tests/ip-reputation.test.js +1 -0
- package/src/js/upow-model-task.js +14 -15
- package/src/php/AutoTuner.php +34 -34
- package/src/php/Challenge/ChallengeUtils.php +57 -11
- package/src/php/Config/SecurityProfiles.php +20 -0
- package/src/php/DirectFingerprint.php +190 -145
- package/src/php/FingerprintBuilder.php +29 -30
- package/src/php/FingerprintClient.php +147 -148
- package/src/php/FingerprintEngine.php +44 -17
- package/src/php/Ja3AnomalyDetector.php +33 -32
- package/src/php/Optimization/FunctionRegistry.php +7 -7
- package/src/php/Optimization/Optimization.php +24 -24
- package/src/php/Optimization/OptimizationOperators.php +26 -25
- package/src/php/Optimization/ProblemInitializers.php +52 -52
- package/src/php/ProblemManager.php +31 -34
- package/src/php/RequestContext.php +8 -1
- package/src/php/Store/IStore.php +7 -8
- package/src/php/Store/InMemoryStore.php +66 -66
- package/src/php/Store/MongoDbStore.php +5 -5
- package/src/php/Store/RedisStore.php +5 -5
- package/src/php/Store/StoreManager.php +35 -35
- package/src/php/Tests/ChallengeUtilsTest.php +453 -453
- package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
- package/src/php/Tests/FingerprintEngineTest.php +38 -0
- package/src/php/Tests/IpReputationTest.php +10 -0
- package/src/php/Tests/MaliciousPatternsTest.php +103 -103
- package/src/php/Tests/MetricsTest.php +97 -97
- package/src/php/Tests/ProblemManagerTest.php +376 -376
- package/src/php/Tests/QuicFingerprintTest.php +53 -53
- package/src/php/Tests/RequestUtilsTest.php +471 -471
- package/src/php/Tests/TLSClientHelloParserTest.php +177 -177
- package/src/php/Tests/config/ed25519_key.json +3 -3
- package/src/php/Utils/BlockList.php +99 -99
- package/src/php/Utils/Env.php +49 -49
- package/src/php/Utils/MaliciousPatterns.php +74 -74
- package/src/php/Utils/MetricsManager.php +209 -209
- package/src/php/Utils/RequestUtils.php +129 -7
- package/src/php/Utils/TLSClientHelloParser.php +369 -369
- package/src/php/WordPress/WpDbStore.php +155 -0
- package/src/php/WordPress/fingerprint-wordpress.php +554 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +262 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +268 -0
- package/src/php/WordPress/package.php +218 -0
- package/src/php/bin/auto-tune.php +118 -118
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Script d'empaquetage automatique du plugin WordPress.
|
|
7
|
+
* Copie les classes du moteur PHP, les assets JS et génère un ZIP prêt à être installé.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* php src/php/WordPress/package.php
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
if (!extension_loaded('zip')) {
|
|
14
|
+
fwrite(STDERR, "Erreur : L'extension PHP 'zip' est requise pour créer l'archive.\n");
|
|
15
|
+
exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
$rootDir = dirname(__DIR__, 3);
|
|
19
|
+
$phpSrcDir = $rootDir . '/src/php';
|
|
20
|
+
$jsSrcDir = $rootDir . '/src/js';
|
|
21
|
+
$wpDir = $phpSrcDir . '/WordPress';
|
|
22
|
+
$distDir = $rootDir . '/public';
|
|
23
|
+
$pluginSlug = 'fingerprint-wordpress';
|
|
24
|
+
$buildDir = $distDir . '/' . $pluginSlug;
|
|
25
|
+
$zipFile = $distDir . '/' . $pluginSlug . '.zip';
|
|
26
|
+
|
|
27
|
+
echo "==> Début du packaging du plugin WordPress...\n";
|
|
28
|
+
|
|
29
|
+
// 1. Nettoyage du répertoire de build précédent
|
|
30
|
+
if (is_dir($buildDir)) {
|
|
31
|
+
$iterator = new RecursiveIteratorIterator(
|
|
32
|
+
new RecursiveDirectoryIterator($buildDir, FilesystemIterator::SKIP_DOTS),
|
|
33
|
+
RecursiveIteratorIterator::CHILD_FIRST
|
|
34
|
+
);
|
|
35
|
+
foreach ($iterator as $item) {
|
|
36
|
+
$item->isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname());
|
|
37
|
+
}
|
|
38
|
+
rmdir($buildDir);
|
|
39
|
+
}
|
|
40
|
+
if (file_exists($zipFile)) {
|
|
41
|
+
unlink($zipFile);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@mkdir($buildDir . '/src', 0777, true);
|
|
45
|
+
@mkdir($buildDir . '/assets', 0777, true);
|
|
46
|
+
@mkdir($buildDir . '/languages', 0777, true);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compile un fichier .po gettext en binaire .mo compatible WordPress.
|
|
50
|
+
*/
|
|
51
|
+
function compilePoToMo(string $poFile, string $moFile): bool {
|
|
52
|
+
if (!file_exists($poFile)) return false;
|
|
53
|
+
$content = file_get_contents($poFile);
|
|
54
|
+
if ($content === false) return false;
|
|
55
|
+
|
|
56
|
+
$entries = [];
|
|
57
|
+
$currentMsgId = null;
|
|
58
|
+
$currentMsgStr = null;
|
|
59
|
+
$lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $content));
|
|
60
|
+
$state = '';
|
|
61
|
+
|
|
62
|
+
foreach ($lines as $line) {
|
|
63
|
+
$line = trim($line);
|
|
64
|
+
if ($line === '' || str_starts_with($line, '#')) continue;
|
|
65
|
+
if (preg_match('/^msgid\s+"(.*)"$/', $line, $m)) {
|
|
66
|
+
if ($currentMsgId !== null && $currentMsgStr !== null) {
|
|
67
|
+
$entries[$currentMsgId] = $currentMsgStr;
|
|
68
|
+
}
|
|
69
|
+
$currentMsgId = stripcslashes($m[1]);
|
|
70
|
+
$currentMsgStr = null;
|
|
71
|
+
$state = 'msgid';
|
|
72
|
+
} elseif (preg_match('/^msgstr\s+"(.*)"$/', $line, $m)) {
|
|
73
|
+
$currentMsgStr = stripcslashes($m[1]);
|
|
74
|
+
$state = 'msgstr';
|
|
75
|
+
} elseif (preg_match('/^"(.*)"$/', $line, $m)) {
|
|
76
|
+
if ($state === 'msgid') {
|
|
77
|
+
$currentMsgId .= stripcslashes($m[1]);
|
|
78
|
+
} elseif ($state === 'msgstr') {
|
|
79
|
+
$currentMsgStr .= stripcslashes($m[1]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if ($currentMsgId !== null && $currentMsgStr !== null) {
|
|
84
|
+
$entries[$currentMsgId] = $currentMsgStr;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ksort($entries);
|
|
88
|
+
$count = count($entries);
|
|
89
|
+
$originals = '';
|
|
90
|
+
$translations = '';
|
|
91
|
+
$origTable = [];
|
|
92
|
+
$transTable = [];
|
|
93
|
+
|
|
94
|
+
$headerSize = 28;
|
|
95
|
+
$tablesSize = $count * 8 * 2;
|
|
96
|
+
$curOffset = $headerSize + $tablesSize;
|
|
97
|
+
|
|
98
|
+
foreach ($entries as $orig => $trans) {
|
|
99
|
+
$origLen = strlen($orig);
|
|
100
|
+
$origTable[] = ['len' => $origLen, 'off' => $curOffset + strlen($originals)];
|
|
101
|
+
$originals .= $orig . "\0";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
$transOffset = $curOffset + strlen($originals);
|
|
105
|
+
foreach ($entries as $orig => $trans) {
|
|
106
|
+
$transLen = strlen($trans);
|
|
107
|
+
$transTable[] = ['len' => $transLen, 'off' => $transOffset + strlen($translations)];
|
|
108
|
+
$translations .= $trans . "\0";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
$mo = pack('V*',
|
|
112
|
+
0x950412de, // Magic Number
|
|
113
|
+
0, // Revision
|
|
114
|
+
$count, // Number of strings
|
|
115
|
+
28, // Offset of table with original string lengths and offsets
|
|
116
|
+
28 + ($count * 8), // Offset of table with translation string lengths and offsets
|
|
117
|
+
0, 0 // Hash table size and offset
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
foreach ($origTable as $t) {
|
|
121
|
+
$mo .= pack('VV', $t['len'], $t['off']);
|
|
122
|
+
}
|
|
123
|
+
foreach ($transTable as $t) {
|
|
124
|
+
$mo .= pack('VV', $t['len'], $t['off']);
|
|
125
|
+
}
|
|
126
|
+
$mo .= $originals . $translations;
|
|
127
|
+
return file_put_contents($moFile, $mo) !== false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 2. Copie des fichiers principaux du plugin WordPress
|
|
131
|
+
copy($wpDir . '/fingerprint-wordpress.php', $buildDir . '/fingerprint-wordpress.php');
|
|
132
|
+
copy($wpDir . '/WpDbStore.php', $buildDir . '/WpDbStore.php');
|
|
133
|
+
|
|
134
|
+
// 3. Copie des assets clients nécessaires (solveur PoW)
|
|
135
|
+
$solverSource = $jsSrcDir . '/pow.solver.inline.js';
|
|
136
|
+
if (file_exists($solverSource)) {
|
|
137
|
+
copy($solverSource, $buildDir . '/assets/pow.solver.inline.js');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 4. Copie et compilation des fichiers de traduction i18n (FR / DE / EN)
|
|
141
|
+
$langSourceDir = $wpDir . '/languages';
|
|
142
|
+
if (is_dir($langSourceDir)) {
|
|
143
|
+
foreach (glob($langSourceDir . '/*.po') as $poFile) {
|
|
144
|
+
$baseName = basename($poFile);
|
|
145
|
+
copy($poFile, $buildDir . '/languages/' . $baseName);
|
|
146
|
+
$moTarget = $buildDir . '/languages/' . preg_replace('/\.po$/', '.mo', $baseName);
|
|
147
|
+
compilePoToMo($poFile, $moTarget);
|
|
148
|
+
compilePoToMo($poFile, $langSourceDir . '/' . preg_replace('/\.po$/', '.mo', $baseName));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 5. Copie récursive de la bibliothèque PHP (src/php -> build/src), en excluant les dossiers de dev
|
|
153
|
+
$excludeDirs = ['WordPress', 'Tests', 'bin'];
|
|
154
|
+
$phpIterator = new RecursiveIteratorIterator(
|
|
155
|
+
new RecursiveDirectoryIterator($phpSrcDir, FilesystemIterator::SKIP_DOTS),
|
|
156
|
+
RecursiveIteratorIterator::SELF_FIRST
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
foreach ($phpIterator as $item) {
|
|
160
|
+
$relativePath = substr($item->getPathname(), strlen($phpSrcDir) + 1);
|
|
161
|
+
$topDir = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $relativePath))[0];
|
|
162
|
+
|
|
163
|
+
if (in_array($topDir, $excludeDirs, true)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
$destPath = $buildDir . '/src/' . $relativePath;
|
|
168
|
+
if ($item->isDir()) {
|
|
169
|
+
if (!is_dir($destPath)) {
|
|
170
|
+
mkdir($destPath, 0777, true);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
copy($item->getPathname(), $destPath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
echo "==> Fichiers copiés dans {$buildDir}\n";
|
|
178
|
+
|
|
179
|
+
// 5. Création de l'archive ZIP
|
|
180
|
+
$zip = new ZipArchive();
|
|
181
|
+
if ($zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
|
|
182
|
+
fwrite(STDERR, "Erreur : Impossible de créer le fichier {$zipFile}\n");
|
|
183
|
+
exit(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
$distIterator = new RecursiveIteratorIterator(
|
|
187
|
+
new RecursiveDirectoryIterator($buildDir, FilesystemIterator::SKIP_DOTS),
|
|
188
|
+
RecursiveIteratorIterator::LEAVES_ONLY
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
foreach ($distIterator as $file) {
|
|
192
|
+
if (!$file->isDir()) {
|
|
193
|
+
$filePath = $file->getPathname();
|
|
194
|
+
// Préserve le préfixe du dossier racine dans le ZIP : fingerprint-wordpress/...
|
|
195
|
+
$relativePath = $pluginSlug . '/' . substr($filePath, strlen($buildDir) + 1);
|
|
196
|
+
$zip->addFile($filePath, str_replace('\\', '/', $relativePath));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function removeDirectory(string $dir): void
|
|
200
|
+
{
|
|
201
|
+
if (!is_dir($dir)) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
$iterator = new RecursiveIteratorIterator(
|
|
205
|
+
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
|
|
206
|
+
RecursiveIteratorIterator::CHILD_FIRST
|
|
207
|
+
);
|
|
208
|
+
foreach ($iterator as $item) {
|
|
209
|
+
$item->isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname());
|
|
210
|
+
}
|
|
211
|
+
rmdir($dir);
|
|
212
|
+
}
|
|
213
|
+
$zip->close();
|
|
214
|
+
removeDirectory($buildDir);
|
|
215
|
+
$sizeKb = round(filesize($zipFile) / 1024, 2);
|
|
216
|
+
echo "==> Archive ZIP générée avec succès :\n";
|
|
217
|
+
echo " Fichier : {$zipFile} ({$sizeKb} Ko)\n";
|
|
218
|
+
echo " Prêt à être installé via wp-admin ou déployé en production !\n";
|
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
declare(strict_types=1);
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
$autoloaderPaths = [
|
|
12
|
-
__DIR__ . '/../../vendor/autoload.php',
|
|
13
|
-
__DIR__ . '/../vendor/autoload.php',
|
|
14
|
-
__DIR__ . '/../../../autoload.php',
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
$autoloaded = false;
|
|
18
|
-
foreach ($autoloaderPaths as $path) {
|
|
19
|
-
if (file_exists($path)) {
|
|
20
|
-
require_once $path;
|
|
21
|
-
$autoloaded = true;
|
|
22
|
-
break;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!$autoloaded) {
|
|
27
|
-
fwrite(STDERR, "
|
|
28
|
-
exit(1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
use Anonympins\Fingerprint\Optimization\Optimization;
|
|
32
|
-
use Anonympins\Fingerprint\Store\StoreManager;
|
|
33
|
-
use Anonympins\Fingerprint\Utils\RequestUtils;
|
|
34
|
-
|
|
35
|
-
// 1. Récupération des arguments CLI
|
|
36
|
-
$configPath = $argv[1] ?? null;
|
|
37
|
-
if (!$configPath) {
|
|
38
|
-
echo "Usage: php auto-tune.php [
|
|
39
|
-
exit(1);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!file_exists($configPath)) {
|
|
43
|
-
fwrite(STDERR, "
|
|
44
|
-
exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
$config = json_decode(file_get_contents($configPath), true);
|
|
48
|
-
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
49
|
-
fwrite(STDERR, "
|
|
50
|
-
exit(1);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 2.
|
|
54
|
-
$store = StoreManager::getStore();
|
|
55
|
-
if (!$store) {
|
|
56
|
-
fwrite(STDERR, "
|
|
57
|
-
exit(1);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
$rawTrafficLogs = $store->get('traffic_logs') ?? [];
|
|
61
|
-
if (empty($rawTrafficLogs)) {
|
|
62
|
-
echo "[AutoTuning]
|
|
63
|
-
exit(0);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// 3.
|
|
67
|
-
$sanitizedLogs = RequestUtils::sanitizeTrafficData($rawTrafficLogs);
|
|
68
|
-
|
|
69
|
-
$minDataPoints = $config['autotuning']['minDataPoints'] ?? 200;
|
|
70
|
-
if (count($sanitizedLogs) < $minDataPoints) {
|
|
71
|
-
echo "[AutoTuning]
|
|
72
|
-
exit(0);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
echo "[AutoTuning]
|
|
76
|
-
|
|
77
|
-
// 4.
|
|
78
|
-
$paretoFront = Optimization::solveFullSecurityTuning(['trafficData' => $sanitizedLogs]);
|
|
79
|
-
if (empty($paretoFront)) {
|
|
80
|
-
fwrite(STDERR, "[AutoTuning]
|
|
81
|
-
exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// 5.
|
|
85
|
-
$bestSolution = $paretoFront[0];
|
|
86
|
-
$minDistance = sqrt(pow($bestSolution['objectives'][0], 2) + pow($bestSolution['objectives'][1], 2));
|
|
87
|
-
foreach ($paretoFront as $candidate) {
|
|
88
|
-
$distance = sqrt(pow($candidate['objectives'][0], 2) + pow($candidate['objectives'][1], 2));
|
|
89
|
-
if ($distance < $minDistance) {
|
|
90
|
-
$minDistance = $distance;
|
|
91
|
-
$bestSolution = $candidate;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
$newConfig = $bestSolution['solution'];
|
|
96
|
-
$maxChangeVelocity = 0.15; //
|
|
97
|
-
|
|
98
|
-
$applyInertialUpdate = function (array &$current, array $target) use ($maxChangeVelocity) {
|
|
99
|
-
$sumCurrent = array_sum($current);
|
|
100
|
-
if ($sumCurrent === 0) return;
|
|
101
|
-
$sumTarget = 0;
|
|
102
|
-
foreach ($current as $k => $v) {
|
|
103
|
-
if (isset($target[$k])) $sumTarget += $target[$k];
|
|
104
|
-
}
|
|
105
|
-
$ratio = ($sumTarget - $sumCurrent) / $sumCurrent;
|
|
106
|
-
$factor = 1 + max(-$maxChangeVelocity, min($maxChangeVelocity, $ratio));
|
|
107
|
-
foreach ($current as $k => &$v) {
|
|
108
|
-
if (isset($target[$k])) $v *= $factor;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
$applyInertialUpdate($config['thresholds'], $newConfig['thresholds']);
|
|
113
|
-
$applyInertialUpdate($config['weights'], $newConfig['weights']);
|
|
114
|
-
$applyInertialUpdate($config['patterns'], $newConfig['patterns']);
|
|
115
|
-
|
|
116
|
-
// 6.
|
|
117
|
-
file_put_contents($configPath, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
118
|
-
echo "[AutoTuning]
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Periodic security configuration auto-tuning CLI script.
|
|
7
|
+
* Reads current configuration, prunes and sanitizes traffic logs,
|
|
8
|
+
* executes the genetic optimizer, and writes back the optimized profile.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
$autoloaderPaths = [
|
|
12
|
+
__DIR__ . '/../../vendor/autoload.php',
|
|
13
|
+
__DIR__ . '/../vendor/autoload.php',
|
|
14
|
+
__DIR__ . '/../../../autoload.php',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
$autoloaded = false;
|
|
18
|
+
foreach ($autoloaderPaths as $path) {
|
|
19
|
+
if (file_exists($path)) {
|
|
20
|
+
require_once $path;
|
|
21
|
+
$autoloaded = true;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!$autoloaded) {
|
|
27
|
+
fwrite(STDERR, "Error: Unable to load autoloader. Run 'composer install'.\n");
|
|
28
|
+
exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
use Anonympins\Fingerprint\Optimization\Optimization;
|
|
32
|
+
use Anonympins\Fingerprint\Store\StoreManager;
|
|
33
|
+
use Anonympins\Fingerprint\Utils\RequestUtils;
|
|
34
|
+
|
|
35
|
+
// 1. Récupération des arguments CLI
|
|
36
|
+
$configPath = $argv[1] ?? null;
|
|
37
|
+
if (!$configPath) {
|
|
38
|
+
echo "Usage: php auto-tune.php [path_to_security-config.json]\n";
|
|
39
|
+
exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!file_exists($configPath)) {
|
|
43
|
+
fwrite(STDERR, "Error: Configuration file not found: {$configPath}\n");
|
|
44
|
+
exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
$config = json_decode(file_get_contents($configPath), true);
|
|
48
|
+
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
49
|
+
fwrite(STDERR, "Error: Invalid JSON configuration file.\n");
|
|
50
|
+
exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 2. Connect to datastore to retrieve accumulated traffic logs
|
|
54
|
+
$store = StoreManager::getStore();
|
|
55
|
+
if (!$store) {
|
|
56
|
+
fwrite(STDERR, "Error: No active persistent datastore available.\n");
|
|
57
|
+
exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
$rawTrafficLogs = $store->get('traffic_logs') ?? [];
|
|
61
|
+
if (empty($rawTrafficLogs)) {
|
|
62
|
+
echo "[AutoTuning] No traffic logs available for optimization.\n";
|
|
63
|
+
exit(0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 3. Sanitize data to prevent poisoning / Sybil attacks
|
|
67
|
+
$sanitizedLogs = RequestUtils::sanitizeTrafficData($rawTrafficLogs);
|
|
68
|
+
|
|
69
|
+
$minDataPoints = $config['autotuning']['minDataPoints'] ?? 200;
|
|
70
|
+
if (count($sanitizedLogs) < $minDataPoints) {
|
|
71
|
+
echo "[AutoTuning] Postponed: Insufficient sanitized logs (" . count($sanitizedLogs) . "/{$minDataPoints}).\n";
|
|
72
|
+
exit(0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
echo "[AutoTuning] Starting optimization pass on " . count($sanitizedLogs) . " traffic data points...\n";
|
|
76
|
+
|
|
77
|
+
// 4. Compute Pareto front
|
|
78
|
+
$paretoFront = Optimization::solveFullSecurityTuning(['trafficData' => $sanitizedLogs]);
|
|
79
|
+
if (empty($paretoFront)) {
|
|
80
|
+
fwrite(STDERR, "[AutoTuning] Optimization returned no solutions.\n");
|
|
81
|
+
exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 5. Select the most balanced solution (closest to origin)
|
|
85
|
+
$bestSolution = $paretoFront[0];
|
|
86
|
+
$minDistance = sqrt(pow($bestSolution['objectives'][0], 2) + pow($bestSolution['objectives'][1], 2));
|
|
87
|
+
foreach ($paretoFront as $candidate) {
|
|
88
|
+
$distance = sqrt(pow($candidate['objectives'][0], 2) + pow($candidate['objectives'][1], 2));
|
|
89
|
+
if ($distance < $minDistance) {
|
|
90
|
+
$minDistance = $distance;
|
|
91
|
+
$bestSolution = $candidate;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
$newConfig = $bestSolution['solution'];
|
|
96
|
+
$maxChangeVelocity = 0.15; // Max 15% inertial step per cycle
|
|
97
|
+
|
|
98
|
+
$applyInertialUpdate = function (array &$current, array $target) use ($maxChangeVelocity) {
|
|
99
|
+
$sumCurrent = array_sum($current);
|
|
100
|
+
if ($sumCurrent === 0) return;
|
|
101
|
+
$sumTarget = 0;
|
|
102
|
+
foreach ($current as $k => $v) {
|
|
103
|
+
if (isset($target[$k])) $sumTarget += $target[$k];
|
|
104
|
+
}
|
|
105
|
+
$ratio = ($sumTarget - $sumCurrent) / $sumCurrent;
|
|
106
|
+
$factor = 1 + max(-$maxChangeVelocity, min($maxChangeVelocity, $ratio));
|
|
107
|
+
foreach ($current as $k => &$v) {
|
|
108
|
+
if (isset($target[$k])) $v *= $factor;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
$applyInertialUpdate($config['thresholds'], $newConfig['thresholds']);
|
|
113
|
+
$applyInertialUpdate($config['weights'], $newConfig['weights']);
|
|
114
|
+
$applyInertialUpdate($config['patterns'], $newConfig['patterns']);
|
|
115
|
+
|
|
116
|
+
// 6. Write back optimized parameters to configuration file
|
|
117
|
+
file_put_contents($configPath, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
118
|
+
echo "[AutoTuning] Success: File {$configPath} updated with optimized parameters.\n";
|