@anonympins/fingerprint 0.8.0 → 0.8.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 +54 -0
- package/README.md +1 -1
- package/package.json +4 -2
- package/public/anonympins-bot-mitigation-pow.zip +0 -0
- package/src/js/asn-lookup.js +223 -0
- package/src/js/fingerprint.client.obfuscated.js +1 -1
- package/src/js/fingerprint.js +214 -29
- package/src/js/tests/asn-lookup.test.js +57 -0
- package/src/php/AsnLookupEngine.php +173 -0
- package/src/php/Challenge/ChallengeUtils.php +162 -64
- package/src/php/DirectFingerprint.php +25 -5
- package/src/php/FingerprintEngine.php +126 -30
- package/src/php/NetworkProfile.php +68 -0
- package/src/php/ProblemManager.php +12 -2
- package/src/php/Tests/AsnLookupTest.php +61 -0
- package/src/php/Tests/FingerprintEngineTest.php +4 -2
- package/src/php/Utils/Env.php +7 -2
- package/src/php/Utils/RequestUtils.php +24 -6
- package/src/php/WordPress/WpDbStore.php +27 -0
- package/src/php/WordPress/anonympins-bot-mitigation-pow.php +303 -64
- package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.po +1 -1
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +1 -1
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +1 -1
- package/src/php/WordPress/package.php +10 -0
- package/src/php/WordPress/readme.txt +7 -1
|
@@ -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.
|
|
6
|
+
* Version: 0.8.2
|
|
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.
|
|
28
|
+
define('ANONYMPINS_BOT_MITIGATION_VERSION', '0.8.2');
|
|
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,50 @@ 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
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Returns the built-in default HTML challenge template (derived from fingerprint.js).
|
|
150
|
+
*/
|
|
151
|
+
function fingerprint_get_default_challenge_template(): string {
|
|
152
|
+
return "<!DOCTYPE html>\n" .
|
|
153
|
+
"<html>\n" .
|
|
154
|
+
"<head>\n" .
|
|
155
|
+
"\t<meta charset=\"utf-8\">\n" .
|
|
156
|
+
"\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" .
|
|
157
|
+
"\t<title>{{TITLE}}</title>\n" .
|
|
158
|
+
"\t<style>\n" .
|
|
159
|
+
"\t\t{{CUSTOM_CSS}}\n" .
|
|
160
|
+
"\t\tbody { font-family: sans-serif; text-align: center; padding-top: 50px; background: #fff; color: #222; }\n" .
|
|
161
|
+
"\t\t#loader { margin: 20px; font-size: 15px; color: #2271b1; }\n" .
|
|
162
|
+
"\t</style>\n" .
|
|
163
|
+
"</head>\n" .
|
|
164
|
+
"<body>\n" .
|
|
165
|
+
"\t<h1>{{TITLE}}</h1>\n" .
|
|
166
|
+
"\t<p>{{MESSAGE}}</p>\n" .
|
|
167
|
+
"\t<div id=\"loader\">⚙️ Initializing combined verification...</div>\n" .
|
|
168
|
+
"\t{{SOLVER_SCRIPT}}\n" .
|
|
169
|
+
"</body>\n" .
|
|
170
|
+
"</html>";
|
|
171
|
+
}
|
|
172
|
+
|
|
129
173
|
// 1. PSR-4 autoloader for the Fingerprint engine.
|
|
130
174
|
if (!class_exists(DirectFingerprint::class)) {
|
|
131
175
|
$fingerprint_composer_paths = [
|
|
132
176
|
__DIR__ . '/vendor/autoload.php',
|
|
133
|
-
dirname(__DIR__, 2) . '/vendor/autoload.php',
|
|
134
|
-
dirname(__DIR__, 3) . '/vendor/autoload.php',
|
|
135
177
|
(defined('ABSPATH') ? ABSPATH . 'vendor/autoload.php' : ''),
|
|
136
178
|
];
|
|
137
179
|
foreach ($fingerprint_composer_paths as $fingerprint_composer_path) {
|
|
@@ -202,7 +244,7 @@ add_action('fingerprint_prune_expired_entries', function () {
|
|
|
202
244
|
add_action('admin_notices', function () {
|
|
203
245
|
if (!is_ssl()) {
|
|
204
246
|
echo '<div class="notice notice-warning is-dismissible">';
|
|
205
|
-
echo '<p><strong>' . esc_html__('[Anonympins
|
|
247
|
+
echo '<p><strong>' . esc_html__('[Anonympins bot mitigation security warning]', 'anonympins-bot-mitigation-pow') . '</strong> : ' .
|
|
206
248
|
esc_html__('Your website is currently running on unencrypted HTTP. In this mode, modern web browsers disable the native Web Cryptography API (crypto.subtle) for security reasons, forcing a JavaScript fallback simulation that is slower and vulnerable to Man-in-the-Middle (MitM) attacks. We strongly recommend deploying a TLS/SSL certificate and enforcing HTTPS to ensure the cryptographic integrity of Proof-of-Work computations and identity protection.', 'anonympins-bot-mitigation-pow') .
|
|
207
249
|
'</p>';
|
|
208
250
|
echo '</div>';
|
|
@@ -217,9 +259,9 @@ add_action('admin_notices', function () {
|
|
|
217
259
|
: esc_html__('Enforced test mode: suspicion analysis and PoW challenges are actively triggered.', 'anonympins-bot-mitigation-pow');
|
|
218
260
|
|
|
219
261
|
echo '<div class="notice notice-warning is-dismissible">';
|
|
220
|
-
echo '<p><strong>' . esc_html__('[Anonympins
|
|
221
|
-
esc_html__('Sandbox
|
|
222
|
-
' <a href="' . esc_url(admin_url('options-general.php?page=anonympins-bot-mitigation#tab-sandbox')) . '">' . esc_html__('Configure
|
|
262
|
+
echo '<p><strong>' . esc_html__('[Anonympins bot mitigation sandbox active]', 'anonympins-bot-mitigation-pow') . '</strong> : ' .
|
|
263
|
+
esc_html__('Sandbox mode is currently ENABLED.', 'anonympins-bot-mitigation-pow') . ' ' . esc_html($modeDesc) .
|
|
264
|
+
' <a href="' . esc_url(admin_url('options-general.php?page=anonympins-bot-mitigation#tab-sandbox')) . '">' . esc_html__('Configure sandbox', 'anonympins-bot-mitigation-pow') . '</a></p>';
|
|
223
265
|
echo '</div>';
|
|
224
266
|
}
|
|
225
267
|
});
|
|
@@ -279,6 +321,11 @@ add_action('plugins_loaded', function () use ($fingerprint_security_profiles) {
|
|
|
279
321
|
$contextConfig = $configs['frontend'] ?? $fingerprint_security_profiles['frontend'];
|
|
280
322
|
}
|
|
281
323
|
|
|
324
|
+
$savedOptions = get_option('anonympins_security_options', get_option('fingerprint_security_options', []));
|
|
325
|
+
$contextConfig['overrides']['challengeTemplate'] = (is_array($savedOptions) && isset($savedOptions['challenge_template']))
|
|
326
|
+
? (string)$savedOptions['challenge_template']
|
|
327
|
+
: fingerprint_get_default_challenge_template();
|
|
328
|
+
|
|
282
329
|
$sandboxConfig = fingerprint_get_sandbox_config();
|
|
283
330
|
$isSandboxActive = false;
|
|
284
331
|
|
|
@@ -361,6 +408,21 @@ add_action('plugins_loaded', function () use ($fingerprint_security_profiles) {
|
|
|
361
408
|
|
|
362
409
|
$securityConfig = SecurityProfiles::createSecurityProfile($contextConfig['profile'], $contextConfig['overrides'] ?? []);
|
|
363
410
|
$guard = new DirectFingerprint($securityConfig);
|
|
411
|
+
|
|
412
|
+
// Handle direct metrics endpoint if requested
|
|
413
|
+
$metricsPath = fingerprint_get_metrics_endpoint();
|
|
414
|
+
$path = (string)wp_parse_url($requestUri, PHP_URL_PATH);
|
|
415
|
+
if ($metricsPath !== '' && ($path === $metricsPath || str_ends_with($path, $metricsPath))) {
|
|
416
|
+
$context = $guard->buildRequestContext();
|
|
417
|
+
$isAuth = current_user_can('manage_options') || apply_filters('fingerprint_metrics_authorization', false, $context);
|
|
418
|
+
if (!$isAuth) {
|
|
419
|
+
status_header(403);
|
|
420
|
+
echo "Access to metrics denied.";
|
|
421
|
+
exit;
|
|
422
|
+
}
|
|
423
|
+
$guard->handleMetricsRequest($context);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
364
426
|
$guard->protect();
|
|
365
427
|
}, 0);
|
|
366
428
|
|
|
@@ -431,19 +493,37 @@ function fingerprint_inject_client_suspicion_event(): void {
|
|
|
431
493
|
// SPECIAL SANDBOX ROUTES: REST & SERVER-SENT EVENTS (SSE)
|
|
432
494
|
// =============================================================================
|
|
433
495
|
add_action('rest_api_init', function () {
|
|
496
|
+
// 1. Prometheus Metrics (registered under plugin namespace and legacy namespace)
|
|
497
|
+
register_rest_route('anonympins/v1', '/metrics', [
|
|
498
|
+
'methods' => 'GET',
|
|
499
|
+
'callback' => 'fingerprint_rest_prometheus_metrics',
|
|
500
|
+
'permission_callback' => function () {
|
|
501
|
+
return current_user_can('manage_options') || apply_filters('fingerprint_metrics_access', false);
|
|
502
|
+
},
|
|
503
|
+
]);
|
|
504
|
+
|
|
434
505
|
register_rest_route('fingerprint/v1', '/sandbox/telemetry', [
|
|
435
506
|
'methods' => 'GET',
|
|
436
507
|
'callback' => 'fingerprint_rest_sandbox_telemetry',
|
|
437
508
|
'permission_callback' => function () {
|
|
438
|
-
return current_user_can('manage_options')
|
|
509
|
+
return current_user_can('manage_options');
|
|
439
510
|
},
|
|
440
511
|
]);
|
|
441
512
|
|
|
513
|
+
// 2. Sandbox Inspection Routes (Strictly restricted to manage_options)
|
|
442
514
|
register_rest_route('fingerprint/v1', '/sandbox/sse', [
|
|
443
515
|
'methods' => 'GET',
|
|
444
516
|
'callback' => 'fingerprint_rest_sandbox_sse',
|
|
445
517
|
'permission_callback' => function () {
|
|
446
|
-
return current_user_can('manage_options')
|
|
518
|
+
return current_user_can('manage_options');
|
|
519
|
+
},
|
|
520
|
+
]);
|
|
521
|
+
|
|
522
|
+
register_rest_route('fingerprint/v1', '/metrics', [
|
|
523
|
+
'methods' => 'GET',
|
|
524
|
+
'callback' => 'fingerprint_rest_prometheus_metrics',
|
|
525
|
+
'permission_callback' => function () {
|
|
526
|
+
return current_user_can('manage_options') || apply_filters('fingerprint_metrics_access', false);
|
|
447
527
|
},
|
|
448
528
|
]);
|
|
449
529
|
|
|
@@ -478,7 +558,21 @@ function fingerprint_rest_sandbox_clear_challenges(): \WP_REST_Response {
|
|
|
478
558
|
return new \WP_REST_Response(['cleared' => true], 200);
|
|
479
559
|
}
|
|
480
560
|
|
|
561
|
+
function fingerprint_rest_prometheus_metrics(\WP_REST_Request $request): \WP_REST_Response {
|
|
562
|
+
$effectiveProfiles = fingerprint_get_effective_profiles($GLOBALS['fingerprint_security_profiles'] ?? []);
|
|
563
|
+
$securityConfig = SecurityProfiles::createSecurityProfile($effectiveProfiles['frontend']['profile'] ?? 'blog', $effectiveProfiles['frontend']['overrides'] ?? []);
|
|
564
|
+
$metricsOutput = MetricsManager::getPrometheusMetrics($securityConfig);
|
|
565
|
+
|
|
566
|
+
$response = new \WP_REST_Response($metricsOutput, 200);
|
|
567
|
+
$response->header('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
|
|
568
|
+
return $response;
|
|
569
|
+
}
|
|
570
|
+
|
|
481
571
|
function fingerprint_rest_sandbox_sse(): void {
|
|
572
|
+
if (!current_user_can('manage_options')) {
|
|
573
|
+
wp_die(esc_html__('Unauthorized access.', 'anonympins-bot-mitigation-pow'), '', ['response' => 403]);
|
|
574
|
+
}
|
|
575
|
+
|
|
482
576
|
if (function_exists('apache_setenv')) {
|
|
483
577
|
apache_setenv('no-gzip', '1');
|
|
484
578
|
}
|
|
@@ -539,8 +633,8 @@ function fingerprint_rest_sandbox_sse(): void {
|
|
|
539
633
|
// =============================================================================
|
|
540
634
|
add_action('admin_menu', function () {
|
|
541
635
|
add_options_page(
|
|
542
|
-
__('Anonympins
|
|
543
|
-
__('Anonympins
|
|
636
|
+
__('Anonympins bot mitigation', 'anonympins-bot-mitigation-pow'),
|
|
637
|
+
__('Anonympins bot mitigation', 'anonympins-bot-mitigation-pow'),
|
|
544
638
|
'manage_options',
|
|
545
639
|
'anonympins-bot-mitigation',
|
|
546
640
|
'fingerprint_render_admin_page'
|
|
@@ -555,6 +649,9 @@ add_action('admin_enqueue_scripts', function (string $hook) {
|
|
|
555
649
|
return;
|
|
556
650
|
}
|
|
557
651
|
|
|
652
|
+
// Enqueue native WordPress CodeMirror editor for HTML template editing
|
|
653
|
+
$editorSettings = wp_enqueue_code_editor(['type' => 'text/html']);
|
|
654
|
+
|
|
558
655
|
wp_register_script('anonympins-admin-settings', false, [], ANONYMPINS_BOT_MITIGATION_VERSION, true);
|
|
559
656
|
wp_enqueue_script('anonympins-admin-settings');
|
|
560
657
|
|
|
@@ -563,8 +660,13 @@ add_action('admin_enqueue_scripts', function (string $hook) {
|
|
|
563
660
|
evt.preventDefault();
|
|
564
661
|
document.querySelectorAll(".fingerprint-tab-content").forEach(function(t) { t.style.display = "none"; });
|
|
565
662
|
document.querySelectorAll(".nav-tab-wrapper a").forEach(function(n) { n.classList.remove("nav-tab-active"); });
|
|
566
|
-
document.getElementById(tabId)
|
|
663
|
+
var target = document.getElementById(tabId);
|
|
664
|
+
if (target) { target.style.display = "block"; }
|
|
567
665
|
evt.currentTarget.classList.add("nav-tab-active");
|
|
666
|
+
|
|
667
|
+
if (tabId === "tab-template" && window.fingerprintCodeMirrorInstance) {
|
|
668
|
+
window.fingerprintCodeMirrorInstance.codemirror.refresh();
|
|
669
|
+
}
|
|
568
670
|
if (window.location.hash !== "#" + tabId && history.pushState) {
|
|
569
671
|
history.pushState(null, null, "#" + tabId);
|
|
570
672
|
}
|
|
@@ -581,6 +683,16 @@ add_action('admin_enqueue_scripts', function (string $hook) {
|
|
|
581
683
|
document.querySelectorAll(".nav-tab-wrapper a").forEach(function(n) { n.classList.remove("nav-tab-active"); });
|
|
582
684
|
targetContent.style.display = "block";
|
|
583
685
|
targetLink.classList.add("nav-tab-active");
|
|
686
|
+
if (targetId === "tab-template" && window.fingerprintCodeMirrorInstance) {
|
|
687
|
+
setTimeout(function() { window.fingerprintCodeMirrorInstance.codemirror.refresh(); }, 50);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (window.wp && wp.codeEditor && document.getElementById("challenge_template")) {
|
|
693
|
+
var editorConfig = ' . wp_json_encode($editorSettings) . ';
|
|
694
|
+
if (editorConfig) {
|
|
695
|
+
window.fingerprintCodeMirrorInstance = wp.codeEditor.initialize(document.getElementById("challenge_template"), editorConfig);
|
|
584
696
|
}
|
|
585
697
|
}
|
|
586
698
|
});
|
|
@@ -596,9 +708,9 @@ add_action('admin_enqueue_scripts', function (string $hook) {
|
|
|
596
708
|
if (sseSource) {
|
|
597
709
|
sseSource.close();
|
|
598
710
|
sseSource = null;
|
|
599
|
-
statusEl.textContent = ' . wp_json_encode(__('SSE
|
|
711
|
+
statusEl.textContent = ' . wp_json_encode(__('SSE disconnected', 'anonympins-bot-mitigation-pow')) . ';
|
|
600
712
|
statusEl.style.color = "#646970";
|
|
601
|
-
btnText.textContent = ' . wp_json_encode(__('Start
|
|
713
|
+
btnText.textContent = ' . wp_json_encode(__('Start real-time SSE stream', 'anonympins-bot-mitigation-pow')) . ';
|
|
602
714
|
return;
|
|
603
715
|
}
|
|
604
716
|
|
|
@@ -607,9 +719,9 @@ add_action('admin_enqueue_scripts', function (string $hook) {
|
|
|
607
719
|
sseSource = new EventSource(' . wp_json_encode(rest_url('fingerprint/v1/sandbox/sse')) . ' + "?_wpnonce=" + wpRestNonce);
|
|
608
720
|
|
|
609
721
|
sseSource.onopen = function() {
|
|
610
|
-
statusEl.textContent = ' . wp_json_encode(__('SSE
|
|
722
|
+
statusEl.textContent = ' . wp_json_encode(__('SSE live connected', 'anonympins-bot-mitigation-pow')) . ';
|
|
611
723
|
statusEl.style.color = "#007017";
|
|
612
|
-
btnText.textContent = ' . wp_json_encode(__('Stop
|
|
724
|
+
btnText.textContent = ' . wp_json_encode(__('Stop stream', 'anonympins-bot-mitigation-pow')) . ';
|
|
613
725
|
};
|
|
614
726
|
|
|
615
727
|
sseSource.addEventListener("challenge", function(e) {
|
|
@@ -625,7 +737,7 @@ add_action('admin_enqueue_scripts', function (string $hook) {
|
|
|
625
737
|
});
|
|
626
738
|
|
|
627
739
|
sseSource.onerror = function() {
|
|
628
|
-
statusEl.textContent = ' . wp_json_encode(__('SSE
|
|
740
|
+
statusEl.textContent = ' . wp_json_encode(__('SSE reconnecting...', 'anonympins-bot-mitigation-pow')) . ';
|
|
629
741
|
statusEl.style.color = "#d63638";
|
|
630
742
|
};
|
|
631
743
|
}
|
|
@@ -803,6 +915,43 @@ function fingerprint_render_admin_page(): void {
|
|
|
803
915
|
echo '<div class="notice notice-success is-dismissible"><p><strong>' . esc_html__('Sandbox settings updated successfully.', 'anonympins-bot-mitigation-pow') . '</strong></p></div>';
|
|
804
916
|
}
|
|
805
917
|
|
|
918
|
+
if (isset($_POST['fingerprint_save_prometheus']) && check_admin_referer('fingerprint_prometheus_nonce', 'fingerprint_nonce_prometheus')) {
|
|
919
|
+
$saved = get_option('anonympins_security_options', get_option('fingerprint_security_options', []));
|
|
920
|
+
if (!is_array($saved)) {
|
|
921
|
+
$saved = [];
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
$rawEndpoint = isset($_POST['metrics_endpoint']) ? sanitize_text_field(wp_unslash($_POST['metrics_endpoint'])) : '/metrics';
|
|
925
|
+
$rawEndpoint = trim($rawEndpoint);
|
|
926
|
+
if ($rawEndpoint !== '' && !str_starts_with($rawEndpoint, '/')) {
|
|
927
|
+
$rawEndpoint = '/' . $rawEndpoint;
|
|
928
|
+
}
|
|
929
|
+
$saved['metrics_endpoint'] = $rawEndpoint;
|
|
930
|
+
update_option('anonympins_security_options', $saved);
|
|
931
|
+
echo '<div class="notice notice-success is-dismissible"><p><strong>' . esc_html__('Prometheus settings updated successfully.', 'anonympins-bot-mitigation-pow') . '</strong></p></div>';
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
if ((isset($_POST['fingerprint_save_template']) || isset($_POST['fingerprint_reset_template'])) && check_admin_referer('fingerprint_template_nonce', 'fingerprint_nonce_template')) {
|
|
935
|
+
$saved = get_option('anonympins_security_options', get_option('fingerprint_security_options', []));
|
|
936
|
+
if (!is_array($saved)) {
|
|
937
|
+
$saved = [];
|
|
938
|
+
}
|
|
939
|
+
if (isset($_POST['fingerprint_reset_template'])) {
|
|
940
|
+
unset($saved['challenge_template']);
|
|
941
|
+
echo '<div class="notice notice-info is-dismissible"><p><strong>' . esc_html__('Template reset to default successfully.', 'anonympins-bot-mitigation-pow') . '</strong></p></div>';
|
|
942
|
+
} else {
|
|
943
|
+
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Administrator code template editor with script/style placeholders.
|
|
944
|
+
$rawTemplate = isset($_POST['challenge_template']) ? wp_unslash($_POST['challenge_template']) : '';
|
|
945
|
+
if (!current_user_can('unfiltered_html')) {
|
|
946
|
+
$saved['challenge_template'] = wp_kses_post($rawTemplate);
|
|
947
|
+
} else {
|
|
948
|
+
$saved['challenge_template'] = (string)$rawTemplate;
|
|
949
|
+
}
|
|
950
|
+
echo '<div class="notice notice-success is-dismissible"><p><strong>' . esc_html__('Challenge template updated successfully.', 'anonympins-bot-mitigation-pow') . '</strong></p></div>';
|
|
951
|
+
}
|
|
952
|
+
update_option('anonympins_security_options', $saved);
|
|
953
|
+
}
|
|
954
|
+
|
|
806
955
|
if (isset($_POST['fingerprint_clear_store']) && check_admin_referer('fingerprint_clear_nonce', 'fingerprint_nonce_clear')) {
|
|
807
956
|
$store = new WpDbStore();
|
|
808
957
|
$store->clear();
|
|
@@ -812,28 +961,33 @@ function fingerprint_render_admin_page(): void {
|
|
|
812
961
|
$sandboxConfig = fingerprint_get_sandbox_config();
|
|
813
962
|
$effective = fingerprint_get_effective_profiles($fingerprint_security_profiles);
|
|
814
963
|
$frontendConfig = SecurityProfiles::createSecurityProfile($effective['frontend']['profile'], $effective['frontend']['overrides'] ?? []);
|
|
964
|
+
$savedOptions = get_option('anonympins_security_options', get_option('fingerprint_security_options', []));
|
|
965
|
+
$currentTemplate = (is_array($savedOptions) && !empty($savedOptions['challenge_template']))
|
|
966
|
+
? (string)$savedOptions['challenge_template']
|
|
967
|
+
: fingerprint_get_default_challenge_template();
|
|
815
968
|
|
|
969
|
+
$store = new WpDbStore();
|
|
970
|
+
$totalRows = $store->getTotalCount();
|
|
971
|
+
$expiredRows = $store->getExpiredCount();
|
|
816
972
|
$tableName = esc_sql($wpdb->prefix . 'fingerprint_store');
|
|
817
973
|
// 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
974
|
$now = time();
|
|
820
975
|
// 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
976
|
|
|
823
977
|
$prometheusRaw = MetricsManager::getPrometheusMetrics($frontendConfig);
|
|
824
978
|
?>
|
|
825
979
|
<div class="wrap">
|
|
826
|
-
<h1><span class="dashicons dashicons-shield-alt" style="font-size:32px;vertical-align:middle;margin-right:8px;"></span> Anonympins
|
|
980
|
+
<h1><span class="dashicons dashicons-shield-alt" style="font-size:32px;vertical-align:middle;margin-right:8px;"></span> Anonympins bot mitigation with proof-of-work</h1>
|
|
827
981
|
<p class="description"><?php esc_html_e('Client-side behavioral and cryptographic anti-bot protection without third-party CAPTCHA for WordPress.', 'anonympins-bot-mitigation-pow'); ?></p>
|
|
828
982
|
|
|
829
983
|
<div style="background:#fff;border-left:4px solid #2271b1;padding:12px 18px;margin:18px 0;box-shadow:0 1px 1px rgba(0,0,0,.04);">
|
|
830
984
|
<p style="margin:4px 0;">
|
|
831
985
|
<strong><?php esc_html_e('Official documentation reference:', 'anonympins-bot-mitigation-pow'); ?></strong>
|
|
832
986
|
<a href="https://github.com/anonympins/fingerprint/blob/main/doc/full_options.md" target="_blank" rel="noopener noreferrer" class="button button-secondary" style="margin-left:8px;">
|
|
833
|
-
<span class="dashicons dashicons-external" style="vertical-align:middle;"></span> <?php esc_html_e('View
|
|
987
|
+
<span class="dashicons dashicons-external" style="vertical-align:middle;"></span> <?php esc_html_e('View full configuration options on GitHub', 'anonympins-bot-mitigation-pow'); ?>
|
|
834
988
|
</a>
|
|
835
989
|
<a href="https://github.com/anonympins/fingerprint" target="_blank" rel="noopener noreferrer" class="button button-secondary" style="margin-left:4px;">
|
|
836
|
-
<span class="dashicons dashicons-admin-plugins" style="vertical-align:middle;"></span> <?php esc_html_e('GitHub
|
|
990
|
+
<span class="dashicons dashicons-admin-plugins" style="vertical-align:middle;"></span> <?php esc_html_e('GitHub repository', 'anonympins-bot-mitigation-pow'); ?>
|
|
837
991
|
</a>
|
|
838
992
|
</p>
|
|
839
993
|
</div>
|
|
@@ -841,7 +995,7 @@ function fingerprint_render_admin_page(): void {
|
|
|
841
995
|
<!-- STATISTICS & STATUS DASHBOARD -->
|
|
842
996
|
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(240px, 1fr));gap:16px;margin-bottom:24px;">
|
|
843
997
|
<div style="background:#fff;padding:16px;border-radius:4px;border:1px solid #ccd0d4;">
|
|
844
|
-
<h3 style="margin-top:0;"><?php esc_html_e('HTTPS
|
|
998
|
+
<h3 style="margin-top:0;"><?php esc_html_e('HTTPS status', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
845
999
|
<?php if (is_ssl()): ?>
|
|
846
1000
|
<p style="color:#007017;font-weight:bold;font-size:16px;">
|
|
847
1001
|
<span class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Active (WebCrypto Subtle available)', 'anonympins-bot-mitigation-pow'); ?>
|
|
@@ -853,7 +1007,7 @@ function fingerprint_render_admin_page(): void {
|
|
|
853
1007
|
<?php endif; ?>
|
|
854
1008
|
</div>
|
|
855
1009
|
<div style="background:#fff;padding:16px;border-radius:4px;border:1px solid #ccd0d4;">
|
|
856
|
-
<h3 style="margin-top:0;"><?php esc_html_e('SQL
|
|
1010
|
+
<h3 style="margin-top:0;"><?php esc_html_e('SQL cache ($wpdb)', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
857
1011
|
<p style="font-size:22px;margin:0;font-weight:600;"><?php echo esc_html((string)$totalRows); ?> <span style="font-size:14px;color:#646970;font-weight:normal;"><?php esc_html_e('keys in database', 'anonympins-bot-mitigation-pow'); ?></span></p>
|
|
858
1012
|
<small style="color:#8c8f94;"><?php
|
|
859
1013
|
/* translators: %d: number of expired keys awaiting cron purge */
|
|
@@ -861,7 +1015,7 @@ function fingerprint_render_admin_page(): void {
|
|
|
861
1015
|
?></small>
|
|
862
1016
|
</div>
|
|
863
1017
|
<div style="background:#fff;padding:16px;border-radius:4px;border:1px solid #ccd0d4;">
|
|
864
|
-
<h3 style="margin-top:0;"><?php esc_html_e('Active
|
|
1018
|
+
<h3 style="margin-top:0;"><?php esc_html_e('Active profiles', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
865
1019
|
<p style="margin:0;">
|
|
866
1020
|
<?php esc_html_e('Frontend:', 'anonympins-bot-mitigation-pow'); ?> <strong><?php echo esc_html($effective['frontend']['profile']); ?></strong><br>
|
|
867
1021
|
<?php esc_html_e('Admin:', 'anonympins-bot-mitigation-pow'); ?> <strong><?php echo esc_html($effective['admin']['profile']); ?></strong><br>
|
|
@@ -869,7 +1023,7 @@ function fingerprint_render_admin_page(): void {
|
|
|
869
1023
|
</p>
|
|
870
1024
|
</div>
|
|
871
1025
|
<div style="background:#fff;padding:16px;border-radius:4px;border:1px solid #ccd0d4;">
|
|
872
|
-
<h3 style="margin-top:0;"><?php esc_html_e('Sandbox
|
|
1026
|
+
<h3 style="margin-top:0;"><?php esc_html_e('Sandbox mode', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
873
1027
|
<?php if ($sandboxConfig['enabled']): ?>
|
|
874
1028
|
<p style="color:#dba617;font-weight:bold;font-size:16px;margin:0;">
|
|
875
1029
|
<span class="dashicons dashicons-warning"></span> <?php esc_html_e('Active (Simulation)', 'anonympins-bot-mitigation-pow'); ?>
|
|
@@ -886,30 +1040,31 @@ function fingerprint_render_admin_page(): void {
|
|
|
886
1040
|
|
|
887
1041
|
<!-- NAVIGATION TABS -->
|
|
888
1042
|
<h2 class="nav-tab-wrapper">
|
|
889
|
-
<a href="#tab-settings" class="nav-tab nav-tab-active" onclick="fingerprintSwitchTab(event, 'tab-settings')"><?php esc_html_e('Settings &
|
|
890
|
-
<a href="#tab-
|
|
891
|
-
<a href="#tab-
|
|
892
|
-
<a href="#tab-
|
|
1043
|
+
<a href="#tab-settings" class="nav-tab nav-tab-active" onclick="fingerprintSwitchTab(event, 'tab-settings')"><?php esc_html_e('Settings & profiles', 'anonympins-bot-mitigation-pow'); ?></a>
|
|
1044
|
+
<a href="#tab-template" class="nav-tab" onclick="fingerprintSwitchTab(event, 'tab-template')"><?php esc_html_e('Challenge HTML template', 'anonympins-bot-mitigation-pow'); ?></a>
|
|
1045
|
+
<a href="#tab-sandbox" class="nav-tab" onclick="fingerprintSwitchTab(event, 'tab-sandbox')"><?php esc_html_e('Sandbox / test mode', 'anonympins-bot-mitigation-pow'); ?></a>
|
|
1046
|
+
<a href="#tab-metrics" class="nav-tab" onclick="fingerprintSwitchTab(event, 'tab-metrics')"><?php esc_html_e('Metrics & weights view', 'anonympins-bot-mitigation-pow'); ?></a>
|
|
1047
|
+
<a href="#tab-prometheus" class="nav-tab" onclick="fingerprintSwitchTab(event, 'tab-prometheus')"><?php esc_html_e('Prometheus stream', 'anonympins-bot-mitigation-pow'); ?></a>
|
|
893
1048
|
</h2>
|
|
894
1049
|
|
|
895
1050
|
<div id="tab-settings" class="fingerprint-tab-content" style="background:#fff;padding:20px;border:1px solid #ccd0d4;border-top:none;">
|
|
896
1051
|
<form method="post" action="">
|
|
897
1052
|
<?php wp_nonce_field('fingerprint_settings_nonce', 'fingerprint_nonce'); ?>
|
|
898
|
-
<h3><?php esc_html_e('1. Target
|
|
1053
|
+
<h3><?php esc_html_e('1. Target security profiles', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
899
1054
|
<table class="form-table">
|
|
900
1055
|
<tr>
|
|
901
|
-
<th scope="row"><label for="frontend_profile"><?php esc_html_e('Visitors &
|
|
1056
|
+
<th scope="row"><label for="frontend_profile"><?php esc_html_e('Visitors & frontend', 'anonympins-bot-mitigation-pow'); ?></label></th>
|
|
902
1057
|
<td>
|
|
903
1058
|
<select name="frontend_profile" id="frontend_profile">
|
|
904
1059
|
<option value="blog" <?php selected($effective['frontend']['profile'], 'blog'); ?>><?php esc_html_e('Blog (Optimal for content, smooth UX)', 'anonympins-bot-mitigation-pow'); ?></option>
|
|
905
1060
|
<option value="balanced" <?php selected($effective['frontend']['profile'], 'balanced'); ?>><?php esc_html_e('Balanced (General purpose)', 'anonympins-bot-mitigation-pow'); ?></option>
|
|
906
1061
|
<option value="strict" <?php selected($effective['frontend']['profile'], 'strict'); ?>><?php esc_html_e('Strict (Maximum protection)', 'anonympins-bot-mitigation-pow'); ?></option>
|
|
907
|
-
<option value="ecommerce" <?php selected($effective['frontend']['profile'], 'ecommerce'); ?>><?php esc_html_e('E-commerce (
|
|
1062
|
+
<option value="ecommerce" <?php selected($effective['frontend']['profile'], 'ecommerce'); ?>><?php esc_html_e('E-commerce (anti-scraping / scalping)', 'anonympins-bot-mitigation-pow'); ?></option>
|
|
908
1063
|
</select>
|
|
909
1064
|
</td>
|
|
910
1065
|
</tr>
|
|
911
1066
|
<tr>
|
|
912
|
-
<th scope="row"><label for="admin_profile"><?php esc_html_e('Administration
|
|
1067
|
+
<th scope="row"><label for="admin_profile"><?php esc_html_e('Administration area (wp-login / wp-admin)', 'anonympins-bot-mitigation-pow'); ?></label></th>
|
|
913
1068
|
<td>
|
|
914
1069
|
<select name="admin_profile" id="admin_profile">
|
|
915
1070
|
<option value="strict" <?php selected($effective['admin']['profile'], 'strict'); ?>><?php esc_html_e('Strict (Recommended to secure logins)', 'anonympins-bot-mitigation-pow'); ?></option>
|
|
@@ -928,27 +1083,27 @@ function fingerprint_render_admin_page(): void {
|
|
|
928
1083
|
</tr>
|
|
929
1084
|
</table>
|
|
930
1085
|
<hr>
|
|
931
|
-
<h3><?php esc_html_e('2. Action
|
|
1086
|
+
<h3><?php esc_html_e('2. Action thresholds (frontend)', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
932
1087
|
<table class="form-table">
|
|
933
1088
|
<tr>
|
|
934
|
-
<th scope="row"><?php esc_html_e('Suspicion
|
|
1089
|
+
<th scope="row"><?php esc_html_e('Suspicion score thresholds (0 - 100)', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
935
1090
|
<td>
|
|
936
|
-
<label><?php esc_html_e('Low (
|
|
1091
|
+
<label><?php esc_html_e('Low (initial challenge):', 'anonympins-bot-mitigation-pow'); ?>
|
|
937
1092
|
<input type="number" name="frontend_threshold_low" value="<?php echo esc_attr((string)($frontendConfig['thresholds']['low'] ?? 20)); ?>" min="1" max="50" style="width:80px;">
|
|
938
1093
|
</label><br><br>
|
|
939
|
-
<label><?php esc_html_e('Medium (
|
|
1094
|
+
<label><?php esc_html_e('Medium (hardened challenge):', 'anonympins-bot-mitigation-pow'); ?>
|
|
940
1095
|
<input type="number" name="frontend_threshold_medium" value="<?php echo esc_attr((string)($frontendConfig['thresholds']['medium'] ?? 45)); ?>" min="10" max="75" style="width:80px;">
|
|
941
1096
|
</label><br><br>
|
|
942
|
-
<label><?php esc_html_e('High (
|
|
1097
|
+
<label><?php esc_html_e('High (heavy proof-of-work):', 'anonympins-bot-mitigation-pow'); ?>
|
|
943
1098
|
<input type="number" name="frontend_threshold_high" value="<?php echo esc_attr((string)($frontendConfig['thresholds']['high'] ?? 75)); ?>" min="30" max="95" style="width:80px;">
|
|
944
1099
|
</label><br><br>
|
|
945
|
-
<label><?php esc_html_e('Block (
|
|
1100
|
+
<label><?php esc_html_e('Block (immediate 403 forbidden):', 'anonympins-bot-mitigation-pow'); ?>
|
|
946
1101
|
<input type="number" name="frontend_threshold_block" value="<?php echo esc_attr((string)($frontendConfig['thresholds']['block'] ?? 95)); ?>" min="50" max="100" style="width:80px;">
|
|
947
1102
|
</label>
|
|
948
1103
|
</td>
|
|
949
1104
|
</tr>
|
|
950
1105
|
<tr>
|
|
951
|
-
<th scope="row"><?php esc_html_e('Advanced
|
|
1106
|
+
<th scope="row"><?php esc_html_e('Advanced behaviors', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
952
1107
|
<td>
|
|
953
1108
|
<label>
|
|
954
1109
|
<input type="checkbox" name="frontend_challenge_new" value="1" <?php checked(!empty($frontendConfig['challengeNewDevices'])); ?>>
|
|
@@ -965,13 +1120,13 @@ function fingerprint_render_admin_page(): void {
|
|
|
965
1120
|
</td>
|
|
966
1121
|
</tr>
|
|
967
1122
|
</table>
|
|
968
|
-
<?php submit_button(esc_html__('Save
|
|
1123
|
+
<?php submit_button(esc_html__('Save changes', 'anonympins-bot-mitigation-pow'), 'primary', 'fingerprint_save_settings'); ?>
|
|
969
1124
|
</form>
|
|
970
1125
|
<hr>
|
|
971
1126
|
<form method="post" action="" style="margin-top:16px;">
|
|
972
1127
|
<?php wp_nonce_field('fingerprint_clear_nonce', 'fingerprint_nonce_clear'); ?>
|
|
973
1128
|
<p>
|
|
974
|
-
<strong><?php esc_html_e('Store
|
|
1129
|
+
<strong><?php esc_html_e('Store maintenance:', 'anonympins-bot-mitigation-pow'); ?></strong>
|
|
975
1130
|
<button type="submit" name="fingerprint_clear_store" class="button button-secondary" onclick="return confirm('<?php echo esc_js(__('Purge all stored sessions and nonces?', 'anonympins-bot-mitigation-pow')); ?>');">
|
|
976
1131
|
<?php esc_html_e('Clear SQL cache table ($wpdb)', 'anonympins-bot-mitigation-pow'); ?>
|
|
977
1132
|
</button>
|
|
@@ -979,26 +1134,90 @@ function fingerprint_render_admin_page(): void {
|
|
|
979
1134
|
</form>
|
|
980
1135
|
</div>
|
|
981
1136
|
|
|
1137
|
+
<div id="tab-template" class="fingerprint-tab-content" style="display:none;background:#fff;padding:20px;border:1px solid #ccd0d4;border-top:none;">
|
|
1138
|
+
<h3><?php esc_html_e('Challenge HTML template editor', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
1139
|
+
<p class="description">
|
|
1140
|
+
<?php esc_html_e('Customize the HTML markup presented to challenged visitors. Leave empty or reset to use the built-in responsive default template.', 'anonympins-bot-mitigation-pow'); ?>
|
|
1141
|
+
</p>
|
|
1142
|
+
|
|
1143
|
+
<div style="background:#f0f6fc;border-left:4px solid #2271b1;padding:12px 16px;margin:16px 0;">
|
|
1144
|
+
<h4 style="margin:0 0 8px 0;"><?php esc_html_e('Available template placeholders', 'anonympins-bot-mitigation-pow'); ?></h4>
|
|
1145
|
+
<table class="widefat striped" style="background:#fff;font-size:12px;">
|
|
1146
|
+
<thead>
|
|
1147
|
+
<tr>
|
|
1148
|
+
<th style="width:200px;"><?php esc_html_e('Placeholder', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
1149
|
+
<th><?php esc_html_e('Description & content injected', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
1150
|
+
</tr>
|
|
1151
|
+
</thead>
|
|
1152
|
+
<tbody>
|
|
1153
|
+
<tr>
|
|
1154
|
+
<td><code>{{TITLE}}</code></td>
|
|
1155
|
+
<td><?php esc_html_e('Challenge page title (e.g., "Security verification")', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1156
|
+
</tr>
|
|
1157
|
+
<tr>
|
|
1158
|
+
<td><code>{{MESSAGE}}</code></td>
|
|
1159
|
+
<td><?php esc_html_e('Human-readable instruction or explanation message', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1160
|
+
</tr>
|
|
1161
|
+
<tr>
|
|
1162
|
+
<td><code>{{NONCE}}</code></td>
|
|
1163
|
+
<td><?php esc_html_e('Unique cryptographic challenge nonce generated by the server', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1164
|
+
</tr>
|
|
1165
|
+
<tr>
|
|
1166
|
+
<td><code>{{DIFFICULTY}}</code></td>
|
|
1167
|
+
<td><?php esc_html_e('Target computational difficulty complexity number', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1168
|
+
</tr>
|
|
1169
|
+
<tr>
|
|
1170
|
+
<td><code>{{ALGORITHM}}</code></td>
|
|
1171
|
+
<td><?php esc_html_e('Selected hashing algorithm (e.g., "SHA-256")', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1172
|
+
</tr>
|
|
1173
|
+
<tr>
|
|
1174
|
+
<td><code>{{FORM_ACTION}}</code></td>
|
|
1175
|
+
<td><?php esc_html_e('Target POST URI destination where the PoW result must be submitted', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1176
|
+
</tr>
|
|
1177
|
+
<tr>
|
|
1178
|
+
<td><code>{{SOLVER_SCRIPT}}</code></td>
|
|
1179
|
+
<td><?php esc_html_e('Mandatory: inlined WebAssembly/WebWorker solver script block required for verification', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1180
|
+
</tr>
|
|
1181
|
+
<tr>
|
|
1182
|
+
<td><code>{{CUSTOM_CSS}}</code></td>
|
|
1183
|
+
<td><?php esc_html_e('Optional: default reset styling and challenge animations', 'anonympins-bot-mitigation-pow'); ?></td>
|
|
1184
|
+
</tr>
|
|
1185
|
+
</tbody>
|
|
1186
|
+
</table>
|
|
1187
|
+
</div>
|
|
1188
|
+
|
|
1189
|
+
<form method="post" action="">
|
|
1190
|
+
<?php wp_nonce_field('fingerprint_template_nonce', 'fingerprint_nonce_template'); ?>
|
|
1191
|
+
<p>
|
|
1192
|
+
<textarea name="challenge_template" id="challenge_template" rows="18" style="width:100%;font-family:monospace;"><?php echo esc_textarea($currentTemplate); ?></textarea>
|
|
1193
|
+
</p>
|
|
1194
|
+
<p style="display:flex;gap:10px;">
|
|
1195
|
+
<?php submit_button(esc_html__('Save template', 'anonympins-bot-mitigation-pow'), 'primary', 'fingerprint_save_template', false); ?>
|
|
1196
|
+
<?php submit_button(esc_html__('Reset to default template', 'anonympins-bot-mitigation-pow'), 'secondary', 'fingerprint_reset_template', false, ['onclick' => "return confirm('" . esc_js(__('Reset challenge template to default?', 'anonympins-bot-mitigation-pow')) . "');"]); ?>
|
|
1197
|
+
</p>
|
|
1198
|
+
</form>
|
|
1199
|
+
</div>
|
|
1200
|
+
|
|
982
1201
|
<div id="tab-sandbox" class="fingerprint-tab-content" style="display:none;background:#fff;padding:20px;border:1px solid #ccd0d4;border-top:none;">
|
|
983
|
-
<h3><?php esc_html_e('Sandbox
|
|
1202
|
+
<h3><?php esc_html_e('Sandbox mode & dry-run configuration', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
984
1203
|
<form method="post" action="">
|
|
985
1204
|
<?php wp_nonce_field('fingerprint_sandbox_nonce', 'fingerprint_nonce_sandbox'); ?>
|
|
986
1205
|
<table class="form-table">
|
|
987
1206
|
<tr>
|
|
988
|
-
<th scope="row"><?php esc_html_e('Sandbox
|
|
1207
|
+
<th scope="row"><?php esc_html_e('Sandbox activation', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
989
1208
|
<td>
|
|
990
1209
|
<label>
|
|
991
1210
|
<input type="checkbox" name="sandbox_enabled" value="1" <?php checked($sandboxConfig['enabled']); ?>>
|
|
992
|
-
<strong><?php esc_html_e('Enable
|
|
1211
|
+
<strong><?php esc_html_e('Enable sandbox mode', 'anonympins-bot-mitigation-pow'); ?></strong>
|
|
993
1212
|
</label>
|
|
994
1213
|
</td>
|
|
995
1214
|
</tr>
|
|
996
1215
|
<tr>
|
|
997
|
-
<th scope="row"><?php esc_html_e('Behavior &
|
|
1216
|
+
<th scope="row"><?php esc_html_e('Behavior & enforcement', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
998
1217
|
<td>
|
|
999
1218
|
<label>
|
|
1000
1219
|
<input type="checkbox" name="sandbox_audit_only" value="1" <?php checked($sandboxConfig['audit_only']); ?>>
|
|
1001
|
-
<strong><?php esc_html_e('Audit
|
|
1220
|
+
<strong><?php esc_html_e('Audit only / dry-run (never block or challenge visitors)', 'anonympins-bot-mitigation-pow'); ?></strong>
|
|
1002
1221
|
</label><br><br>
|
|
1003
1222
|
<label>
|
|
1004
1223
|
<input type="checkbox" name="sandbox_add_headers" value="1" <?php checked($sandboxConfig['add_headers']); ?>>
|
|
@@ -1011,7 +1230,7 @@ function fingerprint_render_admin_page(): void {
|
|
|
1011
1230
|
</td>
|
|
1012
1231
|
</tr>
|
|
1013
1232
|
<tr>
|
|
1014
|
-
<th scope="row"><label for="sandbox_ip_filter"><?php esc_html_e('Test IP
|
|
1233
|
+
<th scope="row"><label for="sandbox_ip_filter"><?php esc_html_e('Test IP whitelist filter', 'anonympins-bot-mitigation-pow'); ?></label></th>
|
|
1015
1234
|
<td>
|
|
1016
1235
|
<?php
|
|
1017
1236
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
|
@@ -1026,32 +1245,32 @@ function fingerprint_render_admin_page(): void {
|
|
|
1026
1245
|
</td>
|
|
1027
1246
|
</tr>
|
|
1028
1247
|
</table>
|
|
1029
|
-
<?php submit_button(esc_html__('Save
|
|
1248
|
+
<?php submit_button(esc_html__('Save sandbox settings', 'anonympins-bot-mitigation-pow'), 'primary', 'fingerprint_save_sandbox'); ?>
|
|
1030
1249
|
</form>
|
|
1031
1250
|
<hr>
|
|
1032
1251
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
|
|
1033
|
-
<h3 style="margin:0;"><span class="dashicons dashicons-shield" style="vertical-align:text-bottom;"></span> <?php esc_html_e('Live
|
|
1252
|
+
<h3 style="margin:0;"><span class="dashicons dashicons-shield" style="vertical-align:text-bottom;"></span> <?php esc_html_e('Live challenged visitors & suspicion monitor', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
1034
1253
|
<div>
|
|
1035
|
-
<span style="font-weight:600;color:#646970;"><?php esc_html_e('Recent
|
|
1254
|
+
<span style="font-weight:600;color:#646970;"><?php esc_html_e('Recent challenges:', 'anonympins-bot-mitigation-pow'); ?></span>
|
|
1036
1255
|
<span id="live-challenges-count" style="display:inline-block;padding:2px 8px;background:#2271b1;color:#fff;border-radius:10px;font-weight:bold;font-size:12px;">0</span>
|
|
1037
1256
|
</div>
|
|
1038
1257
|
</div>
|
|
1039
1258
|
<div style="background:#f6f7f7;padding:16px;border:1px solid #c3c4c7;border-radius:4px;margin-bottom:15px;">
|
|
1040
1259
|
<div style="display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap;">
|
|
1041
1260
|
<button type="button" class="button button-primary" id="btn-toggle-sse" onclick="fingerprintToggleSSE();">
|
|
1042
|
-
<span class="dashicons dashicons-controls-play" style="vertical-align:middle;"></span> <span id="sse-btn-text"><?php esc_html_e('Start
|
|
1261
|
+
<span class="dashicons dashicons-controls-play" style="vertical-align:middle;"></span> <span id="sse-btn-text"><?php esc_html_e('Start real-time SSE stream', 'anonympins-bot-mitigation-pow'); ?></span>
|
|
1043
1262
|
</button>
|
|
1044
1263
|
<button type="button" class="button button-secondary" onclick="fingerprintFetchTelemetry();">
|
|
1045
|
-
<span class="dashicons dashicons-update" style="vertical-align:middle;"></span> <?php esc_html_e('Poll
|
|
1264
|
+
<span class="dashicons dashicons-update" style="vertical-align:middle;"></span> <?php esc_html_e('Poll now (REST)', 'anonympins-bot-mitigation-pow'); ?>
|
|
1046
1265
|
</button>
|
|
1047
1266
|
<button type="button" class="button button-secondary" onclick="fingerprintClearFeed();">
|
|
1048
|
-
<span class="dashicons dashicons-trash" style="vertical-align:middle;"></span> <?php esc_html_e('Clear
|
|
1267
|
+
<span class="dashicons dashicons-trash" style="vertical-align:middle;"></span> <?php esc_html_e('Clear feed', 'anonympins-bot-mitigation-pow'); ?>
|
|
1049
1268
|
</button>
|
|
1050
1269
|
<span id="sse-connection-status" style="font-weight:bold;color:#646970;"></span>
|
|
1051
1270
|
</div>
|
|
1052
1271
|
<div style="display:grid;grid-template-columns: 1.4fr 1fr;gap:16px;">
|
|
1053
1272
|
<div style="background:#fff;padding:12px;border:1px solid #dcdcde;border-radius:4px;overflow-x:auto;">
|
|
1054
|
-
<h4 style="margin:0 0 8px 0;"><?php esc_html_e('Challenged
|
|
1273
|
+
<h4 style="margin:0 0 8px 0;"><?php esc_html_e('Challenged visitors stream', 'anonympins-bot-mitigation-pow'); ?></h4>
|
|
1055
1274
|
<table class="wp-list-table widefat fixed striped" style="font-size:12px;">
|
|
1056
1275
|
<thead>
|
|
1057
1276
|
<tr>
|
|
@@ -1070,7 +1289,7 @@ function fingerprint_render_admin_page(): void {
|
|
|
1070
1289
|
</div>
|
|
1071
1290
|
<div style="background:#fff;padding:14px;border:1px solid #dcdcde;border-radius:4px;">
|
|
1072
1291
|
<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px;">
|
|
1073
|
-
<h4 style="margin:0;"><?php esc_html_e('Inspection &
|
|
1292
|
+
<h4 style="margin:0;"><?php esc_html_e('Inspection & suspicion vector', 'anonympins-bot-mitigation-pow'); ?></h4>
|
|
1074
1293
|
<div id="live-score-val" style="font-size:28px;font-weight:bold;color:#2271b1;line-height:1;">--</div>
|
|
1075
1294
|
</div>
|
|
1076
1295
|
<div id="live-score-sub" style="font-size:12px;color:#646970;background:#f6f7f7;padding:8px;border-radius:3px;margin-bottom:10px;">
|
|
@@ -1085,15 +1304,15 @@ function fingerprint_render_admin_page(): void {
|
|
|
1085
1304
|
</div>
|
|
1086
1305
|
|
|
1087
1306
|
<div id="tab-metrics" class="fingerprint-tab-content" style="display:none;background:#fff;padding:20px;border:1px solid #ccd0d4;border-top:none;">
|
|
1088
|
-
<h3><?php esc_html_e('Behavioral &
|
|
1307
|
+
<h3><?php esc_html_e('Behavioral & transport indicator weights', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
1089
1308
|
<form method="post" action="">
|
|
1090
1309
|
<?php wp_nonce_field('fingerprint_settings_nonce', 'fingerprint_nonce'); ?>
|
|
1091
1310
|
<table class="wp-list-table widefat fixed striped">
|
|
1092
1311
|
<thead>
|
|
1093
1312
|
<tr>
|
|
1094
|
-
<th style="width:280px;"><?php esc_html_e('Indicator /
|
|
1095
|
-
<th style="width:120px;"><?php esc_html_e('Current
|
|
1096
|
-
<th><?php esc_html_e('Description &
|
|
1313
|
+
<th style="width:280px;"><?php esc_html_e('Indicator / metric', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
1314
|
+
<th style="width:120px;"><?php esc_html_e('Current weight', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
1315
|
+
<th><?php esc_html_e('Description & detection role', 'anonympins-bot-mitigation-pow'); ?></th>
|
|
1097
1316
|
</tr>
|
|
1098
1317
|
</thead>
|
|
1099
1318
|
<tbody>
|
|
@@ -1107,13 +1326,33 @@ function fingerprint_render_admin_page(): void {
|
|
|
1107
1326
|
</tbody>
|
|
1108
1327
|
</table>
|
|
1109
1328
|
<p style="margin-top:16px;">
|
|
1110
|
-
<?php submit_button(esc_html__('Update
|
|
1329
|
+
<?php submit_button(esc_html__('Update metric weights', 'anonympins-bot-mitigation-pow'), 'primary', 'fingerprint_save_settings', false); ?>
|
|
1111
1330
|
</p>
|
|
1112
1331
|
</form>
|
|
1113
1332
|
</div>
|
|
1114
1333
|
|
|
1115
1334
|
<div id="tab-prometheus" class="fingerprint-tab-content" style="display:none;background:#fff;padding:20px;border:1px solid #ccd0d4;border-top:none;">
|
|
1116
|
-
<h3><?php esc_html_e('Real-
|
|
1335
|
+
<h3><?php esc_html_e('Real-time prometheus metrics', 'anonympins-bot-mitigation-pow'); ?></h3>
|
|
1336
|
+
<form method="post" action="" style="margin-bottom:20px;">
|
|
1337
|
+
<?php wp_nonce_field('fingerprint_prometheus_nonce', 'fingerprint_nonce_prometheus'); ?>
|
|
1338
|
+
<table class="form-table">
|
|
1339
|
+
<tr>
|
|
1340
|
+
<th scope="row"><label for="metrics_endpoint"><?php esc_html_e('Metrics endpoint path', 'anonympins-bot-mitigation-pow'); ?></label></th>
|
|
1341
|
+
<td>
|
|
1342
|
+
<input type="text" name="metrics_endpoint" id="metrics_endpoint" value="<?php echo esc_attr(fingerprint_get_metrics_endpoint()); ?>" class="regular-text" placeholder="/metrics">
|
|
1343
|
+
<p class="description">
|
|
1344
|
+
<?php esc_html_e('Custom path for direct Prometheus scraping (default: /metrics). Leave empty to disable direct path scraping.', 'anonympins-bot-mitigation-pow'); ?>
|
|
1345
|
+
</p>
|
|
1346
|
+
<?php if (fingerprint_get_metrics_endpoint() !== ''): ?>
|
|
1347
|
+
<p class="description">
|
|
1348
|
+
<strong><?php esc_html_e('Scrape URL:', 'anonympins-bot-mitigation-pow'); ?></strong> <code><?php echo esc_html(home_url(fingerprint_get_metrics_endpoint())); ?></code>
|
|
1349
|
+
</p>
|
|
1350
|
+
<?php endif; ?>
|
|
1351
|
+
</td>
|
|
1352
|
+
</tr>
|
|
1353
|
+
</table>
|
|
1354
|
+
<?php submit_button(esc_html__('Save prometheus settings', 'anonympins-bot-mitigation-pow'), 'primary', 'fingerprint_save_prometheus'); ?>
|
|
1355
|
+
</form>
|
|
1117
1356
|
<textarea readonly style="width:100%;height:380px;font-family:monospace;background:#f6f7f7;padding:12px;"><?php echo esc_textarea($prometheusRaw); ?></textarea>
|
|
1118
1357
|
</div>
|
|
1119
1358
|
</div>
|