PSB Bot Traffic Controller

描述

PSB Bot Traffic Controller sits in front of your WordPress site and classifies incoming traffic
into five bot categories:

  1. AI training bots
  2. AI assistants
  3. Autonomous AI agents
  4. Search engine crawlers
  5. Scraping agents

Each category can be configured independently to block (HTTP 403), rate-limit (HTTP 429 once
a configurable per-hour threshold is exceeded), or allow traffic. A global safety-net cap also
applies to all detected bot traffic regardless of per-category policy.

Detection runs via a free, zero-dependency regex/User-Agent pattern engine by default, curated
from third-party bot-pattern data (see Credits below). An optional WURFL.js-based detection
engine can be enabled for higher accuracy.

For performance, bot interception runs as early as possible via a small mu-plugin installed
automatically into wp-content/mu-plugins/ — this avoids the cost of a full WordPress
bootstrap for traffic that ends up being blocked.

Credits

The bundled bot-pattern data (includes/detection/regex/data/bot-patterns.php) is built from
three third-party sources, each under a GPL-compatible license:

No code from these projects is bundled — only pattern/name/category data, transformed at build
time (tools/cmd/build-patterns) into this plugin’s own runtime format.

This plugin makes no outbound HTTP requests and does not integrate with any third-party or
external service. Earlier versions of the bundled pattern data included a per-signature
producer field (an attribution URL crediting the upstream source of each entry); it has been
removed entirely, since it was never fetched or otherwise dereferenced by the plugin at
runtime.

屏幕截图

安装

  1. Upload the plugin zip via Plugins > Add New > Upload Plugin, or extract it into wp-content/plugins/psb-bot-traffic-controller/.
  2. Activate the plugin. This installs a small companion file into wp-content/mu-plugins/ (WordPress’s “must-use plugin” mechanism) that performs early bot interception.
  3. Configure per-category policy under Settings > PSB Bot Traffic Controller.

常见问题

Does this replace a WAF or CDN-level bot protection?

No — PSB Bot Traffic Controller runs inside WordPress (or, for the mu-plugin path, as early in
WordPress’s own bootstrap as possible) and cannot intercept traffic before it reaches your web
server. It complements, rather than replaces, edge-level protection.

What happens if the mu-plugin file can’t be written on activation?

An admin notice is shown with the filesystem error. The plugin still activates, but early
interception won’t run until the mu-plugin is successfully installed (retry by
deactivating/reactivating, or check file permissions on wp-content/mu-plugins/).

How do I reset rate-limit counters while testing?

Add define( 'PSB_BTC_DEBUG', true ); to wp-config.php on a staging/test site. This
enables a rotating-token reset URL shown on the Dashboard (valid for a few hours at a time,
then automatically expires). It is disabled by default and should never be enabled on a
production site.

评价

此插件暂无评价。

贡献者及开发者

「PSB Bot Traffic Controller」是开源软件。 以下人员对此插件做出了贡献。

贡献者

更新日志

1.0.2

  • Re-analyzed every remaining automated Plugin Check finding against this version’s actual
    code, rather than restating prior documentation, and applied two genuine fixes found in the
    process (both comment-only, zero behavior change):
  • Added phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared annotations to the
    three interpolated-table-name queries in class-ratelimit-store-db.php
    (increment_and_get(), get_count(), cleanup_expired()) — this file had none, unlike its
    sibling class-ratelimit-reset.php, despite sharing the identical unfixable-by-design
    pattern (table names can never be %s/%d $wpdb->prepare() placeholders).
  • Corrected class-ratelimit-reset.php‘s existing phpcs:ignore comment on its
    TRUNCATE TABLE query: it previously named the sniff WordPress.DB.PreparedSQL.NotPrepared,
    which is not what actually fires there — fixed to name
    WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,
    matching what the checker actually reports, so the comment provides real suppression instead
    of none.

