WP Manifestindependent plugin directory
manifest / seo / bot-logger

Bot Logger WP Registry grade D. High-severity findings · 6 findingsWP RegistryHigh-severity findings · 6 findingsOpen the reportD

A Log File Analysis plugin for WordPress

★ 4stars
21release downloads
≈10active sites
v1.7latest · 2 years ago

Install

Download bot-logger-1.7.zip From the v1.7 release on GitHub, Aug 2, 2024

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/mikeciffone/bot-logger/releases/download/v1.7/bot-logger-1.7.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Bot Logger

Author: Mike Ciffone

Bot Logger is a WordPress plugin designed to log and validate incoming requests from search engine bots. It logs Googlebot and Bingbot by default, and allows up to 3 custom user agents. Bot Logger was designed to make log file analysis more convenient and accessible, allowing users to view details usually only shown in access logs such as user agent, request date, resource, status code, and IP address directly in the WordPress admin panel.

This version of the plugin will always be free and open source. You can show your support by linking to it's web page and sharing it with others.

Features

  • Logs requests from Googlebot and Bingbot by default
    • Handles a variety of Google's alternate user agents
  • Ability to import existing access.log data from the server (assuming www-data user has read access)
    • Bot Logger automatically looks in standard access.log paths (eg. /var/log/nginx/) for Nginx, Apache, and Litespeed. Node.js is not supported at this time. -Users can set a custom path in the import page settings.
  • Automatic and manual IP validation against Google's and Bing's IP ranges
  • Ability to track custom user agents (eg. additional search engines, social media apps, scrapers etc)
  • Tabbed interface for viewing logs from different bots
  • Manage retention period and validation frequency in the settings page, as well as manual clearing of log data
  • Cloudflare integration automatically creates a cache-rule for custom user agents to ensure they hit the origin server and are logged

Support

If you encounter any issues or have questions about the plugin, please open an issue on the Issues Page. You can also submit issues using the form on the Bot Logger web page.

License

This plugin is licensed under the GNU General Public License v3.0.

Releases

2 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
v1.7 latest
Aug 2, 2024 2 years ago
bot-logger-1.7.zip
12
May 30, 2024 2 years ago
bot-logger-1.6.zip
9

Active-site estimate ≈10 comes from the median of recent superseded releases. Method.

D grade

Security

Bot Logger 1.7 · audited by WP Registry

High-severity findings.

3 high 3 medium
Audited release
1.7
Findings
6
Worst severity
high
Content hash
e90a19de41d38d03146af8df…

