Skip to content

Plugin Conflicts Guardian: log blocked activations, blocked updates, and post-update rollbacks to logstash#48565

Merged
arthur791004 merged 7 commits intotrunkfrom
add/pcg-log-blocked-activation
May 7, 2026
Merged

Plugin Conflicts Guardian: log blocked activations, blocked updates, and post-update rollbacks to logstash#48565
arthur791004 merged 7 commits intotrunkfrom
add/pcg-log-blocked-activation

Conversation

@arthur791004
Copy link
Copy Markdown
Contributor

@arthur791004 arthur791004 commented May 6, 2026

Proposed changes

Follow-up from #48553 (review comment): emit logstash events whenever Plugin Conflicts Guardian refuses a bad change or recovers from one, so we can measure how often the various PCG gates catch a real fatal in the wild.

Three new events, all under the existing plugin-conflicts-guardian feature bucket, all mirroring the JSON-encoded extra shape used by the existing Probe transport error event in PCG_Load_Tester:

  • Activation blockedpcg_guard_evaluate_plugins() (in activation-guard.php) calls a new pcg_guard_log_blocked_activation() helper right before returning a non-empty $blocked map. extra: checked (probe batch), blocked (basenames being blocked; empty-string entry indicates a batch-level fallback), status (fatal / throwable), file (basename only), line, reason (raw probe-verdict message).
  • Update blockedpcg_update_guard_check() (in update-guard.php) calls a new pcg_update_guard_log_blocked() helper just before returning the WP_Error that aborts the install/update. extra: action (install / update), slug, file (basename), line, reason, error_count (so a single broken file is distinguishable from a wholesale broken package).
  • Update rolled backpcg_healthcheck_after_update() (in update-healthcheck.php) calls a new pcg_healthcheck_log_rollback() helper inside the per-candidate rollback loop. extra: plugin, new_version, previous_version, the probe verdict that triggered rollback (probe_status / probe_file / probe_line / probe_reason), and the rollback outcome (rollback_status — distinguishes reactivated / restored / rollback_failed / rollback_unavailable — and restored_to).

All three are no-ops outside WordPress.com (no log2logstash) and best-effort: a logging failure must never escalate into a fatal, since these run on the activation / install / update request paths.

Related product discussion/links

Does this pull request change what data or activity we track or use?

Yes — adds three new logstash event slugs (Activation blocked, Update blocked, Update rolled back) under the existing plugin-conflicts-guardian feature bucket. Properties shipped per event are listed above; no PII, no new outbound HTTP calls beyond the existing logstash transport.

