@anonympins/fingerprint 0.7.5 → 0.8.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 (68) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +1 -1
  3. package/package.json +3 -2
  4. package/public/anonympins-bot-mitigation-pow.zip +0 -0
  5. package/src/js/asn-lookup.js +223 -0
  6. package/src/js/fingerprint.client.js +1784 -1772
  7. package/src/js/fingerprint.client.obfuscated.js +1 -1
  8. package/src/js/fingerprint.js +236 -40
  9. package/src/js/fingerprint.utils.js +252 -2
  10. package/src/js/pow.worker.js +2 -2
  11. package/src/js/tests/asn-lookup.test.js +57 -0
  12. package/src/js/tests/cross_parity.test.js +3 -3
  13. package/src/js/tests/dns-circuit-breaker.test.js +6 -6
  14. package/src/js/tests/fingerprint.builder.test.js +76 -78
  15. package/src/js/tests/fingerprint.client.test.js +16 -17
  16. package/src/js/tests/fingerprint.test.js +19 -12
  17. package/src/js/tests/ja3AnomalyDetector.test.js +135 -136
  18. package/src/js/tests/library.test.js +94 -95
  19. package/src/js/tests/obfuscation.test.js +8 -8
  20. package/src/js/tests/quicFingerprint.test.js +47 -0
  21. package/src/js/tests/tcpFingerprint.test.js +4 -4
  22. package/src/js/upow-model-task.js +155 -112
  23. package/src/php/AsnLookupEngine.php +173 -0
  24. package/src/php/Challenge/ChallengeUtils.php +430 -117
  25. package/src/php/Config/SecurityProfiles.php +75 -62
  26. package/src/php/DirectFingerprint.php +80 -13
  27. package/src/php/FingerprintBuilder.php +13 -13
  28. package/src/php/FingerprintClient.php +6 -6
  29. package/src/php/FingerprintEngine.php +2141 -1837
  30. package/src/php/Ja3AnomalyDetector.php +22 -22
  31. package/src/php/NetworkProfile.php +68 -0
  32. package/src/php/Optimization/FunctionRegistry.php +4 -4
  33. package/src/php/Optimization/Optimization.php +6 -6
  34. package/src/php/ProblemManager.php +12 -2
  35. package/src/php/RequestContext.php +11 -11
  36. package/src/php/Store/InMemoryStore.php +96 -66
  37. package/src/php/Store/MongoDbStore.php +2 -2
  38. package/src/php/Store/RedisStore.php +1 -1
  39. package/src/php/Store/StoreManager.php +48 -35
  40. package/src/php/Tests/AsnLookupTest.php +61 -0
  41. package/src/php/Tests/ChallengeUtilsTest.php +31 -0
  42. package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
  43. package/src/php/Tests/FingerprintClientTest.php +70 -70
  44. package/src/php/Tests/FingerprintEngineTest.php +4 -2
  45. package/src/php/Tests/PowTest.php +37 -39
  46. package/src/php/Tests/QuicFingerprintTest.php +115 -53
  47. package/src/php/Tests/ThreatIntelTest.php +2 -2
  48. package/src/php/Utils/BigInt.php +202 -202
  49. package/src/php/Utils/BlockList.php +13 -13
  50. package/src/php/Utils/Env.php +7 -2
  51. package/src/php/Utils/Logger.php +19 -1
  52. package/src/php/Utils/MaliciousPatterns.php +6 -6
  53. package/src/php/Utils/MetricsManager.php +209 -209
  54. package/src/php/Utils/RequestUtils.php +348 -18
  55. package/src/php/Utils/TLSClientHelloParser.php +385 -369
  56. package/src/php/WordPress/WpDbStore.php +34 -7
  57. package/src/php/WordPress/anonympins-bot-mitigation-pow.php +1222 -0
  58. package/src/php/WordPress/fingerprint-anti-bot.php +0 -559
  59. package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.mo +0 -0
  60. package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.po +232 -0
  61. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
  62. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +297 -261
  63. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
  64. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +348 -267
  65. package/src/php/WordPress/package.php +88 -99
  66. package/src/php/WordPress/readme.txt +28 -6
  67. package/src/php/bin/auto-tune.php +1 -1
  68. package/public/fingerprint-anti-bot.zip +0 -0
