@anonympins/fingerprint 0.7.4 → 0.8.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 (64) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/public/anonympins-bot-mitigation-pow.zip +0 -0
  5. package/src/js/dynamic-wasm.js +218 -218
  6. package/src/js/fingerprint.client.js +1784 -1738
  7. package/src/js/fingerprint.client.obfuscated.js +1 -1
  8. package/src/js/fingerprint.js +489 -99
  9. package/src/js/fingerprint.utils.js +252 -2
  10. package/src/js/pow.worker.js +2 -2
  11. package/src/js/tests/cross_parity.test.js +3 -3
  12. package/src/js/tests/dns-circuit-breaker.test.js +6 -6
  13. package/src/js/tests/dynamic-wasm.test.js +77 -77
  14. package/src/js/tests/fingerprint.builder.test.js +76 -78
  15. package/src/js/tests/fingerprint.client.init.test.js +78 -0
  16. package/src/js/tests/fingerprint.client.test.js +16 -17
  17. package/src/js/tests/fingerprint.test.js +37 -12
  18. package/src/js/tests/ja3AnomalyDetector.test.js +135 -136
  19. package/src/js/tests/library.test.js +94 -95
  20. package/src/js/tests/obfuscation.test.js +8 -8
  21. package/src/js/tests/quicFingerprint.test.js +47 -0
  22. package/src/js/tests/tcpFingerprint.test.js +4 -4
  23. package/src/js/tests/threatIntel.test.js +40 -1
  24. package/src/js/upow-model-task.js +155 -112
  25. package/src/php/Challenge/ChallengeUtils.php +392 -86
  26. package/src/php/Config/SecurityProfiles.php +79 -61
  27. package/src/php/DirectFingerprint.php +55 -8
  28. package/src/php/FingerprintBuilder.php +13 -13
  29. package/src/php/FingerprintClient.php +6 -6
  30. package/src/php/FingerprintEngine.php +2122 -1690
  31. package/src/php/Ja3AnomalyDetector.php +22 -22
  32. package/src/php/Optimization/FunctionRegistry.php +4 -4
  33. package/src/php/Optimization/Optimization.php +6 -6
  34. package/src/php/RequestContext.php +11 -11
  35. package/src/php/Store/InMemoryStore.php +96 -66
  36. package/src/php/Store/MongoDbStore.php +2 -2
  37. package/src/php/Store/RedisStore.php +1 -1
  38. package/src/php/Store/StoreManager.php +48 -35
  39. package/src/php/Tests/ChallengeUtilsTest.php +31 -0
  40. package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
  41. package/src/php/Tests/FingerprintClientTest.php +70 -70
  42. package/src/php/Tests/PowTest.php +37 -39
  43. package/src/php/Tests/QuicFingerprintTest.php +115 -53
  44. package/src/php/Tests/ThreatIntelTest.php +34 -0
  45. package/src/php/Utils/BigInt.php +202 -202
  46. package/src/php/Utils/BlockList.php +13 -13
  47. package/src/php/Utils/Logger.php +19 -1
  48. package/src/php/Utils/MaliciousPatterns.php +6 -6
  49. package/src/php/Utils/MetricsManager.php +209 -209
  50. package/src/php/Utils/RequestUtils.php +362 -1
  51. package/src/php/Utils/TLSClientHelloParser.php +385 -369
  52. package/src/php/WordPress/WpDbStore.php +7 -7
  53. package/src/php/WordPress/anonympins-bot-mitigation-pow.php +1121 -0
  54. package/src/php/WordPress/fingerprint-anti-bot.php +0 -559
  55. package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.mo +0 -0
  56. package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.po +232 -0
  57. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
  58. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +297 -261
  59. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
  60. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +348 -267
  61. package/src/php/WordPress/package.php +80 -101
  62. package/src/php/WordPress/readme.txt +21 -5
  63. package/src/php/bin/auto-tune.php +1 -1
  64. 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),
