@anonympins/fingerprint 0.8.0 → 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.
@@ -3,7 +3,7 @@
3
3
  * Plugin Name: Anonympins Bot Mitigation with Proof-of-Work
4
4
  * Plugin URI: https://github.com/anonympins/fingerprint
5
5
  * Description: High-performance client-side anti-bot protection and Proof-of-Work challenge verification for WordPress.
6
- * Version: 0.8.0
6
+ * Version: 0.8.1
7
7
  * Author: anonympins
8
8
  * Requires at least: 5.9
9
9
  * Requires PHP: 8.0
@@ -25,7 +25,11 @@ if (!defined('ABSPATH')) {
25
25
  }
26
26
 
27
27
  if (!defined('ANONYMPINS_BOT_MITIGATION_VERSION')) {
28
- define('ANONYMPINS_BOT_MITIGATION_VERSION', '0.8.0');
28
+ define('ANONYMPINS_BOT_MITIGATION_VERSION', '0.8.1');
29
+ }
30
+
31
+ if (!defined('ANONYMPINS_BOT_MITIGATION_DIR')) {
32
+ define('ANONYMPINS_BOT_MITIGATION_DIR', plugin_dir_path(__FILE__));
29
33
  }
30
34
 
31
35
  // =============================================================================
@@ -126,12 +130,25 @@ function fingerprint_get_sandbox_config(): array {
126
130
  ];
127
131
  }
128
132
 
133
+ /**
134
+ * Retrieves the configured direct endpoint path for Prometheus metrics scraping.
135
+ */
136
+ function fingerprint_get_metrics_endpoint(): string {
137
+ $saved = get_option('anonympins_security_options', get_option('fingerprint_security_options', []));
138
+ if (!isset($saved['metrics_endpoint'])) {
139
+ return '/metrics';
140
+ }
141
+ $path = trim((string)$saved['metrics_endpoint']);
142
+ if ($path === '') {
143
+ return '';
144
+ }
145
+ return str_starts_with($path, '/') ? $path : '/' . $path;
146
+ }
147
+
129
148
  // 1. PSR-4 autoloader for the Fingerprint engine.