@@ -3,8 +3,8 @@
3
3
  declare(strict_types=1);
4
4
 
5
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é.
6
+ * Automated packaging script for the WordPress plugin.
7
+ * Copies PHP engine classes, JS assets, and creates an installation-ready ZIP archive.
8
8
  *
9
9
  * Usage:
10
10
  * php src/php/WordPress/package.php
@@ -20,13 +20,13 @@ $phpSrcDir = $rootDir . '/src/php';
20
20
  $jsSrcDir = $rootDir . '/src/js';
21
21
  $wpDir = $phpSrcDir . '/WordPress';
22
22
  $distDir = $rootDir . '/public';
23
- $pluginSlug = 'fingerprint-anti-bot';
23
+ $pluginSlug = 'anonympins-bot-mitigation-pow';
24
24
  $buildDir = $distDir . '/' . $pluginSlug;
25
25
  $zipFile = $distDir . '/' . $pluginSlug . '.zip';
26
26
 
27
- echo "==> Début du packaging du plugin WordPress...\n";
27
+ echo "==> Packaging WordPress plugin...\n";
28
28
 
29
- // 1. Nettoyage du répertoire de build précédent
29
+ // 1. Clean previous build directory
30
30
  if (is_dir($buildDir)) {
31
31
  $iterator = new RecursiveIteratorIterator(
32
32
  new RecursiveDirectoryIterator($buildDir, FilesystemIterator::SKIP_DOTS),
@@ -44,115 +44,104 @@ if (file_exists($zipFile)) {
44
44
  @mkdir($buildDir . '/src', 0777, true);
45
45
  @mkdir($buildDir . '/assets', 0777, true);
46
46
  @mkdir($buildDir . '/languages', 0777, true);
47
+ @mkdir($buildDir . '/config', 0777, true);
48
+
49
+ // 2. Copy main WordPress plugin files
50
+ copy($wpDir . '/anonympins-bot-mitigation-pow.php', $buildDir . '/anonympins-bot-mitigation-pow.php');
51
+ copy($wpDir . '/WpDbStore.php', $buildDir . '/WpDbStore.php');
52
+ if (file_exists($wpDir . '/readme.txt')) {
53
+ copy($wpDir . '/readme.txt', $buildDir . '/readme.txt');
54
+ }
55
+
56
+ // 2.b Compile and copy language files (PO -> MO)
57
+ $languagesDir = $wpDir . '/languages';
58
+ if (is_dir($languagesDir)) {
59
+ $poFiles = glob($languagesDir . '/*.po') ?: [];
60
+ foreach ($poFiles as $poFile) {
61
+ $filename = basename($poFile);
62
+ copy($poFile, $buildDir . '/languages/' . $filename);
63
+
64
+ $moFile = preg_replace('/\.po$/', '.mo', $poFile);
65
+ // Compile PO to binary MO if missing or outdated
66
+ if (!file_exists($moFile) || filemtime($poFile) > filemtime($moFile)) {
67
+ compilePoToMo($poFile, $moFile);
68
+ }
69
+ if (file_exists($moFile)) {
70
+ copy($moFile, $buildDir . '/languages/' . basename($moFile));
71
+ }
72
+ }
73
+ }
47
74
 
48
75
  /**
49
- * Compile un fichier .po gettext en binaire .mo compatible WordPress.
76
+ * Minimal native PHP PO -> binary gettext MO compiler
50
77
  */
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
-
78
+ function compilePoToMo(string $poPath, string $moPath): void {
79
+ $content = (string)file_get_contents($poPath);
80
+ $pattern = '/msgid\s+("(?:[^"\\\\]|\\\\.)*")\s+msgstr\s+("(?:[^"\\\\]|\\\\.)*")/s';
81
+ if (!preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
82
+ return;
83
+ }
56
84
  $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
- }
85
+ foreach ($matches as $m) {
86
+ $orig = stripcslashes(substr($m[1], 1, -1));
87
+ $trans = stripcslashes(substr($m[2], 1, -1));
88
+ if ($orig !== '' && $trans !== '') {
89
+ $entries[$orig] = $trans;
81
90
  }
82
91
  }
83
- if ($currentMsgId !== null && $currentMsgStr !== null) {
84
- $entries[$currentMsgId] = $currentMsgStr;
85
- }
86
-
87
92
  ksort($entries);
88
93
  $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
-
94
+ $originalsTable = '';
95
+ $translationsTable = '';
96
+ $origOffsets = [];
97
+ $transOffsets = [];
98
+ $headerLength = 28 + ($count * 8) * 2;
99
+ $currentOffset = $headerLength;
98
100
  foreach ($entries as $orig => $trans) {
99
- $origLen = strlen($orig);
100
- $origTable[] = ['len' => $origLen, 'off' => $curOffset + strlen($originals)];
101
- $originals .= $orig . "\0";
101
+ $origOffsets[] = ['len' => strlen($orig), 'off' => $currentOffset];
102
+ $currentOffset += strlen($orig) + 1;
102
103
  }
103
-
104
- $transOffset = $curOffset + strlen($originals);
105
104
  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']);
105
+ $transOffsets[] = ['len' => strlen($trans), 'off' => $currentOffset];
106
+ $currentOffset += strlen($trans) + 1;
125
107
  }
126
- $mo .= $originals . $translations;
127
- return file_put_contents($moFile, $mo) !== false;
108
+ $binary = pack('V7', 0x950412de, 0, $count, 28, 28 + ($count * 8), 0, 0);
109
+ foreach ($origOffsets as $o) { $binary .= pack('V2', $o['len'], $o['off']); }
110
+ foreach ($transOffsets as $t) { $binary .= pack('V2', $t['len'], $t['off']); }
111
+ foreach ($entries as $orig => $trans) { $binary .= $orig . "\0"; }
112
+ foreach ($entries as $orig => $trans) { $binary .= $trans . "\0"; }
113
+ file_put_contents($moPath, $binary);
128
114
  }
129
115
 
130
- // 2. Copie des fichiers principaux du plugin WordPress
131
- copy($wpDir . '/fingerprint-anti-bot.php', $buildDir . '/fingerprint-anti-bot.php');
132
- copy($wpDir . '/WpDbStore.php', $buildDir . '/WpDbStore.php');
133
- if (file_exists($wpDir . '/readme.txt')) {
134
- copy($wpDir . '/readme.txt', $buildDir . '/readme.txt');
135
- }
136
-
137
- // 3. Copie des assets clients nécessaires (solveur PoW)
116
+ // 3. Copy required client assets (PoW solver and client bundle without external CDN)
138
117
  $solverSource = $jsSrcDir . '/pow.solver.inline.js';
139
118
  if (file_exists($solverSource)) {
140
- copy($solverSource, $buildDir . '/assets/pow.solver.inline.js');
119
+ $solverContent = (string)file_get_contents($solverSource);
120
+ // Neutralize remote CDN scripts prohibited by WordPress.org guidelines
121
+ $solverContent = str_replace(
122
+ ["'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js'", "'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js'"],
123
+ ["''", "''"],
124
+ $solverContent
125
+ );
126
+ file_put_contents($buildDir . '/assets/pow.solver.inline.js', $solverContent);
127
+ }
128
+ $clientSource = file_exists($jsSrcDir . '/fingerprint.client.obfuscated.js')
129
+ ? $jsSrcDir . '/fingerprint.client.obfuscated.js'
130
+ : $jsSrcDir . '/fingerprint.client.js';
131
+ if (file_exists($clientSource)) {
132
+ copy($clientSource, $buildDir . '/assets/fingerprint.client.js');
141
133
  }
142
134
 
143
- // 4. Copie et compilation des fichiers de traduction i18n (FR / DE / EN)
144
- $langSourceDir = $wpDir . '/languages';
145
- if (is_dir($langSourceDir)) {
146
- foreach (glob($langSourceDir . '/*.po') as $poFile) {
147
- $baseName = basename($poFile);
148
- copy($poFile, $buildDir . '/languages/' . $baseName);
149
- $moTarget = $buildDir . '/languages/' . preg_replace('/\.po$/', '.mo', $baseName);
150
- compilePoToMo($poFile, $moTarget);
151
- compilePoToMo($poFile, $langSourceDir . '/' . preg_replace('/\.po$/', '.mo', $baseName));
135
+ // 3.b Copy problem/bot configuration assets
136
+ $configSourceDir = $rootDir . '/config';
137
+ if (is_dir($configSourceDir)) {
138
+ $configFiles = glob($configSourceDir . '/*.json') ?: [];
139
+ foreach ($configFiles as $cfgFile) {
140
+ copy($cfgFile, $buildDir . '/config/' . basename($cfgFile));
152
141
  }
153
142
  }
154
143
 
155
- // 5. Copie récursive de la bibliothèque PHP (src/php -> build/src), en excluant les dossiers de dev
144
+ // 4. Recursively copy PHP library (src/php -> build/src), excluding dev folders
156
145
  $excludeDirs = ['WordPress', 'Tests', 'bin'];
157
146
  $phpIterator = new RecursiveIteratorIterator(
158
147
  new RecursiveDirectoryIterator($phpSrcDir, FilesystemIterator::SKIP_DOTS),
@@ -177,9 +166,9 @@ foreach ($phpIterator as $item) {
177
166
  }
178
167
  }
179
168
 
180
- echo "==> Fichiers copiés dans {$buildDir}\n";
169
+ echo "==> Files copied to {$buildDir}\n";
181
170
 
182
- // 5. Création de l'archive ZIP
171
+ // 5. Create ZIP archive
183
172
  $zip = new ZipArchive();
184
173
  if ($zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
185
174
  fwrite(STDERR, "Erreur : Impossible de créer le fichier {$zipFile}\n");
@@ -194,7 +183,7 @@ $distIterator = new RecursiveIteratorIterator(
194
183
  foreach ($distIterator as $file) {
195
184
  if (!$file->isDir()) {
196
185
  $filePath = $file->getPathname();
197
- // Préserve le préfixe du dossier racine dans le ZIP : fingerprint-anti-bot/...
186
+ // Preserve plugin root directory prefix in archive: anonympins-bot-mitigation-pow/...
198
187
  $relativePath = $pluginSlug . '/' . substr($filePath, strlen($buildDir) + 1);
199
188
  $zip->addFile($filePath, str_replace('\\', '/', $relativePath));
200
189
  }
@@ -216,6 +205,6 @@ function removeDirectory(string $dir): void
216
205
  $zip->close();
217
206
  removeDirectory($buildDir);
218
207
  $sizeKb = round(filesize($zipFile) / 1024, 2);
219
- echo "==> Archive ZIP générée avec succès :\n";
220
- echo " Fichier : {$zipFile} ({$sizeKb} Ko)\n";
221
- echo " Prêt à être installé via wp-admin ou déployé en production !\n";
208
+ echo "==> ZIP archive generated successfully:\n";
209
+ echo " File: {$zipFile} ({$sizeKb} KB)\n";
210
+ echo " Ready to be installed via wp-admin or deployed to production.\n";
@@ -1,10 +1,10 @@
1
- === Fingerprint Anti-Bot & Proof-of-Work ===
1
+ === Anonympins Bot Mitigation with Proof-of-Work ===
2
2
  Contributors: anonympins
3
3
  Tags: bot protection, security, proof of work, firewall, anti scraping
4
4
  Requires at least: 5.9
5
- Tested up to: 7.1
5
+ Tested up to: 6.7
6
6
  Requires PHP: 8.0
7
- Stable tag: 0.7.5
7
+ Stable tag: 0.8.1
8
8
  License: MIT
9
9
  License URI: https://opensource.org/licenses/MIT
10
10
 
@@ -12,20 +12,22 @@ High-performance client-side anti-bot protection and Proof-of-Work challenge ver
12
12
 
13
13
  == Description ==
14
14
 
15
- Fingerprint Anti-Bot is a high-performance, privacy-friendly bot mitigation engine for WordPress. It combines behavioral telemetry, TLS/HTTP2/QUIC transport inspection (JA3, JA4), passive TCP/IP stack analysis, and asynchronous Proof-of-Work (PoW) verification.
15
+ Anonympins Bot Mitigation with Proof-of-Work is a high-performance, privacy-friendly bot mitigation engine for WordPress. It combines behavioral telemetry, TLS/HTTP2/QUIC transport inspection (JA3, JA4), passive TCP/IP stack analysis, and asynchronous Proof-of-Work (PoW) verification.
16
16
 
17
17
  = Features =
18
18
  * Transparent protection against scrapers, credential stuffers, and inventory scalpers.
19
19
  * Multi-layered behavioral analysis (mouse, keystrokes, touch dynamics).
20
20
  * Cryptographic Proof-of-Work challenges (CPU and Memory bound) without third-party CAPTCHA cookies or trackers.
21
21
  * Prometheus-compatible metrics endpoint for monitoring and observability.
22
+ * Dedicated Sandbox / Dry-Run mode with IP targeting for testing without blocking legitimate traffic.
22
23
  * Tailored security profiles for Frontend, Admin Area (wp-login), and REST API endpoints.
24
+ * Real-time suspicion vector inspection via Server-Sent Events (SSE), REST telemetry, and DOM CustomEvents.
23
25
 
24
26
  == Installation ==
25
27
 
26
- 1. Upload the plugin files to the `/wp-content/plugins/fingerprint-anti-bot` directory, or install the plugin through the WordPress plugins screen directly.
28
+ 1. Upload the plugin files to the `/wp-content/plugins/anonympins-bot-mitigation-pow` directory, or install the plugin through the WordPress plugins screen directly.
27
29
  2. Activate the plugin through the 'Plugins' screen in WordPress.
28
- 3. Navigate to **Settings -> Fingerprint Anti-Bot** to review security profiles, threshold adjustments, and Prometheus metrics.
30
+ 3. Navigate to **Settings -> Anonympins Bot Mitigation** to review security profiles, threshold adjustments, and Prometheus metrics.
29
31
 
30
32
  == Frequently Asked Questions ==
31
33
 
@@ -34,3 +36,23 @@ No. All evaluations, challenges, and validations occur directly on your WordPres
34
36
 
35
37
  = Is HTTPS required? =
36
38
  HTTPS is strongly recommended. Under unencrypted HTTP, modern browsers disable the Web Cryptography API (`crypto.subtle`), which reduces challenge verification performance.
39
+
40
+ = How do I scrape the Prometheus metrics endpoint? =
41
+ Metrics can be accessed in standard Prometheus text exposition format via:
42
+ 1. The REST API endpoint: `GET /wp-json/fingerprint/v1/metrics` (Requires `manage_options` permission or a custom authorization callback hooked into the `fingerprint_metrics_access` filter).
43
+ 2. Direct path endpoint: `GET /metrics` (Evaluated in early execution lifecycle; filterable with `fingerprint_metrics_authorization`).
44
+ 3. The admin dashboard: **Settings -> Anonympins Bot Mitigation -> Prometheus Stream**.
45
+
46
+ = How can I monitor incoming request suspicion scores in real time? =
47
+ Under **Settings -> Anonympins Bot Mitigation -> Sandbox / Test Mode**, the plugin provides real-time observability interfaces for all challenged visitors across the website:
48
+
49
+ 1. **Server-Sent Events (SSE)**:
50
+ `GET /wp-json/fingerprint/v1/sandbox/sse`
51
+ Streams timestamped telemetry objects containing `id`, `timestamp`, `ip`, `uri`, `action`, `suspicionScore`, and `suspicionVector` for every challenged visitor.
52
+
53
+ 2. **REST Telemetry Polling**:
54
+ `GET /wp-json/fingerprint/v1/sandbox/telemetry`
55
+ Returns the latest list and evaluated challenge payloads across all visitors in JSON format.
56
+
57
+ 3. **Client-side JavaScript event**:
58
+ Every page emits a DOM `CustomEvent('fingerprint:suspicion')` and sets `window.__FINGERPRINT_VECTOR__` when Sandbox mode is active or when an administrator is authenticated.
@@ -32,7 +32,7 @@ use Anonympins\Fingerprint\Optimization\Optimization;
32
32
  use Anonympins\Fingerprint\Store\StoreManager;
33
33
  use Anonympins\Fingerprint\Utils\RequestUtils;
34
34
 
35
- // 1. Récupération des arguments CLI
35
+ // 1. Retrieve CLI arguments
36
36
  $configPath = $argv[1] ?? null;
37
37
  if (!$configPath) {
38
38
  echo "Usage: php auto-tune.php [path_to_security-config.json]\n";
Binary file