Skip to content

v1.2.0 — Signed remote plugin updates

Latest

Choose a tag to compare

@arnevanhoorn arnevanhoorn released this 25 Sep 20:05
7e468fe

Adds a signed REST endpoint so the Kobalt app can update an installed plugin on a reporting site. Every command must carry the site key plus an Ed25519 signature from the app, so a leaked site key alone cannot trigger an update.

Details

  • New endpoint POST /wp-json/plugin-reporter/v1/update-plugin, body {"plugin": "<slug>"}. Updates the plugin to the version WordPress already offers in the update_plugins transient.
  • Auth is layered: site key (X-Reporter-Key) and an Ed25519 signature (X-Reporter-Timestamp, X-Reporter-Nonce, X-Reporter-Signature) over method, route, timestamp, nonce, key hash and body hash.
  • Replay protection: requests older than 5 minutes are rejected; nonces (32 alphanumeric chars) are single-use for 10 minutes. The permission callback never reveals which check failed.
  • Only already-installed plugins with a pending update are touched. Requires direct filesystem access; otherwise returns an error instead of prompting for credentials.
  • Re-activates the plugin if it was active, refills the update transient, and re-sends the daily report right after so the backend sees fresh state.
  • Optional REMOTE_ADDR allowlist via PLUGIN_REPORTER_ALLOWED_IPS. Only REMOTE_ADDR is trusted — leave unset behind a proxy or CDN.
  • Hardening: outgoing report and heartbeat calls now use sslverify => true (was false).

Configuration (wp-config.php)

// Public key from `php artisan reporter:generate-signing-key --show-public`.
// Without a valid key every command is rejected.
define('PLUGIN_REPORTER_COMMAND_PUBLIC_KEY', 'base64-public-key');

// Optional: only accept commands from these REMOTE_ADDR values.
define('PLUGIN_REPORTER_ALLOWED_IPS', '203.0.113.10,203.0.113.11');

Response

{ "updated": true, "plugin": "akismet", "old_version": "5.3", "new_version": "5.4" }