';
self::log_table( $events, false );
echo '';
}
// Activity log.
/**
* Activity log tab.
*/
private static function tab_log() {
global $wpdb;
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only list filters.
$type = isset( $_GET['type'] ) ? sanitize_key( $_GET['type'] ) : '';
$search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
$paged = max( 1, isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 1 );
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$per = 100;
$where = array( '1=1' );
$args = array( CBBG_Store::t_log() );
if ( $type ) {
$where[] = 'type = %s';
$args[] = $type;
}
if ( '' !== $search ) {
$like = '%' . $wpdb->esc_like( $search ) . '%';
$where[] = '(ip LIKE %s OR uri LIKE %s OR ua LIKE %s OR detail LIKE %s)';
array_push( $args, $like, $like, $like, $like );
}
$w = implode( ' AND ', $where );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $w contains only fixed SQL with placeholders; values are passed to prepare().
$total = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE {$w}", $args ) );
$rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM %i WHERE {$w} ORDER BY t DESC, id DESC LIMIT %d OFFSET %d", array_merge( $args, array( $per, ( $paged - 1 ) * $per ) ) ) );
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders, PluginCheck.Security.DirectDB.UnescapedDBParameter
echo '
';
/* translators: %d: number of days. */
echo '' . esc_html( sprintf( __( 'Normal visitors are never logged, only suspicious or blocked traffic. Entries older than %d days are removed.', 'cb-bot-guard' ), (int) CBBG_Settings::get( 'log_retention_days' ) ) ) . '
';
self::f_check( 'enabled', __( 'Protection enabled', 'cb-bot-guard' ), __( 'Turn off to stop all blocking while keeping your lists and settings.', 'cb-bot-guard' ) );
self::f_check( 'cloudflare', __( 'Site is behind Cloudflare', 'cb-bot-guard' ), __( 'Reads the real visitor IP from the CF-Connecting-IP header, only when the request really comes from a Cloudflare server. .htaccess blocking is disabled in this mode because the web server only sees Cloudflare addresses.', 'cb-bot-guard' ) );
echo '
';
self::f_check( 'verify_bots', __( 'Verify Googlebot, Bingbot, Applebot and Yandex', 'cb-bot-guard' ), __( 'Anything claiming to be these crawlers must pass a reverse DNS check (once per IP, using your server\'s own DNS resolver). Impostors are blocked; genuine crawlers are never rate limited.', 'cb-bot-guard' ) );
self::f_check( 'block_empty_ua', __( 'Block requests with no user agent', 'cb-bot-guard' ) );
self::f_check( 'block_bad_ua', __( 'Block scraping tools and attack scanners', 'cb-bot-guard' ) );
self::f_text( 'bad_ua_list', __( 'Scraper and attack user agents', 'cb-bot-guard' ), __( 'One per line, matched anywhere in the user agent, not case sensitive.', 'cb-bot-guard' ), 8 );
self::f_check( 'block_seo_bots', __( 'Block aggressive SEO crawlers', 'cb-bot-guard' ), __( 'Third-party SEO and data crawlers that do not send you visitors. Remove any your SEO team relies on.', 'cb-bot-guard' ) );
self::f_text( 'seo_ua_list', __( 'SEO crawler user agents', 'cb-bot-guard' ), '', 6 );
self::f_check( 'block_ai_bots', __( 'Block AI crawlers', 'cb-bot-guard' ), __( 'Crawlers that collect content for AI models and assistants. Off by default.', 'cb-bot-guard' ) );
self::f_text( 'ai_ua_list', __( 'AI crawler user agents', 'cb-bot-guard' ), '', 6 );
self::f_check( 'probe_block', __( 'Block vulnerability probes', 'cb-bot-guard' ) );
self::f_text( 'probe_patterns', __( 'Probe URL patterns', 'cb-bot-guard' ), __( 'Regular expressions matched against the URL path, one per line.', 'cb-bot-guard' ), 8 );
self::f_check( 'block_xmlrpc', __( 'Block XML-RPC', 'cb-bot-guard' ), __( 'Leave on unless the site uses Jetpack or the WordPress mobile app.', 'cb-bot-guard' ) );
self::f_check( 'honeypot', __( 'Honeypot on comment and registration forms', 'cb-bot-guard' ), __( 'Adds an invisible field that only bots fill in. Works alongside other anti-spam plugins.', 'cb-bot-guard' ) );
self::f_text( 'exempt_paths', __( 'Never check these URLs', 'cb-bot-guard' ), __( 'Requests whose URL contains any of these are skipped by the user agent, probe and rate checks (already-blocked IPs stay blocked). Keep payment gateway callback URLs here so orders are never lost. One per line.', 'cb-bot-guard' ), 5 );
echo '';
echo '
';
self::f_check( 'htaccess_sync', __( 'Also write blocked IPs into .htaccess (Apache)', 'cb-bot-guard' ), __( 'Apache rejects these IPs before PHP starts, so they cost almost nothing. A backup of .htaccess is saved in the plugin\'s uploads folder before every change. Preview the rules under Tools first.', 'cb-bot-guard' ) );
self::f_num( 'log_retention_days', __( 'Keep activity log for', 'cb-bot-guard' ), __( 'days', 'cb-bot-guard' ) );
echo '';
/**
* Lets add-ons print extra settings sections inside the form.
*/
do_action( 'cbbg_settings_sections' );
echo '
';
self::out( self::onoff( $mu ) );
echo ' ' . esc_html__( 'Adds a small file to wp-content/mu-plugins so the blocklist check runs before other plugins and the theme load. A blocked bot then uses a few MB of memory instead of a full page load. It adds no measurable cost for real visitors.', 'cb-bot-guard' ) . '
' . esc_html__( 'The file does nothing while CB Bot Guard is inactive and is removed automatically when you deactivate the plugin.', 'cb-bot-guard' ) . '
';
if ( CBBG_Settings::get( 'cloudflare' ) ) {
echo '
' . esc_html__( 'Not available behind Cloudflare: the web server sees Cloudflare\'s addresses, not your visitors\'. Use Cloudflare\'s own IP Access Rules for edge blocking.', 'cb-bot-guard' ) . '
' . esc_html__( 'If you are ever locked out', 'cb-bot-guard' ) . '
';
echo '
' . esc_html__( 'Add this line to wp-config.php using SFTP or your host\'s file manager. All blocking stops immediately:', 'cb-bot-guard' ) . ' define( \'CBBG_DISABLE\', true );
';
echo '
' . esc_html__( 'If .htaccess blocking was on, delete the lines between "BEGIN CB Bot Guard" and "END CB Bot Guard" in .htaccess.', 'cb-bot-guard' ) . '
';
echo '
' . esc_html__( 'Log in, add your IP under IP Manager as "Always allow", then remove the line from wp-config.php.', 'cb-bot-guard' ) . '
';
echo '
';
}
// Pro.
/**
* Pro tab: a single, non-intrusive overview page.
*/
private static function tab_pro() {
$features = array(
array( __( 'Country and network blocking', 'cb-bot-guard' ), __( 'Block or allow whole countries and hosting networks (ASNs) where your customers never come from.', 'cb-bot-guard' ) ),
array( __( 'Attack alerts', 'cb-bot-guard' ), __( 'Email or Slack alerts when a flood starts, with the top offending IPs.', 'cb-bot-guard' ) ),
array( __( 'Shared blocklist across sites', 'cb-bot-guard' ), __( 'An IP blocked on one of your sites is blocked on all of them.', 'cb-bot-guard' ) ),
array( __( 'Cloudflare firewall sync', 'cb-bot-guard' ), __( 'Push blocked IPs to Cloudflare so they are stopped before reaching your server.', 'cb-bot-guard' ) ),
array( __( 'IP reputation', 'cb-bot-guard' ), __( 'Check visitors against AbuseIPDB and block known bad IPs before they misbehave.', 'cb-bot-guard' ) ),
array( __( 'Weekly reports', 'cb-bot-guard' ), __( 'A weekly email summary of what was blocked and how much load was saved.', 'cb-bot-guard' ) ),
);
echo '
' . esc_html__( 'Everything on the other tabs is free and stays free. Pro is a separate add-on for agencies and busy stores that want more automation.', 'cb-bot-guard' ) . '