Skip to content

Performance Console 3.0.4

Latest

Choose a tag to compare

@bfrye26 bfrye26 released this 05 Oct 18:18
7d449cb

A full source audit of Performance Console. No SQL injection, auth bypass or IDOR was found. The defects below are crash, correctness and cost issues.

Existing regression suites pass: 33/33 PHP cases and 7/7 JS cases (5 upstream plus 2 new guards added here). CI is green on PHP 7.4, 8.2, 8.3, 8.4 and Node 22.

Fatal error on hosts without mbstring

19 unguarded mb_substr() calls in the profiler, database health, admin and CLI raised a fatal error the moment they truncated SQL, a file path or an error message. mbstring is optional and is not installed everywhere; CI only ever installed it, which is why this was never caught. All call sites now route through PFC_Utils::truncate(), which falls back to substr().

If you run sites without the mbstring extension, this release fixes a crash that could take down the admin page and CLI.

Halved finding write load

PFC_Utils::issue() did a SELECT and then an INSERT or UPDATE per finding. It is now one INSERT ... ON DUPLICATE KEY UPDATE: a 200-finding scan issues 200 statements instead of 400, and two concurrent requests can no longer both decide a finding is new. The upsert confirms the issue_key UNIQUE index first and falls back to the original read-then-write when it is absent, so occurrence counts cannot silently stop accumulating.

RUM no longer costs every visitor 12 KB

The sampling decision ran in rum.js, so the 8.8 KB web-vitals bundle plus rum.js were enqueued for every anonymous page view while roughly 99.5% of visitors returned immediately. It also multiplied two independent gates (the server sample rate and a client-side roll), so the configured RUM rate was never the rate actually delivered.

The decision now happens server-side with a marker cookie so it survives a page-cache hit, plus pfc_rum_sampled and pfc_rum_rate filters. rum.js honours doNotTrack and Global Privacy Control before collecting anything, retries a rejected beacon once, refreshes the token before expiry, and reports the largest observed value per metric rather than the last.

After upgrading, purge your page cache so visitors receive the new script wiring.

Correctness fixes

  • MariaDB: $wpdb->db_version() returns 5.5.5 on MariaDB because the raw server string is 5.5.5-10.4.28-MariaDB. Comparing that against MariaDB thresholds is always false, so online InnoDB index and table rebuild was permanently unavailable on MariaDB 10.x while the report blamed an "old or unrecognized" server. This is the most consequential fix in the release.
  • Auto-increment exhaustion could only ever fire for one column type: a 1e9 pre-gate ran before the per-type maximum was known, making the tinyint, smallint, mediumint and even bigint branches unreachable. Narrow keys that actually overflow — the ones that stop inserts — are now reported.
  • Frontend stylesheet inventory missed every stylesheet emitted by WordPress core because the regex required href before rel. Bare (unquoted) width, height and loading attributes are now accepted too.
  • RUM percentile: metric_percentile() returned the 600000 ms ingest clamp for the final histogram bucket, so every sample above 10 seconds was reported as exactly ten minutes.
  • Retention: the autoload sample ledger and the change log were the only two stores with no bound; both are now pruned.
  • WP-CLI: --keep-index was never mapped, which made drop_duplicate_index unreachable from the command line even though the admin plan tells large-table operators to use it. Failed repairs also exited 0 with a green "Success" line; they now exit non-zero.
  • REST: ?deep=false started a deep scan, and a missing autoload parameter silently disabled autoload.
  • Jobs: unescaped _ in SHOW TABLES LIKE, and a failed Action Scheduler count was coerced to 0 and reported as a healthy empty queue.
  • Admin UI: a failed backup left the button disabled with no way to retry, and a proxy or WAF error page produced Unexpected token '<' instead of an actionable message.
  • RUM ingest: the signed payload is validated before the rate limit is charged, so a crawler behind a shared NAT address can no longer exhaust the budget real visitors need.

Known remaining items

The backup path has data-integrity risks, and the repair engine has safety-gate gaps, that need a live MySQL/MariaDB to validate safely. They are documented in full in the README rather than silently changed. The most serious is silent column omission in insertable_columns(), which can export a table with zero rows while still certifying the dump as verified.

The plugin also remains diagnostic-only: no object cache, page cache, asset optimisation, Heartbeat or revision control. That is a scope decision rather than an oversight, and is recorded as such.

Install

Download performance-console-3.0.4.zip below and upload it, or replace an existing install. Existing data, settings, diagnostic history and backups are preserved.

SHA-256: CD2475CEBA4A8366C6AD965A603099F76B54A3F4AD6EEFEA6ABDAF683E3A37AB