1.0.1

  • Regenerated the icon/banner/logo image assets from an updated source design (ring emblem +
    “BOT TRAFFIC CONTROLLER” wordmark), replacing the placeholder artwork shipped in 1.0.0.
  • Sanitize psb_btc_mu_respond()‘s output (strip control characters before echoing) rather
    than relying on every current call site happening to pass a hardcoded literal — all 3 call
    sites still do today, so this closes a latent gap rather than fixing a live exploit, but
    escaping at the point of output shouldn’t depend on that staying true as the file is edited.
  • Re-reviewed the remaining automated Plugin Check findings against this version and confirmed
    they remain the already-documented false positives / accepted architectural exceptions
    covered in SUBMISSION.md (non-atomic-counter flock() locking has no WP_Filesystem
    equivalent and must run pre-bootstrap; $wpdb->prepare() placeholders the checker’s static
    analysis can’t trace through) — nothing else new to fix.

1.0.0

  • Renamed the plugin from “botpolice” to “PSB Bot Traffic Controller” (slug
    psb-bot-traffic-controller), following a WordPress.org plugin review finding that
    “botpolice” too closely matched an existing project with overlapping functionality.
    Every internal class/function/constant prefix, option/table/transient name, and
    generated mu-plugin filename was renamed to match (Botpolice_* PSB_BTC_*,
    botpolice_* psb_btc_*, BOTPOLICE_* PSB_BTC_*).
  • Removed the producer attribution field from the bundled bot-pattern data
    (includes/detection/regex/data/bot-patterns.php) — it held a URL in most entries and
    was flagged twice by review under two headings (“calling files remotely” and
    “undocumented use of a 3rd party/external service”), even though it was never
    dereferenced as a URL anywhere in the runtime code. Removed entirely rather than
    disclosed, since the plugin makes no outbound HTTP requests and integrates with no
    external service.
  • Changed the mu-plugin’s per-category config cache from an executable
    <?php return array(…); file to plain JSON (now psb-btc-config-cache.json,
    read via json_decode()), per a review finding against writing executable code
    files into wp-content/mu-plugins/ outside the small set of core-defined drop-in
    exceptions. The mu-plugin bootstrap file itself and the (much larger) pattern-data
    cache were not affected — review did not flag either of those.
  • Fixed a real correctness bug review’s automated tooling also caught: both the
    object-cache-backed rate-limit store and the mu-plugin’s flat-file rate-limit
    counters used a non-atomic read-increment-write sequence, letting concurrent
    requests undercount and bypass configured limits. The object-cache store now uses
    wp_cache_add()/wp_cache_incr() (atomic at the cache backend); the mu-plugin’s
    flat-file counters now use flock()-based exclusive locking around the
    read-modify-write.
  • Replaced the compound if ( ! defined('ABSPATH') && ! defined('...STANDALONE') )
    direct-access guard (used to allow PHPUnit to load classes without a full WP
    install) with the literal bare if ( ! defined( 'ABSPATH' ) ) { exit; } review
    expects, across all affected files. Test-only standalone loading now works by
    having the PHPUnit bootstrap itself define ABSPATH, rather than each production
    file special-casing a second constant.

0.4.1

  • Address findings from the official WordPress Plugin Check tool: escape remaining
    unescaped Dashboard output, switch class-mu-installer.php‘s version-stamp read and
    class-ratelimit-reset.php’s counter-file cleanup off raw file-handle/unlink() calls,
    document the intentional nonce-free design of the (opt-in, off-by-default) debug reset
    endpoint, add a direct-access guard to the generated bot-patterns.php (via
    tools/cmd/build-patterns, not by hand-editing the output), and correct “Tested up to”
    to the current WordPress release. See SUBMISSION.md for a list of remaining Plugin
    Check findings that are intentional design choices rather than defects (the
    BOTPOLICE_STANDALONE direct-access guard variant, the mu-plugin’s necessarily-early
    filesystem calls, and the reset endpoint’s token-based-not-nonce-based auth).

0.4.0

  • The rate-limit-reset debug endpoint (?botpolice_reset=<token>) is no longer loaded or
    registered by default — it now requires define( 'BOTPOLICE_DEBUG', true ); in
    wp-config.php to become reachable at all, rather than being an unauthenticated endpoint
    present on every install. uninstall.php also now removes its secret option, if present.
  • Prepare for WordPress.org Plugin Directory submission: tightened the short description
    to fit the directory’s display limit, corrected the “Tested up to” header (was a
    nonexistent “7.0”), added a Screenshots section covering the Settings and Dashboard
    admin pages, added a Credits section disclosing and licensing the bundled bot-pattern
    data’s third-party sources, and fixed composer.json declaring “proprietary” while the
    plugin itself is GPLv2 or later.

