Conversation
Contributor
|
Test on Playground |
Contributor
Composer package changes
|
Contributor
✅ Code Coverage Report
🎉 Great job maintaining/improving code coverage! 📊 File-level Coverage Changes (155 files)🆕 New Files
ℹ️ About this report
|
Change composer namespace
Revert to old onboarding system
Resolve conflict in onboarding e2e test: develop reverted to old onboarding system, so update the refactored TypeScript test to match the old form-based onboarding flow.
e2e tests refactor
Add get_per_page() helper to Suggested_Tasks widget that returns PER_PAGE_DASHBOARD (3) on the WP Dashboard screen and PER_PAGE_DEFAULT (5) on all other screens. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* WIP * filter out the activity category * tweak when onboarding tasks should show, pp-hosts compat * Replace hardcoded Ravi icon with branding system icon Use get_admin_menu_icon() from the branding system instead of hardcoded icon_progress_planner.svg references, so hosts with custom branding automatically get their own icon everywhere. Closes #51 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix coding standards: add backslash prefix to global functions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix e2e onboarding test: remove pre-set license key and mock remote API The blueprint was pre-setting progress_planner_license_key, which made is_privacy_policy_accepted() return true and the welcome screen never appeared. The onboarding test couldn't find .prpl-welcome. Fix: remove the license key from the blueprint so the fresh install onboarding screen shows, and mock the remote progressplanner.com API calls (get-nonce + onboard) since Playground can't reach them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix onboarding test: wait for page reload after form submission After the form submit triggers the JS flow (remote API → save license key → window.location.reload()), explicitly wait for the page load event before checking for the dashboard widget. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix onboarding test: bypass remote API with direct AJAX call Previous approaches using page.route() failed because Playwright's route mocking doesn't intercept XMLHttpRequest in WP Playground. Instead, use page.evaluate() to call the local WP AJAX save endpoint directly, then reload the page to verify the dashboard appears. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix onboarding test: use XMLHttpRequest instead of fetch The fetch() API fails in Playground's service worker environment with "TypeError: Failed to fetch". Use XMLHttpRequest instead, which is the same mechanism the actual onboarding JS uses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix e2e onboarding test for WP Playground environment The onboarding test was broken since the Docker→Playground migration: 1. Blueprint pre-set the license key, hiding the welcome screen 2. page.route() cannot intercept requests handled by Playground's service worker, so the remote API (progressplanner.com) calls in the JS onboarding flow silently fail Fix by: - Removing pre-set license key from blueprint (keep demo_data_generated to prevent Playground class from auto-generating one) - Using Playwright's page.request.post() to call the local WP AJAX endpoint directly — this bypasses the service worker entirely while sharing the page's auth cookies Works in both Playground (e2e-tests) and Docker (yoast-premium-tests). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * apply branding to dashboard widget titles * Pin dist-archive-command to v3.1.0 for WP-CLI 2.12 compat dist-archive-command v3.2.0 requires wp-cli ^2.13 but the CI runner (shivammathur/setup-php) provides 2.12.0, causing plugin-check to fail. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…andle (#762) Allows external integrations (e.g. pp-hosts guided tour, which runs on the block editor, site editor, frontend, and non-PP admin screens) to apply the partner branding custom CSS to their own stylesheets, instead of being limited to the PP admin pages where Page::enqueue_styles() runs. The new method is idempotent per style handle, so the per-request dedupe that previously lived on Page (via the $branding_inline_styles_added static — needed because dashboard widgets call enqueue_styles() multiple times per request) now lives with the branding class itself and works for any handle. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix PHPStan errors and phpunit CVE on main 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). * Fix abstract method fatal in test-class-security.php 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. * v1.9.1 (#763) * 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> * Fix plain-text title test to pass on multisite On multisite, editors lack the unfiltered_html capability, so core's kses encodes the ampersand in the test title and the byte-for-byte assertion fails. Grant the capability (via super admin on multisite) so the test isolates our XSS sanitization rather than core's kses behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Grant unfiltered_html before switching user in title test kses_init() runs on the set_current_user hook and decides whether to attach the kses filters at switch time. The capability must be granted before wp_set_current_user(), otherwise the filters are already attached and the multisite assertion still sees the ampersand encoded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Bump composer/composer 2.9.2 -> 2.10.0 to clear dev-dependency CVEs Resolves the Security check failure: composer/composer 2.9.2 (pulled in transitively via wp-cli/wp-cli-bundle in require-dev) carried CVE-2026-40176, CVE-2026-40261, and CVE-2026-45793. Targeted `composer update composer/composer --with-dependencies`; composer.json (runtime deps) unchanged. `composer audit` now reports no advisories. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Joost de Valk <joost@altha.nl> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates wp-coding-standards/wpcs to 3.4.1, which fixes a security issue in the WordPress.WP.EnqueuedResourceParameters sniff where running PHPCS over untrusted PHP code could lead to arbitrary command execution on the scanning host (GHSA-3pwp-g2mj-5p3v). Also bumps the required transitive dependencies: - phpcsstandards/phpcsutils to 1.2.3 - phpcsstandards/phpcsextra to 1.5.1 Lock-file only; composer.json constraints already allowed 3.4.1. Verified locally with PHPCS: no new violations. Signed-off-by: Filip Ilic <ilic.filip@gmail.com>
…o v1.1.9 Replaces this branch's earlier attempts, all of which were dead ends: setup-php pinning, unsetting GITHUB_TOKEN, the WP-CLI nightly, and the @wordpress/env 11.5.0 pin. Two independent breakages: 1. "Your github oauth token for github.com contains invalid characters". This is INTERMITTENT — setup-php stores the run's ephemeral Actions token in ~/.composer/auth.json as a github-oauth credential, and Composer rejects it only when that particular token contains a character its validator dislikes (composer#12076). Roughly half of runs failed, which is why every previous "fix" appeared to work and then regressed: a single green run proves nothing here. Verified in run 31784727273 that GITHUB_TOKEN and COMPOSER_AUTH are already EMPTY at that point and the credential lives on disk, so clearing env vars cannot help. dist-archive-command is a public package needing no auth, so the fix is to delete the stored credential. Note the WP-CLI nightly used earlier is now actively harmful: it reports as wp-cli 3.0.0-alpha, while every released dist-archive-command requires wp-cli ^2 / ^2.13, so `wp package install` cannot resolve against it. 2. "Environment not initialized. Run `wp-env start` first." — fixed upstream in plugin-check-action v1.1.7 (WordPress/plugin-check-action#590); v1.1.9 also picks up the v1.1.8 bundle-regression fix. Since that landed upstream, the @wordpress/env 11.5.0 pin is dropped rather than carried forward. dist-archive-command stays at v3.1.0: newest release accepting wp-cli ^2. v3.2.x requires ^2.13, which has no stable release (latest is 2.12.0). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.13.5 has a known OS command injection vulnerability (GHSA-hmqg-cxww-wqhq), which makes the Security workflow fail on every PR. Dev dependency only; composer check-cs verified working on 3.13.6. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Plugin Check (the `test` CI job) fails repo-wide with outdated_tested_upto_header now that WordPress 7.1 is current. The suite already runs green against WP latest (7.1) in CI, including the e2e and multisite integration jobs. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Five AIOSEO interactive task handlers verified that the plugin was active and that the nonce was valid, but did not check the user's capability: - Archive_Author - Archive_Date - Crawl_Settings_Feed_Comments - Crawl_Settings_Feed_Authors - Media_Pages They now call the existing verify_aioseo_ajax_security() helper, which runs the active check, the capability check and the nonce check together. Every other settings-changing interactive handler in the plugin already required manage_options, so this brings the AIOSEO handlers in line with the existing convention. Email_Sending::enqueue_scripts() also overrode its parent without carrying over the parent's capability guard. It now checks $this->capability_required(), which resolves to the capability the provider actually declares (manage_options here, inherited from Tasks) rather than a hardcoded literal, so it stays correct if a subclass overrides CAPABILITY. Also switch esc_attr() to esc_url() on three hrefs. esc_url is the correct escaper for a link target, since esc_attr does not validate the URL scheme. Verified: PHPCS clean, parallel-lint clean, PHPStan clean, PHPUnit 398 tests / 1203 assertions passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Remove_Terms_Without_Posts and Update_Term_Description acted on
whatever term_id/taxonomy the request supplied, without checking it
against the term the task actually suggested.
The relevant constraints already existed in the task generation path,
they just were not applied when acting on a request:
maybe_remove_irrelevant_tasks() skips non-public taxonomies and drops
tasks once a term has more than MIN_POSTS posts.
Both handlers now, after the existing capability and nonce checks:
- reject non-public taxonomies,
- require a task from the same provider that targets this exact
term_id/taxonomy pair,
- and, for deletion, re-check the post count at submit time, since
the term may have gained posts after the task was created.
Binding rather than raising the required capability keeps the feature
working for its intended audience: these tasks are meant for Editors,
and the JS already submits the task's own target_term_id and
target_taxonomy, so the legitimate flow is unchanged.
Adds regression tests covering both the rejected and the permitted
paths. Verified that the negative tests fail when the new guards are
removed, and that the positive tests still pass, so they exercise the
behaviour rather than passing vacuously.
Verified: PHPCS clean, PHPStan clean, PHPUnit 404 tests / 1216
assertions passing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The site icon and Yoast organization logo pickers assembled their preview <img> by concatenating attachment metadata into an HTML string and assigning it to innerHTML. Both now build the node with createElement and property assignment, so no HTML string is parsed and attachment metadata is always handled as an attribute value rather than as markup. This follows the same reasoning as the existing textContent usage in updateTaskTitle(). Verified with jsdom that metadata containing quote characters round-trips as a value and does not become markup. Note: the Yoast logo picker's alt fallback still reads "Site icon preview", which looks like a copy-paste artifact. Left as-is to avoid mixing a user-visible string change into this commit. Verified: JS lint clean, PHPCS clean, PHPUnit 404 tests / 1216 assertions passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…in-check Fix Plugin Check CI: remove stored Composer credential, bump plugin-check-action
Add year-specific monthly badge names with 2026 names
Two copies of get_post_types_names() existed: Settings::get_post_types_names() and Content_Helpers::get_post_types_names(). The Settings version has become the canonical one -- it is what the newer suggested-task and data-collector classes call, it carries the init-timing guard, and it is where the exclusion list is maintained. The Content_Helpers copy had drifted behind, and its filtering logic differs in three ways: - It does not exclude elementor_library, which Elementor registers as public + viewable, so it passes the viewability check. - It does not honour the progress_planner_public_post_types filter. - Its empty-case fallback returns a raw [ 'post', 'page' ] without checking those post types are still registered and public. Each copy holds its own static cache, warmed independently on first call. In a normal request both warm after init and agree, so the drift is latent rather than actively breaking sites -- but two functions answering the same question no longer share filtering rules, and any future change to the exclusion list or the filter lands in only one. Point the six remaining Content_Helpers call sites at Settings and reduce Content_Helpers::get_post_types_names() to a deprecated shim so external callers keep working. All six run well after init (shutdown, wp_insert_post, admin widget rendering), so no call site is moved earlier by this change. The null guards in Content_Scan and Content are left in place: they still protect the get_activity_from_post() calls in those methods, and in Content_Scan::get_total_pages() the guard doubles as a deliberate circuit-breaker that halts the scan during plugin updates. Implements #433.
…ypes-names Consolidate get_post_types_names() into Settings
test_delete_rejects_suggested_term_that_gained_posts created the task row before attaching posts to the term. By the time the handler ran, no matching task existed, so the request was rejected by the task-binding check and never reached the post-count re-check the test is named for. Removing the count guard entirely left all six tests passing. Attach the posts first, then record the task, and assert on the specific rejection message so the test cannot silently drift to a different guard again. Verified by mutation: removing any one of the four guards (count re-check, delete binding, update binding, public-taxonomy) now fails at least one test.
…-task-handlers Harden interactive task handlers
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.
No description provided.