Findings

  • high Unauthenticated Excel export of all bot logs

    information_disclosure

    bl_export_excel is hooked to admin_init and runs whenever export_excel=1 is present. There is no current_user_can, no nonce, and no is_user_logged_in check. WordPress fires admin_init on /wp-admin/admin-post.php for anonymous requests and on /wp-admin/admin-ajax.php after only requiring a non-empty action parameter — both before any page-capability check. A Subscriber can also open /wp-admin/profile.php?export_excel=1 (auth_redirect passes; admin_init runs; the function exit()s before the manage_options menu gate). The handler SELECTs every Googlebot, bingbot, and custom-bot row (IP, URI, user agent, status, validation flag) into PhpSpreadsheet and writes the xlsx to php://output. That is unauthenticated disclosure of data the plugin otherwise confines to a manage_options screen.

    index.php:713-771

    Recommendation

    In bl_export_excel require current_user_can('manage_options') and check_admin_referer (or a dedicated export nonce) before any SELECT or header(). Refuse the request for unauthenticated and low-privilege users; do not hook privileged downloads to admin_init without those gates.

  • high Unauthenticated custom-bot create/delete writes options and Cloudflare cache rules

    missing_auth

    bl_save_custom_bot and bl_remove_custom_bot are admin_init callbacks gated only by isset($_POST['bl_save_custom_bot']) / isset($_POST['bl_remove_custom_bot']). Unauthenticated POST to admin-post.php or admin-ajax.php?action=1 appends a row to bl_custom_bots (tab_name, user_agent, ip_addresses) up to the hardcoded limit of 3, or deletes an index and $wpdb->delete()s matching log rows. If bl_cloudflare_api_token and bl_cloudflare_zone_id are set, save calls get_or_create_cloudflare_ruleset / update_cloudflare_ruleset with the stored Bearer token. update_cloudflare_ruleset interpolates the attacker-controlled user_agent into a Cloudflare expression string: (http.user_agent eq "{$user_agent}"). A payload such as foo") or (true) or (http.user_agent eq "x rewrites the zone's http_request_cache_settings ruleset (e.g. bypass cache for all traffic). Independently, PHP strpos($ua, '') === 0, so an empty custom user_agent matches every front-end request and floods wp_bot_logs. Same handlers are CSRF-able against an administrator because no nonce is checked.

    index.php:525-578

    Recommendation

    Require current_user_can('manage_options') and a matching nonce in both handlers. Reject empty user agents. Stop interpolating user_agent into Cloudflare expressions; pass it through json_encode as a data value or use CF's documented escaping. Do not call the Cloudflare API from an ungated hook.

  • high Unauthenticated trigger of server access-log import

    missing_auth

    bl_handle_import on admin_init treats GET import=1 as a command to call bl_import_access_logs() with no capability or nonce check. The importer fopen()s the default path derived from SERVER_SOFTWARE (/var/log/nginx/access.log, /var/log/apache2/access.log, or /usr/local/lsws/logs/access.log), error_log()s every line, and INSERTs any line whose UA matches Googlebot/bingbot/etc. The plugin README documents this working when the web user can read those files. An unauthenticated client can therefore (1) force a potentially multi-hundred-MB fopen/parse (DoS) and (2) copy bot-matching access-log lines into wp_bot_logs, which the ungated export handler then returns. Reachable at /wp-admin/admin-post.php?import=1 and /wp-admin/admin-ajax.php?action=1&import=1, and by any logged-in user on any wp-admin URL.

    index.php:113-205

    Recommendation

    Delete the admin_init import trigger or protect it with current_user_can('manage_options') plus check_admin_referer. Keep import exclusively inside the manage_options page callback. Cap file size, do not error_log raw log lines, and never fopen a path from the request without an allowlist.

  • medium Settings save and log wipe have no nonce (admin CSRF)

    csrf

    bl_settings_page is the manage_options submenu callback, so a Subscriber cannot submit it directly. Inside the callback, POST bl_save_settings writes bl_retention_days, bl_validation_frequency, bl_delete_data_on_uninstall, bl_cloudflare_api_token, and bl_cloudflare_zone_id with no wp_nonce_field / check_admin_referer, and POST bl_clear_logs calls TRUNCATE TABLE wp_bot_logs. A forged cross-site POST from an administrator's browser overwrites the Cloudflare API token (breaks or redirects the integration), sets retention to 0 (bl_clear_old_logs then deletes essentially all rows), or wipes the log table. Token value is echoed back with esc_attr so this CSRF cannot steal the existing secret, but it can destroy logs and sabotage CDN integration.

    index.php:597-666

    Recommendation

    Add wp_nonce_field to both settings forms and verify with check_admin_referer before any update_option or TRUNCATE. Keep the manage_options menu cap. Do not echo the Cloudflare token into an HTML input; use a write-only placeholder.

  • medium Import form fopen()s an arbitrary path with no nonce

    file_access

    The custom-path branch lives inside bl_import_logs_page (manage_options), so it is not directly Subscriber-reachable. The form has no nonce. POST has_custom_log_path + custom_log_path_value is only passed through sanitize_text_field, stored in bl_custom_log_path, and handed to fopen($access_log_path, 'r'). There is no realpath prefix check, no scheme allowlist, and no restriction to *access.log. A CSRF'd administrator (or a compromised admin session) can cause the web user to read wp-config.php, /etc/passwd, php://filter wrappers, or — if allow_url_fopen is on — a remote URL. Every line is error_log()'d; lines matching the bot UA regex are inserted into wp_bot_logs and then available to the unauthenticated export handler. This is the same fopen sink as the ungated default-path import, with attacker-controlled path.

    index.php:255-265

    Recommendation

    Require a nonce and manage_options. Resolve the path with realpath() and allow only a configured list of suffixes/directories (e.g. */access.log, */access.log.1). Disable URL wrappers (reject ://). Stop error_log'ing raw file contents. Do not persist an attacker-supplied path in an option used later without re-validating it.

  • medium Stored XSS in Remove-bot confirm() via unauthenticated tab_name

    xss_stored

    Custom-bot tab_name is stored with sanitize_text_field (quotes preserved) through the ungated save handler, then interpolated into an HTML onclick handler: confirm('Are you sure you want to remove ' . esc_html($bot['tab_name']) . '?'). esc_html()/ENT_QUOTES encodes an apostrophe as ', but the HTML parser decodes entities before JavaScript runs, so a tab_name of ');alert(document.cookie);// becomes a string break inside the administrator's session. The payload is only output on the custom-bot tab's Remove button (click-to-trigger), but that is the exact control an admin uses to delete a bot they did not create. Nav-tab and <h2> uses of tab_name are esc_html in HTML text and are safe. Log fields rendered in bl_display_log_table are also esc_html.

    index.php:498-500

    Recommendation

    Do not build JavaScript with esc_html. Use esc_js() (or json_encode) inside the JS string, or better attach the name via a data-* attribute and confirm() in botLogger.js. Fix the save handler's missing auth so unauthenticated users cannot plant the payload.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.