0.3.2

  • Fix: the debug rate-limit-reset token (?botpolice_reset=<token>, shown on the
    Dashboard for manual testing) was a single fixed value stored in wp_options
    once leaked (screenshot, shared log, browser history), it stayed valid forever.
    It now rotates automatically every 4 hours: the displayed/accepted token is an
    HMAC-SHA256 of a persistent random secret (never displayed) and the current
    4-hour time window, so a leaked token expires on its own without any manual
    rotation step, and reading the plugin’s source alone is not enough to compute a
    valid token (the algorithm is public; the secret it’s keyed on is not). The
    Dashboard now also shows how many minutes remain before the current URL expires.

0.3.1

  • Add the product logo/emblem to the Settings and Dashboard admin page headers
    (assets/images/logo.png, shipped in the plugin zip) and WordPress.org-listing icon
    and banner assets (.wordpress-org/, not bundled into the plugin zip per WP.org
    convention — those live in the SVN assets/ directory of the plugin listing, separate
    from trunk/).

0.3.0

  • Fix: rate-limiting and category policy keyed on the raw TCP peer address
    (REMOTE_ADDR), which is the reverse proxy’s/load balancer’s own IP on any site
    running behind one (Cloudflare, nginx, AWS ALB/ELB) — collapsing every distinct
    visitor onto one shared rate-limit bucket. Added Botpolice_Client_Ip
    (includes/detection/class-client-ip.php, documented in CLIENT_IP.md), which
    resolves the real public client IP by walking the Forwarded (RFC 7239) and
    X-Forwarded-For headers before falling back to the raw peer address, and only
    ever returns an internet-routable address for rate-limiting purposes. Wired into
    both enforcement layers: Botpolice_Request_Context (full-WP path) and a
    duplicated botpolice_mu_client_ip() in the self-contained mu-plugin hot path.

0.2.1

  • Fix: mu-plugin bootstrap and its pattern-data cache could go stale after an upload-based
    plugin upgrade (WordPress does not re-fire the activation hook on that path), silently
    running old enforcement logic indefinitely. Added Botpolice_Upgrade_Handler, which resyncs
    the mu-plugin on upgrader_process_complete and via a cheap staleness check on every
    admin page load, covering upgrade paths that bypass WordPress’s upgrader (e.g. manual FTP
    replacement) as well.

0.2.0

  • Add per-category, per-action activity recording (Botpolice_Activity_Recorder and the
    mu-plugin’s own inline counter), surfaced on the dashboard’s Recent Activity view.
  • Add a token-gated debug endpoint (?botpolice_reset=<token>) to clear rate-limit counters
    for iterative live-site testing without waiting out hourly buckets.
  • Add a curl-based live end-to-end test suite (tests/live/test-live-site.sh,
    make test-live) verifying block/limit/allow enforcement against a real deployed site.

0.1.1

  • Fix: mu-plugin helper functions were declared conditionally (inside function_exists()
    guards) but called from a closure defined before those declarations executed — PHP only
    hoists unconditional top-level function declarations, so any request matching a bot pattern
    hit a fatal “call to undefined function” error, blocking all traffic on the plugin’s
    highest-risk hot path. Passed a plain php -l lint check without complaint. Caught by a new
    subprocess-based regression suite (tests/php/mu-bootstrap/), since PHPUnit cannot safely
    intercept exit()-calling code in-process.

0.1.0

  • Initial development release: regex/User-Agent detection engine (911 pattern entries across
    5 categories, built from matomo-org/device-detector, crawler-user-agents, and ai.robots.txt),
    policy engine (block/limit/allow per category plus a global rate cap), rate limiter
    (transients or DB-table-backed), mu-plugin-based early interception with a
    WP-API-backed fallback layer, Settings/Dashboard admin UI, and installable-zip packaging.