wp_create_nonce(self::NONCE_ACTION),
'ajaxUrl' => admin_url('admin-ajax.php'),
]);
}
/* ── Actions (form submissions) ───────────────────────────── */
public function handle_actions(): void {
if (!isset($_POST['itk_action']) || !check_admin_referer(self::NONCE_ACTION)) return;
if (!current_user_can('manage_options')) wp_die('Unauthorized');
switch ($_POST['itk_action']) {
case 'clear_bot_log':
ITK_Database::clear_bot_log();
$this->redirect(['tab' => 'bot-logs', 'cleared' => 1]);
break;
case 'clear_honeypot_log':
ITK_Database::clear_honeypot_log();
$this->redirect(['tab' => 'honeypot-logs', 'cleared' => 1]);
break;
case 'save_settings_security':
$this->save_settings_form('itk_security', [
'response_code', 'redirect_url', 'custom_message',
], [
'log_blocked_attempts',
]);
$this->redirect(['tab' => 'bot-blocker', 'saved' => 1]);
break;
case 'save_bot_api':
$this->save_api_settings(ITK_Bot_API::OPT_SETTINGS, 'itk_bot_api_settings');
$this->redirect(['tab' => 'bot-blocker', 'saved' => 1]);
break;
case 'save_hp_api':
$this->save_api_settings(ITK_HP_API::OPT_SETTINGS, 'itk_hp_api_settings');
$this->redirect(['tab' => 'honeypot', 'saved' => 1]);
break;
case 'test_bot_api':
$result = ITK_Bot_API::test_connection();
$s = ITK_Bot_API::settings();
$s['connection_ok'] = $result['ok']; $s['last_verified'] = time();
$s['last_error'] = $result['ok'] ? '' : $result['message'];
update_option(ITK_Bot_API::OPT_SETTINGS, $s);
set_transient('itk_bot_api_test_result', $result, 60);
$this->redirect(['tab' => 'bot-blocker', 'api_tested' => 1]);
break;
case 'test_hp_api':
$result = ITK_HP_API::test_connection();
$s = ITK_HP_API::settings();
$s['connection_ok'] = $result['ok']; $s['last_verified'] = time();
$s['last_error'] = $result['ok'] ? '' : $result['message'];
update_option(ITK_HP_API::OPT_SETTINGS, $s);
set_transient('itk_hp_api_test_result', $result, 60);
$this->redirect(['tab' => 'honeypot', 'api_tested' => 1]);
break;
case 'flush_bot_api':
ITK_Bot_API::flush();
$this->redirect(['tab' => 'bot-blocker', 'api_flushed' => 1]);
break;
case 'flush_hp_api':
ITK_HP_API::flush();
$this->redirect(['tab' => 'honeypot', 'api_flushed' => 1]);
break;
case 'send_bot_history':
$result = ITK_Bot_API::send_history_batch();
set_transient('itk_bot_history_result', $result, 60);
$this->redirect(['tab' => 'bot-blocker', 'history_sent' => 1]);
break;
case 'send_hp_history':
$result = ITK_HP_API::send_history_batch();
set_transient('itk_hp_history_result', $result, 60);
$this->redirect(['tab' => 'honeypot', 'history_sent' => 1]);
break;
case 'reset_bot_history':
delete_option('itk_bot_history_last_id');
delete_option('itk_bot_history_sent');
$this->redirect(['tab' => 'bot-blocker']);
break;
case 'reset_hp_history':
delete_option('itk_hp_history_last_id');
delete_option('itk_hp_history_sent');
$this->redirect(['tab' => 'honeypot']);
break;
case 'save_settings_login':
$this->save_settings_form('itk_security', [
'custom_login_slug',
], [
'enable_custom_login',
]);
$this->redirect(['tab' => 'protection', 'saved' => 1]);
break;
case 'save_settings_honeypot':
$this->save_settings_form('itk_honeypot', [
'min_time', 'max_time', 'retain_days',
], []);
$this->redirect(['tab' => 'honeypot', 'saved' => 1]);
break;
case 'save_settings_waf':
$this->save_settings_form('itk_waf', [
'action', 'response_code', 'redirect_url', 'custom_message',
], [
'enabled', 'log_attacks', 'scan_post', 'scan_cookies', 'scan_ua',
'block_sqli', 'block_xss', 'block_lfi', 'block_rfi', 'block_cmdi',
'block_xxe', 'block_php_inject', 'block_ssrf', 'block_wp_specific',
]);
(new ITK_WAF())->invalidate_cache();
$this->redirect(['tab' => 'waf', 'saved' => 1]);
break;
case 'clear_attack_log':
ITK_Database::clear_attack_log();
$this->redirect(['tab' => 'attack-logs', 'cleared' => 1]);
break;
case 'save_attacks_api':
$this->save_api_settings(ITK_Attacks_API::OPT_SETTINGS, 'itk_attacks_api_settings');
$this->redirect(['tab' => 'waf', 'saved' => 1]);
break;
case 'test_attacks_api':
$result = ITK_Attacks_API::test_connection();
$s = ITK_Attacks_API::settings();
$s['connection_ok'] = $result['ok']; $s['last_verified'] = time();
$s['last_error'] = $result['ok'] ? '' : $result['message'];
update_option(ITK_Attacks_API::OPT_SETTINGS, $s);
set_transient('itk_attacks_api_test_result', $result, 60);
$this->redirect(['tab' => 'waf', 'api_tested' => 1]);
break;
case 'flush_attacks_api':
ITK_Attacks_API::flush();
$this->redirect(['tab' => 'waf', 'api_flushed' => 1]);
break;
case 'send_attacks_history':
$result = ITK_Attacks_API::send_history_batch();
set_transient('itk_attacks_history_result', $result, 60);
$this->redirect(['tab' => 'waf', 'history_sent' => 1]);
break;
case 'reset_attacks_history':
delete_option('itk_attacks_history_last_id');
delete_option('itk_attacks_history_sent');
$this->redirect(['tab' => 'waf']);
break;
}
}
/**
* Save a subset of fields from $_POST['itk_*'] into a WP option.
* $text_fields = scalar fields (sanitize_text_field)
* $toggle_fields = checkbox fields (0 or 1)
*/
private function save_settings_form(string $option, array $text_fields, array $toggle_fields): void {
$opts = get_option($option, []);
$posted = $_POST[$option] ?? [];
foreach ($text_fields as $key) {
if (isset($posted[$key])) {
$opts[$key] = sanitize_text_field(wp_unslash($posted[$key]));
}
}
foreach ($toggle_fields as $key) {
$opts[$key] = !empty($posted[$key]) ? 1 : 0;
}
update_option($option, $opts);
}
private function save_api_settings(string $option_key, string $post_key): void {
$cur = get_option($option_key, []);
$posted = $_POST[$post_key] ?? [];
$new_url = esc_url_raw(trim($posted['api_url'] ?? ''));
$changed = $new_url !== ($cur['api_url'] ?? '') || (!empty($posted['api_token']) && $posted['api_token'] !== ($cur['api_token'] ?? ''));
update_option($option_key, array_merge($cur, [
'enabled' => !empty($posted['enabled']),
'api_url' => $new_url,
'api_token' => sanitize_text_field($posted['api_token'] ?? ($cur['api_token'] ?? '')),
'connection_ok' => $changed ? null : ($cur['connection_ok'] ?? null),
'last_verified' => $changed ? 0 : ($cur['last_verified'] ?? 0),
'last_error' => $changed ? '' : ($cur['last_error'] ?? ''),
]));
}
/* ── AJAX: test API connection ─────────────────────────────── */
public function ajax_test_api(): void {
check_ajax_referer(self::NONCE_ACTION, 'nonce');
if (!current_user_can('manage_options')) wp_send_json_error('unauthorized');
$which = sanitize_key($_POST['api'] ?? '');
$result = match ($which) {
'bot' => ITK_Bot_API::test_connection(),
'attacks' => ITK_Attacks_API::test_connection(),
default => ITK_HP_API::test_connection(),
};
wp_send_json($result);
}
/* ── AJAX: flush API queue ─────────────────────────────────── */
public function ajax_flush_api_queue(): void {
check_ajax_referer(self::NONCE_ACTION, 'nonce');
if (!current_user_can('manage_options')) wp_send_json_error('unauthorized');
$which = sanitize_key($_POST['api'] ?? '');
match ($which) {
'bot' => ITK_Bot_API::flush(),
'attacks' => ITK_Attacks_API::flush(),
default => ITK_HP_API::flush(),
};
wp_send_json_success('Queue flushed.');
}
private function redirect(array $args): void {
wp_redirect(add_query_arg(array_merge(['page' => self::MENU_SLUG], $args), admin_url('options-general.php')));
exit;
}
/* ── AJAX: save single toggle setting ─────────────────────── */
public function ajax_save_setting(): void {
check_ajax_referer(self::NONCE_ACTION, 'nonce');
if (!current_user_can('manage_options')) wp_send_json_error('unauthorized');
$option = sanitize_key($_POST['option'] ?? '');
$setting = sanitize_key($_POST['setting'] ?? '');
$value = (int)($_POST['value'] ?? 0);
$allowed = ['itk_security','itk_optimization','itk_honeypot','itk_waf'];
if (!in_array($option, $allowed, true) || empty($setting)) {
wp_send_json_error('invalid');
}
$opts = get_option($option, []);
$opts[$setting] = $value;
update_option($option, $opts);
wp_send_json_success();
}
/* ── AJAX: save config file ───────────────────────────────── */
public function ajax_save_config_file(): void {
check_ajax_referer(self::NONCE_ACTION, 'nonce');
if (!current_user_can('manage_options')) wp_send_json_error('unauthorized');
$file = sanitize_key($_POST['file'] ?? '');
$content = wp_unslash($_POST['content'] ?? '');
$allowed = [
'badbots' => ITK_PATH . 'config/badbots.conf',
'goodbots' => ITK_PATH . 'config/goodbots.conf',
'referrers' => ITK_PATH . 'config/referrers.conf',
'networks' => ITK_PATH . 'config/networks.conf',
'allowed-ips'=> ITK_PATH . 'config/allowed-ips.conf',
'whitelist' => ITK_PATH . 'config/whitelist.conf',
];
if (!isset($allowed[$file])) wp_send_json_error('invalid file');
$result = file_put_contents($allowed[$file], sanitize_textarea_field($content));
if ($result === false) {
wp_send_json_error('write failed');
}
// Clear transient caches
delete_transient('itk_bots_list');
delete_transient('itk_referrers_list');
delete_transient('itk_networks_list');
delete_transient('itk_goodbots_list');
ITK_Whitelist::invalidate_cache();
wp_send_json_success();
}
/* ── Main page render ─────────────────────────────────────── */
public function render_page(): void {
if (!current_user_can('manage_options')) return;
$tab = sanitize_key($_GET['tab'] ?? 'dashboard');
?>
IQ InformatiQ Toolkit
Logs cleared successfully.
$this->tab_dashboard(),
'bot-blocker' => $this->tab_bot_blocker(),
'protection' => $this->tab_protection(),
'optimization' => $this->tab_optimization(),
'honeypot' => $this->tab_honeypot(),
'waf' => $this->tab_waf(),
'bot-logs' => $this->tab_bot_logs(),
'honeypot-logs' => $this->tab_honeypot_logs(),
'attack-logs' => $this->tab_attack_logs(),
'config-files' => $this->tab_config_files(),
default => $this->tab_dashboard(),
};
?>
= number_format($bot_stats['total']) ?>
Total Blocked
= number_format($bot_stats['today']) ?>
Today
= number_format($bot_stats['rate_limited']) ?>
Rate Limited
= number_format($hp_stats['total']) ?>
Honeypot Catches
= number_format($hp_stats['today']) ?>
Honeypot Today
TOP THREAT SOURCES
cnt);
foreach ($bot_stats['top_bot_types'] as $row):
$pct = round(($row->cnt / $max) * 100);
?>
= esc_html($row->bot_type ?: 'Unknown') ?>
= number_format($row->cnt) ?>
HONEYPOT – TOP TARGETED FORMS
cnt);
foreach ($hp_stats['top_forms'] as $row):
$pct = round(($row->cnt / $max) * 100);
?>
= esc_html($row->form_type) ?>
= number_format($row->cnt) ?>
ACTIVE MODULES
!empty($sec['block_malicious_bots']),
'OpenAI Block' => !empty($sec['block_openai_bots']),
'Good Bot Rate Limit'=> !empty($sec['rate_limit_good_bots']),
'Network Block' => !empty($sec['block_bad_networks']),
'Login Protection' => !empty($sec['protect_wp_login']),
'Security Headers' => !empty($sec['add_security_headers']),
'Block XML-RPC' => !empty($sec['block_xmlrpc']),
'Honeypot' => !empty($hp['enabled']),
'Remove WP Version' => !empty($opt['remove_wp_version']),
'Disable Emoji' => !empty($opt['remove_emoji']),
'Admin Branding' => !empty($opt['admin_branding']),
];
foreach ($modules as $label => $active):
?>
= esc_html($label) ?>
= $active ? 'ACTIVE' : 'off' ?>
Bot Blocking
['OpenAI / GPT Bots', 'Block GPTBot, ChatGPT-User, OAI-SearchBot'],
'block_malicious_bots' => ['Malicious Bots', 'Block bots listed in badbots.conf'],
'block_bad_referrers' => ['Bad Referrers', 'Block requests from spam referrer domains'],
'block_bad_networks' => ['Bad Networks', 'Block IP ranges listed in networks.conf'],
'rate_limit_good_bots' => ['Rate-Limit Good Bots', 'Apply crawl-rate limits to Googlebot, Bingbot, etc. (configurable in goodbots.conf)'],
];
foreach ($toggles as $key => [$label, $desc]):
$this->render_toggle('itk_security', $key, $label, $desc, $opts);
endforeach;
?>
Central Bot API
Send blocked-bot events to your self-hosted Bot Intelligence Docker stack (port 3001).
= esc_html($bot_lbl) ?>
0): ?>
Last tested = esc_html(human_time_diff((int)$bot_api['last_verified'])) ?> ago
= esc_html($bot_api['last_error']) ?>
= esc_html($bot_test_r['message']) ?>
= esc_html($bot_hist_r['message']) ?>
WordPress Protection
['WP Login IP Whitelist', 'Restrict wp-login.php to IPs in allowed-ips.conf'],
'add_security_headers' => ['Security Headers', 'Add X-Content-Type-Options, X-Frame-Options, X-XSS-Protection headers'],
'protect_wp_includes' => ['Protect WP Core Files', 'Block direct access to wp-includes, wp-admin/includes'],
'protect_uploads' => ['Block PHP in Uploads', 'Deny PHP file access and uploads in /wp-content/uploads/'],
'block_xmlrpc' => ['Block XML-RPC', 'Deny all access to xmlrpc.php'],
'block_malicious_queries' => ['Block Malicious Queries', 'Detect and block SQLi, XSS, and command injection in query strings'],
'block_author_scans' => ['Block Author Scans', 'Redirect ?author=N requests to prevent username enumeration'],
];
foreach ($toggles as $key => [$label, $desc]):
$this->render_toggle('itk_security', $key, $label, $desc, $opts);
endforeach;
?>
Performance & Cleanup
['Remove WP Version', 'Strip WordPress version from and all enqueued assets'],
'remove_script_versions' => ['Remove Asset Versions', 'Remove ?ver= query string from CSS/JS URLs'],
'remove_emoji' => ['Remove Emojis', 'Disable WordPress emoji scripts and styles'],
'deregister_wp_embed' => ['Remove WP Embed', 'Deregister the wp-embed script'],
'remove_wp_head_noise' => ['Clean WP Head', 'Remove RSD, wlwmanifest, feed links, and adjacent post links from '],
'limit_revisions' => ['Limit Revisions', 'Keep only 3 post revisions and autosave every 5 minutes'],
'defer_js' => ['Defer JavaScript', 'Add defer attribute to non-critical scripts'],
'limit_heartbeat' => ['Limit Heartbeat', 'Restrict WordPress heartbeat to post editor pages only'],
'stop_empty_search_redirect'=> ['Fix Empty Search', 'Prevent redirect loop on empty search queries'],
'use_google_jquery' => ['Use Google jQuery', 'Load jQuery from Google CDN instead of local'],
'dns_prefetch' => ['DNS Prefetch', 'Enable DNS prefetching via meta header'],
];
foreach ($toggles as $key => [$label, $desc]):
$this->render_toggle('itk_optimization', $key, $label, $desc, $opts);
endforeach;
?>
Security Tweaks
['Hide Login Errors', 'Replace specific login error messages with a generic one'],
'remove_author_class' => ['Remove Author Class', 'Strip admin username from comment CSS classes'],
'remove_default_userfields'=> ['Remove User Fields', 'Remove AIM, Jabber, YIM from user profiles'],
'clean_bad_content' => ['Clean Bad Content', 'Remove empty tags, inline styles, and font tags on save'],
'change_author_base' => ['Change Author URL Base', "Change /author/ to /writer/ in author archive URLs"],
'disable_xml_rpc' => ['Disable XML-RPC (via filter)', 'Filter-based XML-RPC disable (additional to the blocker)'],
];
foreach ($toggles as $key => [$label, $desc]):
$this->render_toggle('itk_optimization', $key, $label, $desc, $opts);
endforeach;
?>
UI / Branding
['Disable Core Widgets', 'Remove WordPress News and WPEngine news dashboard widgets'],
'unregister_default_widgets'=> ['Unregister Sidebar Widgets', 'Remove Calendar, Archives, Meta, Search, Tag Cloud widgets'],
'disable_comments_url' => ['Remove Comment URL Field', 'Hide the website URL field from comment forms'],
'remove_admin_bar_links' => ['Clean Admin Bar', 'Remove WordPress logo, links, and noisy items from the toolbar'],
'admin_branding' => ['InformatiQ Branding', 'Add InformatiQ logo widget, toolbar link, admin notice, and custom footer'],
'disable_floc' => ['Disable FLoC', 'Add Permissions-Policy: interest-cohort=() header'],
'lightbox_images' => ['Lightbox Images', 'Add rel="lightbox" to image links in post content'],
'featured_image_rss' => ['Featured Image in RSS', 'Include featured image in RSS feed entries'],
];
foreach ($toggles as $key => [$label, $desc]):
$this->render_toggle('itk_optimization', $key, $label, $desc, $opts);
endforeach;
?>
Honeypot Fields
['Enable Honeypot', 'Inject invisible honeypot fields into all protected forms'],
'protect_comments' => ['Comments', 'Protect comment forms'],
'protect_login' => ['Login Form', 'Protect wp-login.php login'],
'protect_register' => ['Registration', 'Protect user registration'],
'protect_lost_password' => ['Lost Password', 'Protect lost password form'],
'protect_woocommerce' => ['WooCommerce', 'Protect WooCommerce checkout and registration'],
'protect_cf7' => ['Contact Form 7', 'Protect CF7 forms'],
'protect_elementor' => ['Elementor Forms', 'Protect Elementor Pro form widget'],
'protect_gravity' => ['Gravity Forms', 'Protect Gravity Forms'],
'protect_search' => ['Search Form', 'Protect the WordPress search form'],
];
foreach ($toggles as $key => [$label, $desc]):
$this->render_toggle('itk_honeypot', $key, $label, $desc, $opts);
endforeach;
?>
Central Honeypot API
Send honeypot catch events to your self-hosted Honeypot Intelligence Docker stack (port 3000).
= esc_html($hp_lbl) ?>
0): ?>
Last tested = esc_html(human_time_diff((int)$hp_api['last_verified'])) ?> ago
= esc_html($hp_api['last_error']) ?>
= esc_html($hp_test_r['message']) ?>
= esc_html($hp_hist_r['message']) ?>
Web Application Firewall
['Enable WAF', 'Inspect incoming requests against all active rule categories'],
'block_sqli' => ['SQL Injection', 'Block SQLi patterns in query parameters, POST fields, and URIs'],
'block_xss' => ['Cross-Site Scripting', 'Block XSS payloads including script tags, event handlers, and JS URIs'],
'block_lfi' => ['Local File Inclusion', 'Block path traversal and LFI patterns (../../etc/passwd etc.)'],
'block_rfi' => ['Remote File Inclusion', 'Block attempts to include remote URLs as file paths'],
'block_cmdi' => ['Command Injection', 'Block OS command injection patterns (;, |, backticks, etc.)'],
'block_xxe' => ['XML External Entity', 'Block XXE payloads in request bodies and parameters'],
'block_php_inject' => ['PHP Code Injection', 'Block PHP code execution attempts (eval, base64_decode, etc.)'],
'block_ssrf' => ['SSRF', 'Block Server-Side Request Forgery patterns targeting internal hosts'],
'block_wp_specific' => ['WordPress-Specific','Block WP-targeted probes (xmlrpc abuse, admin enumeration, etc.)'],
];
foreach ($toggles as $key => [$label, $desc]):
$this->render_toggle('itk_waf', $key, $label, $desc, $opts);
endforeach;
?>
Scan Scope & Response
['Scan POST Data', 'Inspect POST body fields for attack patterns'],
'scan_cookies' => ['Scan Cookies', 'Inspect cookie values (adds overhead; off by default)'],
'scan_ua' => ['Scan User-Agent', 'Inspect the User-Agent header'],
'log_attacks' => ['Log Attacks', 'Record matched attacks to the local attack log table'],
];
foreach ($scope_toggles as $key => [$label, $desc]):
$this->render_toggle('itk_waf', $key, $label, $desc, $opts);
endforeach;
?>
Central Attacks API
Send WAF attack events to your self-hosted Attack Intelligence Docker stack (port 3083).
= esc_html($atk_lbl) ?>
0): ?>
Last tested = esc_html(human_time_diff((int)$atk_api['last_verified'])) ?> ago
= esc_html($atk_api['last_error']) ?>
= esc_html($atk_test_r['message']) ?>
= esc_html($atk_hist_r['message']) ?>
self::PER_PAGE, 'offset' => $offset];
if ($search) $args['search'] = $search;
if ($filter_ip) $args['ip'] = $filter_ip;
if ($filter_type) $args['attack_type'] = $filter_type;
$rows = ITK_Database::get_attack_rows($args);
$total = ITK_Database::count_attack_rows($args);
$attack_types = ITK_Database::get_attack_types();
$total_pages = max(1, (int)ceil($total / self::PER_PAGE));
$base_url = admin_url('options-general.php?page=' . self::MENU_SLUG . '&tab=attack-logs');
?>
Showing = count($rows) ?> of = number_format($total) ?> result(s)
| Date / Time | IP | Type |
Source | Param | Payload | URI | Method |
| No attacks logged yet. |
| = esc_html($row->logged_at) ?> |
= esc_html($row->ip_address) ?>
[filter]
[lookup]
|
= esc_html($row->attack_type) ?> |
= esc_html($row->source) ?> |
= esc_html(substr($row->param, 0, 60)) ?> |
= esc_html(substr($row->payload, 0, 80)) ?> |
= esc_html(substr($row->request_uri, 0, 80)) ?> |
= esc_html($row->method) ?> |
render_pager($paged, $total_pages, $base_url); ?>
self::PER_PAGE, 'offset' => $offset];
if ($search) $args['search'] = $search;
if ($filter_ip) $args['ip'] = $filter_ip;
if ($filter_bt) $args['bot_type'] = $filter_bt;
if ($filter_ac) $args['action'] = $filter_ac;
$rows = ITK_Database::get_bot_rows($args);
$total = ITK_Database::count_bot_rows($args);
$bot_types = ITK_Database::get_bot_types();
$total_pages= max(1, (int)ceil($total / self::PER_PAGE));
$base_url = admin_url('options-general.php?page=' . self::MENU_SLUG . '&tab=bot-logs');
?>
Showing = count($rows) ?> of = number_format($total) ?> result(s)
| Date / Time | IP | Bot Type |
Action | Reason | URI | User Agent |
| No bot activity logged yet. |
action === 'rate_limited' ? 'itk-badge-warn' : 'itk-badge-block';
?>
| = esc_html($row->logged_at) ?> |
= esc_html($row->ip_address) ?>
[filter]
[lookup]
|
= esc_html($row->bot_type) ?> |
= esc_html($row->action) ?> |
= esc_html($row->reason) ?> |
= esc_html(substr($row->request_uri, 0, 80)) ?> |
= esc_html(substr($row->user_agent, 0, 100)) ?> |
render_pager($paged, $total_pages, $base_url); ?>
self::PER_PAGE, 'offset' => $offset];
if ($search) $args['search'] = $search;
if ($filter_ip) $args['ip'] = $filter_ip;
if ($filter_form) $args['form'] = $filter_form;
$rows = ITK_Database::get_honeypot_rows($args);
$total = ITK_Database::count_honeypot_rows($args);
$form_types = ITK_Database::get_honeypot_form_types();
$total_pages = max(1, (int)ceil($total / self::PER_PAGE));
$base_url = admin_url('options-general.php?page=' . self::MENU_SLUG . '&tab=honeypot-logs');
?>
Showing = count($rows) ?> of = number_format($total) ?> result(s)
| Date / Time | IP | Form Type |
Reason | URI | User Agent |
| No honeypot catches yet. |
| = esc_html($row->blocked_at) ?> |
= esc_html($row->ip_address) ?>
[filter]
[lookup]
|
= esc_html($row->form_type) ?> |
= esc_html($row->reason) ?> |
= esc_html(substr($row->request_uri, 0, 80)) ?> |
= esc_html(substr($row->user_agent, 0, 100)) ?> |
render_pager($paged, $total_pages, $base_url); ?>
['Bad Bots', 'config/badbots.conf', 'One bot user-agent substring per line. Lines starting with # are comments.'],
'goodbots' => ['Good Bots', 'config/goodbots.conf', 'Format: BotName|rate_per_minute (0 = always block)'],
'referrers' => ['Bad Referrers', 'config/referrers.conf', 'One domain substring per line.'],
'networks' => ['Bad Networks', 'config/networks.conf', 'One IP or CIDR range per line (e.g. 1.2.3.0/24).'],
'allowed-ips' => ['Allowed IPs', 'config/allowed-ips.conf','IPs/CIDRs allowed to access wp-login.php (one per line).'],
'whitelist' => ['Whitelist', 'config/whitelist.conf', 'IPs, CIDRs, or UA substrings (ua:...) that bypass all restrictions. One per line.'],
];
$active_file = sanitize_key($_GET['file'] ?? 'badbots');
if (!isset($files[$active_file])) $active_file = 'badbots';
[$title, $path, $desc] = $files[$active_file];
$full_path = ITK_PATH . $path;
$content = file_exists($full_path) ? file_get_contents($full_path) : '';
?>
= esc_html($title) ?> = esc_html($path) ?>
= esc_html($desc) ?>
Saved!
';
if ($paged > 1) {
echo '« Prev';
}
echo '' . sprintf('Page %d of %d', $paged, $total_pages) . '';
if ($paged < $total_pages) {
echo 'Next »';
}
echo '';
}
}