| 开发者 | paulborile |
|---|---|
| 更新时间 | 2026年8月29日 18:34 |
| PHP版本: | 8.0 及以上 |
| WordPress版本: | 7.1 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
wp-content/mu-plugins/ — this avoids the cost of a full WordPress bootstrap for traffic that ends up being blocked.
wp-content/plugins/psb-bot-traffic-controller/.wp-content/mu-plugins/ (WordPress's "must-use plugin" mechanism) that performs early bot interception.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.
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/).
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.
User-agent/Disallow: / block in
robots.txt, sourced from the same curated bot-name data the detector already uses. Default
mode is dynamic (WordPress's robots_txt filter, nothing written to disk); an advanced,
opt-in setting writes a physical robots.txt file instead, with a persistent warning (Settings
page and Dashboard) whenever a physical file exists on disk, since it always takes precedence
over the dynamic filter regardless of which mode is configured.\n does not get
collapsed into a space. Fixed by joining each paragraph onto one logical line. Docs-only, no
code changes.robots_txt filter
is designed for multiple plugins to extend the same output, so this doesn't change behavior by
default, it just surfaces the interaction. Our filter callback also now registers at a later
priority to reduce the chance of being silently overwritten by another plugin's callback.CLAUDE.md's architecture
notes: WURFL.js is planned, not implemented). Docs-only correction, no code changes.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).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.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.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.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_*).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.<?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.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.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.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).?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.?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.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/).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.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.Botpolice_Activity_Recorder and the
mu-plugin's own inline counter), surfaced on the dashboard's Recent Activity view.?botpolice_reset=<token>) to clear rate-limit counters
for iterative live-site testing without waiting out hourly buckets.tests/live/test-live-site.sh,
make test-live) verifying block/limit/allow enforcement against a real deployed site.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.