CCCPaste

ClickCease WP plugin audit - DB usage

 Autoload — The Primary Problem

  None of the update_option() or add_option() calls ever pass false as the autoload argument. WordPress defaults to autoload = 'yes', which means all ~17 plugin
  options are loaded on every single page request as part of WordPress's initial options bootstrap query — even on pages where the plugin does nothing with them.

  That's roughly 17 extra rows in the autoload payload that every visitor triggers, regardless of whether the plugin runs.

  ---
  Per-Request Read Pattern (Non-Admin Users)

  Every non-admin, non-whitelisted page request fires clickcease_server_validation() via the send_headers hook. That function reads:

  clickcease_whitelist          (constructor, line 39)
  clickcease_api_key            (line 103)
  clickcease_domain_key         (line 105)
  clickcease_bot_zapping_authenticated  (line 106)
  clickcease_secret_key         (line 107)
  cheq_invalid_secret           (line 108)
  monitoring                    (line 109)
  cc_version_updated            (line 147)

  8 get_option() calls per page load. WordPress's in-memory object cache means most of these hit memory after the first one, but only if the autoloaded options
  were already cached in that request — which they are. So this part is largely benign in practice, but it's unnecessary work since all these values were already
  bulk-loaded.

  ---
  The Write Problem — Multiple update_option() Calls Per Request

  update_option() always writes to the database — it bypasses the object cache. The cheq_invalid_secret option can be written 3 times in a single request through
  the RTI auth flow:

  // rtiService.php:152 or 155 — written once during cookie check
  update_option('cheq_invalid_secret', true/false);

  // rtiService.php:224 or 226 — written again during hash validation
  update_option('cheq_invalid_secret', true/false);

  // rtiService.php:232 or 237 — written a third time on output length check
  update_option('cheq_invalid_secret', true/false);

  Each is a separate UPDATE wp_options SET option_value = ... WHERE option_name = 'cheq_invalid_secret' — 3 round trips to the DB that could be 1, or ideally 0
  (compute the final value once, write once).

  ---
  Timestamps Stored as Regular Options

  The rate-limiting timestamps (cc-monitoring-date, cc_white_list_send_date, cc-active-domain, cc_send_plugin_latest_version) are stored as plain options —
  autoloaded by default. WordPress transients exist specifically for this pattern and would be more appropriate: they carry TTL semantics natively, they can be
  stored in an external object cache (Memcached/Redis) instead of the DB, and they self-clean. Using raw options for time-gating is reinventing a worse version of
  transients.

  ---
  Redundant Reads of the Same Keys

  clickcease_bot_zapping_authenticated is read at lines 39, 74, 106, 183, 216, 354, 368 — 7 times across a single request's potential call stack. The object cache
  means these don't all hit the DB, but it signals the plugin has no local variable caching strategy. A single read into a class property at construction time
  would serve all downstream calls.

  Similarly, clickcease_api_key and clickcease_domain_key are each read in clickcease_server_validation(), then read again separately inside
  update_monitoring_status() (lines 341–342) even though they were already retrieved earlier in the same request.

  ---
  Summary of Impact

  ┌─────────────────────────────────────────────┬─────────────────────────────────────────────────────────────┐
  │                    Issue                    │                           Impact                            │
  ├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
  │ All 17 options autoloaded                   │ Adds to the autoload query on every page request, site-wide │
  ├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
  │ No autoload = false on any option           │ Can't opt out without code change                           │
  ├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
  │ cheq_invalid_secret written 3x per request  │ 2 unnecessary DB writes per RTI auth call                   │
  ├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
  │ Timestamps as options instead of transients │ Can't be offloaded to object cache; always hit DB           │
  ├─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
  │ Redundant get_option() calls                │ Benign with object cache, but noisy and fragile             │
  └─────────────────────────────────────────────┴─────────────────────────────────────────────────────────────┘

  On a low-traffic site this is invisible. On a high-traffic site with many concurrent visitors, the repeated writes and the autoload bloat compound — especially
  since the send_headers hook fires early, before any output buffering, meaning these DB interactions happen synchronously on the critical path for every page
  load.