Resolve 1.10.0 release conflicts (merge main into develop) - #781
Merged
Conversation
Brings main's static analysis and dependency security checks back to green: - Static Analysis: clear 25 pre-existing PHPStan errors. Ports develop's typed @return on Date::get_periods()/get_range() (which also resolves the Chart modify() errors), takes develop's exact versions of class-page-settings, class-activity-scores, class-chart and class-update-140, converts the WP-core require_once ignores to the @phpstan-ignore-next-line form that suppresses under PHPStan 2.1.x, and adds inline ignores elsewhere. - Security check: bump phpunit/phpunit 9.6.30 -> 9.6.34 in composer.lock to resolve CVE-2026-24765 (unsafe deserialization in PHPT code coverage).
The anonymous classes extending the abstract Tasks_Interactive did not implement the abstract Tasks::should_add_task() method. phpunit 9.6.30 did not surface this, but 9.6.34 (the CVE-2026-24765 fix) does, causing a fatal when the test class loads. Implement should_add_task() in all 8 anonymous task providers.
Fix PHPStan errors and phpunit CVE on main
* Sanitize and escape prpl_recommendations title
An authenticated Editor (or higher) could create a recommendation via
POST /wp/v2/prpl_recommendations with an HTML payload in the `title`
field (e.g. `<img src=x onerror=alert(1)>`). The dashboard JS template
(views/js-templates/suggested-task.html) renders `title.rendered` with
Underscore's unescaped `{{{ }}}` syntax, so the payload executed when an
admin loaded the dashboard.
Defense in depth:
- Input: add a `rest_pre_insert_prpl_recommendations` filter that strips
tags from `post_title` on every REST insert/update, regardless of the
user's `unfiltered_html` capability. Recommendation titles are plain
text, so this neutralizes the payload at the source.
- Output (JS): route the two raw `{{{ }}}` title sinks through a new
`prplSuggestedTask.sanitizeTitle()` helper, which inert-parses the
value with DOMParser (no script/resource side effects) and re-escapes
it, preserving legitimate entities like `&` without double-encoding
the server-side `esc_html`'d provider titles.
- Output (admin bar): the PRPL debug tool printed `post_title` unescaped
into a `WP_Admin_Bar` node id (an HTML attribute) and title (rendered
as raw HTML), firing the payload on every admin page in debug mode.
Escape the title with `esc_html()`, use the post ID for the node id,
and escape the activities node title too.
- Also switch `updateTaskTitle` to set `.textContent` instead of
`.innerHTML` for the screen-reader label, closing a self-XSS sink.
Adds tests/phpunit/test-class-rest-recommendations-xss.php covering
Editor and Administrator payloads plus a plain-text regression check.
* Bump version to 1.9.1
* add migration script and revert JS title escaping
* add inline comment, cc @tacoverdo
* Delete recommendation when sanitized title is empty
A title that is pure markup strips to an empty string. wp_update_post()
rejects an update that would leave the title, content, and excerpt all
empty, so the malicious title was left in the DB. The plugin never stores
title-less recommendations, so delete such rows instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* update readme.txt
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…flicts # Conflicts: # classes/admin/class-page-settings.php # classes/class-suggested-tasks.php # classes/utils/class-color-customizer.php # composer.lock # progress-planner.php # readme.txt # tests/phpunit/test-class-rest-recommendations-xss.php
Contributor
|
Test merged PR on Playground |
Contributor
🔍 WordPress Plugin Check Report
📊 Report
|
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
232 |
WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in | Using exclusionary parameters, like post__not_in, in calls to get_posts() should be done with caution, see https://docs.wpvip.com/databases/optimize-queries/using-post__not_in/ for more information. |
377 |
WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in | Using exclusionary parameters, like post__not_in, in calls to get_posts() should be done with caution, see https://docs.wpvip.com/databases/optimize-queries/using-post__not_in/ for more information. |
381 |
WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in | Using exclusionary parameters, like post__not_in, in calls to get_posts() should be done with caution, see https://docs.wpvip.com/databases/optimize-queries/using-post__not_in/ for more information. |
388 |
WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in | Using exclusionary parameters, like post__not_in, in calls to get_posts() should be done with caution, see https://docs.wpvip.com/databases/optimize-queries/using-post__not_in/ for more information. |
📁 classes/suggested-tasks/data-collector/class-unpublished-content.php (1 warning)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
103 |
WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in | Using exclusionary parameters, like post__not_in, in calls to get_posts() should be done with caution, see https://docs.wpvip.com/databases/optimize-queries/using-post__not_in/ for more information. |
📁 classes/suggested-tasks/data-collector/class-terms-without-posts.php (1 warning)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
120 |
PluginCheck.Security.DirectDB.UnescapedDBParameter | Unescaped parameter $query used in $wpdb->get_results()\n$query assigned unsafely at line 118. |
📁 classes/suggested-tasks/data-collector/class-yoast-orphaned-content.php (1 warning)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
111 |
PluginCheck.Security.DirectDB.UnescapedDBParameter | Unescaped parameter $query used in $wpdb->get_row()\n$query assigned unsafely at line 98. |
📁 classes/suggested-tasks/data-collector/class-terms-without-description.php (1 warning)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
108 |
PluginCheck.Security.DirectDB.UnescapedDBParameter | Unescaped parameter $query used in $wpdb->get_results()\n$query assigned unsafely at line 106. |
📁 classes/activities/class-query.php (2 warnings)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
71 |
PluginCheck.Security.DirectDB.UnescapedDBParameter | Unescaped parameter $table_name used in $wpdb->query()\n$table_name assigned unsafely at line 58. |
163 |
PluginCheck.Security.DirectDB.UnescapedDBParameter | Unescaped parameter $where_args used in $wpdb->get_results()\n$where_args assigned unsafely at line 153. |
🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check
Contributor
✅ Code Coverage Report
🎉 Great job maintaining/improving code coverage! ℹ️ About this report
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Back-merges
mainintodevelopto clear the conflicts blocking the 1.10.0 release PR (#695).All conflicts resolve to develop's side:
1.10.0, stable tag1.10.0= 1.10.0 =changelog block (the= 1.9.1 =entry is preserved below it)save_redirect_on_loginstays removed)orderbyhandling inclass-suggested-tasks.phpThe 1.9.1 XSS sanitization fix from
mainis retained.Verified: lint clean, PHPStan clean, 404 PHPUnit tests pass on the merged result.
Once merged, #695 (develop → main) becomes conflict-free.
🤖 Generated with Claude Code