130
149
  if (!class_exists(DirectFingerprint::class)) {
131
150
  $fingerprint_composer_paths = [
132
151
  __DIR__ . '/vendor/autoload.php',
133
- dirname(__DIR__, 2) . '/vendor/autoload.php',
134
- dirname(__DIR__, 3) . '/vendor/autoload.php',
135
152
  (defined('ABSPATH') ? ABSPATH . 'vendor/autoload.php' : ''),
136
153
  ];
137
154
  foreach ($fingerprint_composer_paths as $fingerprint_composer_path) {
@@ -361,6 +378,21 @@ add_action('plugins_loaded', function () use ($fingerprint_security_profiles) {
361
378
 
362
379
  $securityConfig = SecurityProfiles::createSecurityProfile($contextConfig['profile'], $contextConfig['overrides'] ?? []);
363
380
  $guard = new DirectFingerprint($securityConfig);
381
+
382
+ // Handle direct metrics endpoint if requested
383
+ $metricsPath = fingerprint_get_metrics_endpoint();
384
+ $path = (string)wp_parse_url($requestUri, PHP_URL_PATH);
385
+ if ($metricsPath !== '' && ($path === $metricsPath || str_ends_with($path, $metricsPath))) {
386
+ $context = $guard->buildRequestContext();
387
+ $isAuth = current_user_can('manage_options') || apply_filters('fingerprint_metrics_authorization', false, $context);
388
+ if (!$isAuth) {
389
+ status_header(403);
390
+ echo "Access to metrics denied.";
391
+ exit;
392
+ }
393
+ $guard->handleMetricsRequest($context);
394
+ return;
395
+ }
364
396
  $guard->protect();
365
397
  }, 0);
366
398
 
@@ -431,19 +463,37 @@ function fingerprint_inject_client_suspicion_event(): void {
431
463
  // SPECIAL SANDBOX ROUTES: REST & SERVER-SENT EVENTS (SSE)
432
464
  // =============================================================================
433
465
  add_action('rest_api_init', function () {
466
+ // 1. Prometheus Metrics (registered under plugin namespace and legacy namespace)
467
+ register_rest_route('anonympins/v1', '/metrics', [
468
+ 'methods' => 'GET',
469
+ 'callback' => 'fingerprint_rest_prometheus_metrics',
470
+ 'permission_callback' => function () {
471
+ return current_user_can('manage_options') || apply_filters('fingerprint_metrics_access', false);
472
+ },
473
+ ]);
474
+
434
475
  register_rest_route('fingerprint/v1', '/sandbox/telemetry', [
435
476
  'methods' => 'GET',
436
477
  'callback' => 'fingerprint_rest_sandbox_telemetry',
437
478
  'permission_callback' => function () {
438
- return current_user_can('manage_options') || !empty(fingerprint_get_sandbox_config()['enabled']);
479
+ return current_user_can('manage_options');
439
480
  },
440
481
  ]);
441
482
 
483
+ // 2. Sandbox Inspection Routes (Strictly restricted to manage_options)
442
484
  register_rest_route('fingerprint/v1', '/sandbox/sse', [
443
485
  'methods' => 'GET',
444
486
  'callback' => 'fingerprint_rest_sandbox_sse',
445
487
  'permission_callback' => function () {
446
- return current_user_can('manage_options') || !empty(fingerprint_get_sandbox_config()['enabled']);
488
+ return current_user_can('manage_options');
489
+ },
490
+ ]);
491
+
492
+ register_rest_route('fingerprint/v1', '/metrics', [
493
+ 'methods' => 'GET',
494
+ 'callback' => 'fingerprint_rest_prometheus_metrics',
495
+ 'permission_callback' => function () {
496
+ return current_user_can('manage_options') || apply_filters('fingerprint_metrics_access', false);
447
497
  },
448
498
  ]);
449
499
 
@@ -478,7 +528,21 @@ function fingerprint_rest_sandbox_clear_challenges(): \WP_REST_Response {
478
528
  return new \WP_REST_Response(['cleared' => true], 200);
479
529
  }
480
530
 
531
+ function fingerprint_rest_prometheus_metrics(\WP_REST_Request $request): \WP_REST_Response {
532
+ $effectiveProfiles = fingerprint_get_effective_profiles($GLOBALS['fingerprint_security_profiles'] ?? []);
533
+ $securityConfig = SecurityProfiles::createSecurityProfile($effectiveProfiles['frontend']['profile'] ?? 'blog', $effectiveProfiles['frontend']['overrides'] ?? []);
534
+ $metricsOutput = MetricsManager::getPrometheusMetrics($securityConfig);
535
+
536
+ $response = new \WP_REST_Response($metricsOutput, 200);
537
+ $response->header('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
538
+ return $response;
539
+ }
540
+
481
541
  function fingerprint_rest_sandbox_sse(): void {
542
+ if (!current_user_can('manage_options')) {
543
+ wp_die(esc_html__('Unauthorized access.', 'anonympins-bot-mitigation-pow'), '', ['response' => 403]);
544
+ }
545
+
482
546
  if (function_exists('apache_setenv')) {
483
547
  apache_setenv('no-gzip', '1');
484
548
  }
@@ -803,6 +867,22 @@ function fingerprint_render_admin_page(): void {
803
867
  echo '<div class="notice notice-success is-dismissible"><p><strong>' . esc_html__('Sandbox settings updated successfully.', 'anonympins-bot-mitigation-pow') . '</strong></p></div>';
804
868
  }
805
869
 
870
+ if (isset($_POST['fingerprint_save_prometheus']) && check_admin_referer('fingerprint_prometheus_nonce', 'fingerprint_nonce_prometheus')) {
871
+ $saved = get_option('anonympins_security_options', get_option('fingerprint_security_options', []));
872
+ if (!is_array($saved)) {
873
+ $saved = [];
874
+ }
875
+
876
+ $rawEndpoint = isset($_POST['metrics_endpoint']) ? sanitize_text_field(wp_unslash($_POST['metrics_endpoint'])) : '/metrics';
877
+ $rawEndpoint = trim($rawEndpoint);
878
+ if ($rawEndpoint !== '' && !str_starts_with($rawEndpoint, '/')) {
879
+ $rawEndpoint = '/' . $rawEndpoint;
880
+ }
881
+ $saved['metrics_endpoint'] = $rawEndpoint;
882
+ update_option('anonympins_security_options', $saved);
883
+ echo '<div class="notice notice-success is-dismissible"><p><strong>' . esc_html__('Prometheus settings updated successfully.', 'anonympins-bot-mitigation-pow') . '</strong></p></div>';
884
+ }
885
+
806
886
  if (isset($_POST['fingerprint_clear_store']) && check_admin_referer('fingerprint_clear_nonce', 'fingerprint_nonce_clear')) {
807
887
  $store = new WpDbStore();
808
888
  $store->clear();
@@ -813,12 +893,13 @@ function fingerprint_render_admin_page(): void {
813
893
  $effective = fingerprint_get_effective_profiles($fingerprint_security_profiles);
814
894
  $frontendConfig = SecurityProfiles::createSecurityProfile($effective['frontend']['profile'], $effective['frontend']['overrides'] ?? []);
815
895
 
896
+ $store = new WpDbStore();
897
+ $totalRows = $store->getTotalCount();
898
+ $expiredRows = $store->getExpiredCount();
816
899
  $tableName = esc_sql($wpdb->prefix . 'fingerprint_store');
817
900
  // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
818
- $totalRows = (int)$wpdb->get_var("SELECT COUNT(*) FROM `{$tableName}`");
819
901
  $now = time();
820
902
  // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
821
- $expiredRows = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$tableName}` WHERE expires_at IS NOT NULL AND expires_at < %d", $now));
822
903
 
823
904
  $prometheusRaw = MetricsManager::getPrometheusMetrics($frontendConfig);
824
905
  ?>
@@ -1114,6 +1195,26 @@ function fingerprint_render_admin_page(): void {
1114
1195
 
1115
1196
  <div id="tab-prometheus" class="fingerprint-tab-content" style="display:none;background:#fff;padding:20px;border:1px solid #ccd0d4;border-top:none;">
1116
1197
  <h3><?php esc_html_e('Real-Time Prometheus Metrics', 'anonympins-bot-mitigation-pow'); ?></h3>
1198
+ <form method="post" action="" style="margin-bottom:20px;">
1199
+ <?php wp_nonce_field('fingerprint_prometheus_nonce', 'fingerprint_nonce_prometheus'); ?>
1200
+ <table class="form-table">
1201
+ <tr>
1202
+ <th scope="row"><label for="metrics_endpoint"><?php esc_html_e('Metrics Endpoint Path', 'anonympins-bot-mitigation-pow'); ?></label></th>
1203
+ <td>
1204
+ <input type="text" name="metrics_endpoint" id="metrics_endpoint" value="<?php echo esc_attr(fingerprint_get_metrics_endpoint()); ?>" class="regular-text" placeholder="/metrics">
1205
+ <p class="description">
1206
+ <?php esc_html_e('Custom path for direct Prometheus scraping (default: /metrics). Leave empty to disable direct path scraping.', 'anonympins-bot-mitigation-pow'); ?>
1207
+ </p>
1208
+ <?php if (fingerprint_get_metrics_endpoint() !== ''): ?>
1209
+ <p class="description">
1210
+ <strong><?php esc_html_e('Scrape URL:', 'anonympins-bot-mitigation-pow'); ?></strong> <code><?php echo esc_html(home_url(fingerprint_get_metrics_endpoint())); ?></code>
1211
+ </p>
1212
+ <?php endif; ?>
1213
+ </td>
1214
+ </tr>
1215
+ </table>
1216
+ <?php submit_button(esc_html__('Save Prometheus Settings', 'anonympins-bot-mitigation-pow'), 'primary', 'fingerprint_save_prometheus'); ?>
1217
+ </form>
1117
1218
  <textarea readonly style="width:100%;height:380px;font-family:monospace;background:#f6f7f7;padding:12px;"><?php echo esc_textarea($prometheusRaw); ?></textarea>
1118
1219
  </div>
1119
1220
  </div>
@@ -1,6 +1,6 @@
1
1
  msgid ""
2
2
  msgstr ""
3
- "Project-Id-Version: Anonympins Bot Mitigation with Proof-of-Work 0.8.0\n"
3
+ "Project-Id-Version: Anonympins Bot Mitigation with Proof-of-Work 0.8.1\n"
4
4
  "Report-Msgid-Bugs-To: https://github.com/anonympins/fingerprint/issues\n"
5
5
  "POT-Creation-Date: 2026-09-24 12:00+0000\n"
6
6
  "PO-Revision-Date: 2026-09-24 12:00+0000\n"
@@ -1,6 +1,6 @@
1
1
  msgid ""
2
2
  msgstr ""
3
- "Project-Id-Version: Fingerprint Anti-Bot 0.8.0\n"
3
+ "Project-Id-Version: Fingerprint Anti-Bot 0.8.1\n"
4
4
  "Report-Msgid-Bugs-To: https://github.com/anonympins/fingerprint/issues\n"
5
5
  "POT-Creation-Date: 2026-09-24 12:00+0000\n"
6
6
  "PO-Revision-Date: 2026-09-24 12:00+0000\n"
@@ -1,6 +1,6 @@
1
1
  msgid ""
2
2
  msgstr ""
3
- "Project-Id-Version: Fingerprint Anti-Bot 0.8.0\n"
3
+ "Project-Id-Version: Fingerprint Anti-Bot 0.8.1\n"
4
4
  "Report-Msgid-Bugs-To: https://github.com/anonympins/fingerprint/issues\n"
5
5
  "POT-Creation-Date: 2026-09-24 12:00+0000\n"
6
6
  "PO-Revision-Date: 2026-09-24 12:00+0000\n"
@@ -44,6 +44,7 @@ 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);
47
48
 
48
49
  // 2. Copy main WordPress plugin files
49
50
  copy($wpDir . '/anonympins-bot-mitigation-pow.php', $buildDir . '/anonympins-bot-mitigation-pow.php');
@@ -131,6 +132,15 @@ if (file_exists($clientSource)) {
131
132
  copy($clientSource, $buildDir . '/assets/fingerprint.client.js');
132
133
  }
133
134
 
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));
141
+ }
142
+ }
143
+
134
144
  // 4. Recursively copy PHP library (src/php -> build/src), excluding dev folders
135
145
  $excludeDirs = ['WordPress', 'Tests', 'bin'];
136
146
  $phpIterator = new RecursiveIteratorIterator(
@@ -2,9 +2,9 @@
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.8.0
7
+ Stable tag: 0.8.1
8
8
  License: MIT
9
9
  License URI: https://opensource.org/licenses/MIT
10
10
 
@@ -37,6 +37,12 @@ No. All evaluations, challenges, and validations occur directly on your WordPres
37
37
  = Is HTTPS required? =
38
38
  HTTPS is strongly recommended. Under unencrypted HTTP, modern browsers disable the Web Cryptography API (`crypto.subtle`), which reduces challenge verification performance.
39
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
+
40
46
  = How can I monitor incoming request suspicion scores in real time? =
41
47
  Under **Settings -> Anonympins Bot Mitigation -> Sandbox / Test Mode**, the plugin provides real-time observability interfaces for all challenged visitors across the website:
42
48