@@ -45,114 +45,93 @@ if (file_exists($zipFile)) {
45
45
  @mkdir($buildDir . '/assets', 0777, true);
46
46
  @mkdir($buildDir . '/languages', 0777, true);
47
47
 
48
+ // 2. Copy main WordPress plugin files
49
+ copy($wpDir . '/anonympins-bot-mitigation-pow.php', $buildDir . '/anonympins-bot-mitigation-pow.php');
50
+ copy($wpDir . '/WpDbStore.php', $buildDir . '/WpDbStore.php');
51
+ if (file_exists($wpDir . '/readme.txt')) {
52
+ copy($wpDir . '/readme.txt', $buildDir . '/readme.txt');
53
+ }
54
+
55
+ // 2.b Compile and copy language files (PO -> MO)
56
+ $languagesDir = $wpDir . '/languages';
57
+ if (is_dir($languagesDir)) {
58
+ $poFiles = glob($languagesDir . '/*.po') ?: [];
59
+ foreach ($poFiles as $poFile) {
60
+ $filename = basename($poFile);
61
+ copy($poFile, $buildDir . '/languages/' . $filename);
62
+
63
+ $moFile = preg_replace('/\.po$/', '.mo', $poFile);
64
+ // Compile PO to binary MO if missing or outdated
65
+ if (!file_exists($moFile) || filemtime($poFile) > filemtime($moFile)) {
66
+ compilePoToMo($poFile, $moFile);
67
+ }
68
+ if (file_exists($moFile)) {
69
+ copy($moFile, $buildDir . '/languages/' . basename($moFile));
70
+ }
71
+ }
72
+ }
73
+
48
74
  /**
49
- * Compile un fichier .po gettext en binaire .mo compatible WordPress.
75
+ * Minimal native PHP PO -> binary gettext MO compiler
50
76
  */
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
-
77
+ function compilePoToMo(string $poPath, string $moPath): void {
78
+ $content = (string)file_get_contents($poPath);
79
+ $pattern = '/msgid\s+("(?:[^"\\\\]|\\\\.)*")\s+msgstr\s+("(?:[^"\\\\]|\\\\.)*")/s';
80
+ if (!preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
81
+ return;
82
+ }
56
83
  $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
- }
84
+ foreach ($matches as $m) {
85
+ $orig = stripcslashes(substr($m[1], 1, -1));
86
+ $trans = stripcslashes(substr($m[2], 1, -1));
87
+ if ($orig !== '' && $trans !== '') {
88
+ $entries[$orig] = $trans;
81
89
  }
82
90
  }
83
- if ($currentMsgId !== null && $currentMsgStr !== null) {
84
- $entries[$currentMsgId] = $currentMsgStr;
85
- }
86
-
87
91
  ksort($entries);
88
92
  $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
-
93
+ $originalsTable = '';
94
+ $translationsTable = '';
95
+ $origOffsets = [];
96
+ $transOffsets = [];
97
+ $headerLength = 28 + ($count * 8) * 2;
98
+ $currentOffset = $headerLength;
98
99
  foreach ($entries as $orig => $trans) {
99
- $origLen = strlen($orig);
100
- $origTable[] = ['len' => $origLen, 'off' => $curOffset + strlen($originals)];
101
- $originals .= $orig . "\0";
100
+ $origOffsets[] = ['len' => strlen($orig), 'off' => $currentOffset];
101
+ $currentOffset += strlen($orig) + 1;
102
102
  }
103
-
104
- $transOffset = $curOffset + strlen($originals);
105
103
  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']);
104
+ $transOffsets[] = ['len' => strlen($trans), 'off' => $currentOffset];
105
+ $currentOffset += strlen($trans) + 1;
122
106
  }
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;
107
+ $binary = pack('V7', 0x950412de, 0, $count, 28, 28 + ($count * 8), 0, 0);
108
+ foreach ($origOffsets as $o) { $binary .= pack('V2', $o['len'], $o['off']); }
109
+ foreach ($transOffsets as $t) { $binary .= pack('V2', $t['len'], $t['off']); }
110
+ foreach ($entries as $orig => $trans) { $binary .= $orig . "\0"; }
111
+ foreach ($entries as $orig => $trans) { $binary .= $trans . "\0"; }
112
+ file_put_contents($moPath, $binary);
128
113
  }
129
114
 
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)
115
+ // 3. Copy required client assets (PoW solver and client bundle without external CDN)
138
116
  $solverSource = $jsSrcDir . '/pow.solver.inline.js';