Testing instructions

  1. Sync jetpack-mu-wpcom to a test site with pcg_guard_activation enabled (the new default after Plugin Conflicts Guardian: default pcg_guard_activation to true #48553) and pcg_guard_updates enabled.
  2. Activation block: place a plugin known to fatal on load, click Activate in wp-admin → Plugins. Expected: existing admin notice and one new Activation blocked row in the plugin-conflicts-guardian logstash bucket.
  3. Update block: upload-install a zip containing a PHP parse error. Expected: install refused with a parse-error notice and one Update blocked row carrying action: install, the offending file basename + line, and error_count.
  4. Rollback: install a "good" plugin, then update it to a version that fatals only at runtime (passes parse-error gate). Expected: post-update probe fails, plugin gets rolled back, and one Update rolled back row with rollback_status: reactivated (or restored / rollback_failed depending on the snapshot path).
  5. On a non-WordPress.com install (no log2logstash) → all three guards still work, no log emitted, no fatal.

🤖 Generated with Claude Code

Emit an `Activation blocked` event from `pcg_guard_evaluate_plugins()` whenever the activation guard returns a non-empty $blocked map, so we can measure how often the pre-flight check catches a real fatal in the wild — alongside the existing transport-error event in PCG_Load_Tester. Best-effort: silently no-ops off WordPress.com (no `log2logstash`) and never escalates a logging failure into a fatal on the activation request path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (WordPress.com Site Helper), and enable the add/pcg-log-blocked-activation branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack-mu-wpcom-plugin add/pcg-log-blocked-activation

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!

@github-actions github-actions Bot added the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label May 6, 2026
Extends the new logstash coverage to the update path. `update-guard.php` emits an `Update blocked` event when the parse-error gate refuses an install or update; `update-healthcheck.php` emits an `Update rolled back` event for each plugin the post-update probe reverted, including the rollback-status / restored_to fields so we can separately track clean restores from `rollback_failed` / `rollback_unavailable`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arthur791004 arthur791004 changed the title Plugin Conflicts Guardian: log blocked activations to logstash Plugin Conflicts Guardian: log blocked activations, blocked updates, and post-update rollbacks to logstash May 6, 2026
@jp-launch-control
Copy link
Copy Markdown

jp-launch-control Bot commented May 6, 2026

Code Coverage Summary

Coverage changed in 5 files.

File Coverage Δ% Δ Uncovered
projects/packages/jetpack-mu-wpcom/src/features/plugin-conflicts-guardian/update-healthcheck.php 19/143 (13.29%) -1.56% 15 💔
projects/packages/jetpack-mu-wpcom/src/features/plugin-conflicts-guardian/activation-guard.php 40/139 (28.78%) -2.97% 13 💔
projects/packages/jetpack-mu-wpcom/src/features/plugin-conflicts-guardian/plugin-conflicts-guardian.php 0/8 (0.00%) 0.00% 1 ❤️‍🩹
projects/packages/jetpack-mu-wpcom/src/features/plugin-conflicts-guardian/update-guard.php 66/77 (85.71%) 3.75% 0 💚
projects/packages/jetpack-mu-wpcom/src/features/plugin-conflicts-guardian/class-pcg-load-tester.php 16/127 (12.60%) 1.00% -11 💚

1 file is newly checked for coverage.

File Coverage
projects/packages/jetpack-mu-wpcom/src/features/plugin-conflicts-guardian/pcg-log.php 4/26 (15.38%) 💔

Full summary · PHP report

Coverage check overridden by Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR .

arthur791004 and others added 2 commits May 6, 2026 19:36
Drop the explanatory prose to one-line docblocks and keep only the non-obvious inline comment ("Basename only — absolute paths leak install layout"). Behavior unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the on-demand `log2logstash` require + dispatch into a single helper in `pcg-log.php`. Callers in `activation-guard.php`, `update-guard.php`, `update-healthcheck.php`, and `class-pcg-load-tester.php::log_probe_error()` now pass just the message slug + properties array. Behavior unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arthur791004 arthur791004 self-assigned this May 7, 2026
The test file requires update-guard.php directly, so it must also load
the new shared logger; otherwise calls to pcg_log_event() from inside
the guard fatal at test time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arthur791004 arthur791004 added [Status] Needs Review This PR is ready for review. and removed [Tests] Includes Tests labels May 7, 2026
@arthur791004 arthur791004 requested a review from taipeicoder May 7, 2026 05:34
@arthur791004 arthur791004 added the Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR label May 7, 2026
* @param array $extra Event-specific properties; JSON-encoded into the `extra` field.
* @return void
*/
function pcg_log_event( $message, array $extra ) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is best-effort , should we wrap it in a try/catch block? That way we prevent exceptions here blocking the rest of the flows.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 8ceac93 — wrapped the whole dispatcher in try/catch with a fail-open empty handler. The require + json_encode + log2logstash chain now can't escalate into a fatal on the activation / install / update request paths.

'checked' => $checked,
'blocked' => array_keys( $blocked ),
'status' => (string) ( $result['status'] ?? '' ),
// Basename only — absolute paths leak install layout.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reason might also leak absolute paths if it's just raw PHP error text 😅
Since this is log, it should be fine?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, fine for log purposes — reason is the raw probe verdict (PHP fatal/parse-error message). It can carry an absolute path, but everything stays in our internal logstash bucket; no PII, no exposure to the site owner. We accept that as the cost of having an actually useful reason field for triage. The file field stays sanitized to basename only because that one's structured/queried, so worth keeping consistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually went ahead and sanitized in ef3e3ca — added pcg_log_redact_paths() that walks the extra payload before JSON-encoding and rewrites any ABSPATH / WP_CONTENT_DIR prefix to .../. Keeps the relative tail (plugins/foo/bar.php) which is the useful part for triage, drops the install-layout leak. Applied centrally in pcg_log_event() so all events (including the pre-existing Probe transport error) get it for free.

'Update blocked',
array(
'action' => (string) $action,
'slug' => (string) ( $hook_extra['plugin'] ?? ( $hook_extra['theme'] ?? '' ) ),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plugin_Upgrader::install() doesn't populate hook_extra['plugin']. We can pass $source and fall back to basename( untrailingslashit( $source ) ).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 8ceac93. Threaded $source through to pcg_update_guard_log_blocked() and fall back to basename( untrailingslashit( $source ) ) when hook_extra['plugin'] is empty (install path).

…ll slug

Two review nits from #48565:

* Wrap `pcg_log_event()` in try/catch — the helper is documented as
  best-effort, but `wp_json_encode()` / `log2logstash()` could still
  throw, and these run on activation / install / update request paths
  where any escalation defeats the point.
* `Plugin_Upgrader::install()` doesn't populate `hook_extra['plugin']`,
  so the install case logged an empty slug. Thread `$source` through
  to `pcg_update_guard_log_blocked()` and fall back to
  `basename( untrailingslashit( $source ) )`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
taipeicoder
taipeicoder previously approved these changes May 7, 2026
Follow-up to taipeicoder's note that `reason` (raw probe verdict / parse-
error message) can carry absolute paths. Walk the extra payload before
JSON-encoding and rewrite any `ABSPATH` / `WP_CONTENT_DIR` prefix to
`.../`. Keeps the relative tail (`plugins/foo/bar.php`) — the useful
part for triage — and drops the install-layout leak. Applied centrally
in pcg_log_event() so all three events plus the existing
`Probe transport error` event get it for free.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arthur791004 arthur791004 merged commit 11eb0fe into trunk May 7, 2026
72 checks passed
@arthur791004 arthur791004 deleted the add/pcg-log-blocked-activation branch May 7, 2026 07:34
@github-actions github-actions Bot removed [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. [Status] Needs Review This PR is ready for review. labels May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR [mu wpcom Feature] Plugin Conflicts Guardian [Package] Jetpack mu wpcom WordPress.com Features [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants