Repository navigation
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 theupdate_pluginstransient. - 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
directfilesystem 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_ADDRallowlist viaPLUGIN_REPORTER_ALLOWED_IPS. OnlyREMOTE_ADDRis trusted — leave unset behind a proxy or CDN. - Hardening: outgoing report and heartbeat calls now use
sslverify => true(wasfalse).
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" }