139
117
  if (file_exists($solverSource)) {
140
- copy($solverSource, $buildDir . '/assets/pow.solver.inline.js');
118
+ $solverContent = (string)file_get_contents($solverSource);
119
+ // Neutralize remote CDN scripts prohibited by WordPress.org guidelines
120
+ $solverContent = str_replace(
121
+ ["'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js'", "'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js'"],
122
+ ["''", "''"],
123
+ $solverContent
124
+ );
125
+ file_put_contents($buildDir . '/assets/pow.solver.inline.js', $solverContent);
141
126
  }
142
-
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));
152
- }
127
+ $clientSource = file_exists($jsSrcDir . '/fingerprint.client.obfuscated.js')
128
+ ? $jsSrcDir . '/fingerprint.client.obfuscated.js'
129
+ : $jsSrcDir . '/fingerprint.client.js';
130
+ if (file_exists($clientSource)) {
131
+ copy($clientSource, $buildDir . '/assets/fingerprint.client.js');
153
132
  }
154
133
 
155
- // 5. Copie récursive de la bibliothèque PHP (src/php -> build/src), en excluant les dossiers de dev
134
+ // 4. Recursively copy PHP library (src/php -> build/src), excluding dev folders
156
135
  $excludeDirs = ['WordPress', 'Tests', 'bin'];
157
136
  $phpIterator = new RecursiveIteratorIterator(
158
137
  new RecursiveDirectoryIterator($phpSrcDir, FilesystemIterator::SKIP_DOTS),
@@ -177,9 +156,9 @@ foreach ($phpIterator as $item) {
177
156
  }
178
157
  }
179
158
 
180
- echo "==> Fichiers copiés dans {$buildDir}\n";
159
+ echo "==> Files copied to {$buildDir}\n";
181
160
 
182
- // 5. Création de l'archive ZIP
161
+ // 5. Create ZIP archive
183
162
  $zip = new ZipArchive();
184
163
  if ($zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
185
164
  fwrite(STDERR, "Erreur : Impossible de créer le fichier {$zipFile}\n");
@@ -194,7 +173,7 @@ $distIterator = new RecursiveIteratorIterator(
194
173
  foreach ($distIterator as $file) {
195
174
  if (!$file->isDir()) {
196
175
  $filePath = $file->getPathname();
197
- // Préserve le préfixe du dossier racine dans le ZIP : fingerprint-anti-bot/...
176
+ // Preserve plugin root directory prefix in archive: anonympins-bot-mitigation-pow/...
198
177
  $relativePath = $pluginSlug . '/' . substr($filePath, strlen($buildDir) + 1);
199
178
  $zip->addFile($filePath, str_replace('\\', '/', $relativePath));
200
179
  }
@@ -216,6 +195,6 @@ function removeDirectory(string $dir): void
216
195
  $zip->close();
217
196
  removeDirectory($buildDir);
218
197
  $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";
198
+ echo "==> ZIP archive generated successfully:\n";
199
+ echo " File: {$zipFile} ({$sizeKb} KB)\n";
200
+ 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
5
  Tested up to: 7.1
6
6
  Requires PHP: 8.0
7
- Stable tag: 0.7.4
7
+ Stable tag: 0.8.0
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,17 @@ 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 can I monitor incoming request suspicion scores in real time? =
41
+ Under **Settings -> Anonympins Bot Mitigation -> Sandbox / Test Mode**, the plugin provides real-time observability interfaces for all challenged visitors across the website:
42
+
43
+ 1. **Server-Sent Events (SSE)**:
44
+ `GET /wp-json/fingerprint/v1/sandbox/sse`
45
+ Streams timestamped telemetry objects containing `id`, `timestamp`, `ip`, `uri`, `action`, `suspicionScore`, and `suspicionVector` for every challenged visitor.
46
+
47
+ 2. **REST Telemetry Polling**:
48
+ `GET /wp-json/fingerprint/v1/sandbox/telemetry`
49
+ Returns the latest list and evaluated challenge payloads across all visitors in JSON format.
50
+
51
+ 3. **Client-side JavaScript event**:
52
+ 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