Repository navigation
Protect Dashboard: Add the Scan history tab - #53196
Conversation
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
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:
If you have questions about anything, reach out in #jetpack-developers for guidance! Jetpack plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. |
Code Coverage SummaryCoverage changed in 7 files. Only the first 5 are listed here.
9 files are newly checked for coverage. Only the first 5 are listed here.
Full summary · PHP report · JS report If appropriate, add one of these labels to override the failing coverage check:
Covered by non-unit tests
|
14d2903 to
952095c
Compare
b531356 to
6567e4b
Compare
8c42593 to
0535e45
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Fix results remain stale in both cache layers, and multisite plugin deletion uses the wrong admin context.
7 open findings
Refresh history after successful threat fixes · New Document and test the new plugin deletion flow · New Use Network Admin URL for multisite plugin deletion · New Add automated coverage for paid-plan History access · New Invalidate history cache when threats become fixed · New Fix incomplete grammar in the sentence · New Do not advertise the tab while its feature flag is disabled · New
What changed in this PR
Adds a Scan-plan-only History tab to the flag-gated Protect dashboard, backed by a cached WordPress.com history endpoint.
Changes:
- Adds the History REST endpoint, tab, filters, table, and inspector.
- Shares Scan history caching between fixed and ignored threats.
- Refactors threat actions and adds plugin/theme deletion controls.
| File | Description |
|---|---|
projects/plugins/jetpack/changelog/add-protect-dashboard-history |
Adds the Jetpack release note. |
projects/packages/protect/src/sections/class-scan.php |
Shares and invalidates the history cache. |
projects/packages/protect/src/sections/class-history.php |
Adds History state and REST route. |
projects/packages/protect/src/class-dashboard-threats.php |
Adds software deletion links. |
projects/packages/protect/routes/dashboard/test/use-close-on-escape.test.tsx |
Tests Escape-key behavior. |
projects/packages/protect/routes/dashboard/stage.tsx |
Generalizes tab navigation. |
projects/packages/protect/routes/dashboard/sections/types.ts |
Exposes generalized tab navigation. |
projects/packages/protect/routes/dashboard/sections/scan/types.ts |
Adds deletion actions to threat types. |
projects/packages/protect/routes/dashboard/sections/scan/threats-list.tsx |
Extracts reusable threat fields and views. |
projects/packages/protect/routes/dashboard/sections/scan/threat-row-actions.tsx |
Defines DataViews row actions. |
projects/packages/protect/routes/dashboard/sections/scan/threat-details.tsx |
Shows fixed status and deletion controls. |
projects/packages/protect/routes/dashboard/sections/scan/threat-actions.ts |
Prevents duplicate threat actions. |
projects/packages/protect/routes/dashboard/sections/scan/store.ts |
Generalizes URL search-parameter state. |
projects/packages/protect/routes/dashboard/sections/scan/settings-card.tsx |
Updates the Scan icon. |
projects/packages/protect/routes/dashboard/sections/scan/scan-card.tsx |
Links Scan to History and revises presentation. |
projects/packages/protect/routes/dashboard/sections/scan/labels.ts |
Adds deletion labels. |
projects/packages/protect/routes/dashboard/sections/scan/inspector.tsx |
Uses generalized search parameters. |
projects/packages/protect/routes/dashboard/sections/scan/index.tsx |
Passes tab navigation into Scan. |
projects/packages/protect/routes/dashboard/sections/scan/delete-software.tsx |
Adds deletion confirmations and controls. |
projects/packages/protect/routes/dashboard/sections/inspector-params.ts |
Registers the History inspector parameter. |
projects/packages/protect/routes/dashboard/sections/history/store.ts |
Fetches and stores history data. |
projects/packages/protect/routes/dashboard/sections/history/inspector.tsx |
Adds read-only history threat details. |
projects/packages/protect/routes/dashboard/sections/history/index.tsx |
Registers the plan-gated History tab. |
projects/packages/protect/routes/dashboard/sections/history/history-panel.tsx |
Renders loading, error, and list states. |
projects/packages/protect/routes/dashboard/sections/history/history-list.tsx |
Renders fixed and ignored threat tables. |
projects/packages/protect/routes/dashboard/route.scss |
Adjusts card headers and inspector width. |
projects/packages/protect/routes/dashboard/inspector.tsx |
Adds Escape-key inspector closing. |
projects/packages/protect/routes/dashboard/components/use-close-on-escape.ts |
Implements Escape-key handling. |
projects/packages/protect/routes/dashboard/components/tab-link.tsx |
Generalizes dashboard tab links. |
projects/packages/protect/routes/dashboard/components/card.tsx |
Adds card descriptions and header sizing. |
projects/packages/protect/changelog/add-protect-dashboard-history |
Adds the package release note. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| export function loadHistory(): Promise< void > { | ||
| if ( isLoading || historyStore.get().threats ) { | ||
| return Promise.resolve(); |
There was a problem hiding this comment.
Fixed in 5406e5b. Opening the History tab now always refetches (loadHistory( true )), keeping the current list on screen while it does, so a threat fixed from Overview shows up without a reload. The details sidebar still only fetches when nothing is loaded. Paired with the server-side cache clear on a completed fix (see the class-scan.php thread).
| id: 'delete-plugin', | ||
| label: softwareLabel( 'delete' ), | ||
| isEligible: item => item.extension?.type === 'plugins' && hasLink( 'delete' )( item ), | ||
| // WordPress asks to confirm on the page this opens. | ||
| callback: link( 'delete' ), |
There was a problem hiding this comment.
Documented in the PR description and changelog entries (5406e5b): the delete behaviour, its confirmation (core's own screen for plugins, a dashboard confirmation for themes, since core deletes themes without asking), and testing steps 6–7. I kept it in this PR since it's part of the same Scan/History pass; happy to split it out if a reviewer prefers.
| $actions['delete'] = add_query_arg( | ||
| '_wpnonce', | ||
| wp_create_nonce( 'bulk-plugins' ), | ||
| self_admin_url( 'plugins.php?action=delete-selected&checked[]=' . rawurlencode( $file ) ) |
There was a problem hiding this comment.
Good catch, and it goes a bit further: on multisite a plugin inactive on this site can still be active on another, so offering delete from one site's dashboard is unsafe even with the right admin URL. Fixed in 5406e5b by not offering plugin delete on multisite at all, matching what this PR already did for themes.
| public static function get_history() { | ||
| if ( ! Dashboard::has_scan_plan() ) { | ||
| return new WP_Error( 'no_scan_plan', __( 'Scan history needs a Jetpack Scan plan.', 'jetpack-protect-pkg' ), array( 'status' => 403 ) ); | ||
| } |
There was a problem hiding this comment.
Added in 5406e5b: History_Section_Test::test_history_needs_a_scan_plan checks the 403 no_scan_plan response even when the history cache is already filled (the gate runs before the cache), and Scan_Section_Test::test_ignored_threats_come_from_the_shared_history checks the shared cache is split correctly. Removing the plan gate or the ignored filter fails exactly these tests. I didn't add a plan-enabled delegation test: Plan::has_required_plan() memoizes in a function-level static, so it can't be flipped within one PHPUnit process without changing that package.
| $threats = Dashboard_Threats::format_all( (array) ( $body->threats ?? array() ) ); | ||
| set_transient( self::HISTORY_CACHE, $threats, 5 * MINUTE_IN_SECONDS ); |
There was a problem hiding this comment.
Fixed in 5406e5b: HISTORY_CACHE is now cleared when the fix POST reports fixed immediately and when a polled fix status first reports fixed.
| /** | ||
| * Call `onClose` when Escape is pressed anywhere on the page. | ||
| * | ||
| * Skips presses a menu, listbox or dialog handled or had focus for, so Escape closes those first. |
There was a problem hiding this comment.
Reworded in 5406e5b: "Ignores Escape inside a menu, listbox or dialog, or when one already handled it, so that closes first."
| Significance: patch | ||
| Type: other | ||
|
|
||
| Protect: Add a Scan history tab to the Protect dashboard for sites with Jetpack Scan. |
There was a problem hiding this comment.
Keeping this one as is. The entry is Type: other, which is Jetpack's non-user-facing bucket ("This section will not be copied to readme.txt"), so it doesn't reach the readme or release notes. It matches the earlier flag-gated dashboard PRs, e.g. #53195's add-protect-dashboard-scan entry. The user-facing entry can land when the flag ships.


Fixes JETPACK-2931
Part of JETPACK-2880
Proposed changes
All of this is behind the
jetpack-protect-dashboardflag and theprotect-dashboardmodule.History tab (sites with a Jetpack Scan plan)
GET jetpack/v4/protect-dashboard/history(manage_options); returns403 no_scan_planwithout a Scan plan. It shares one cached WordPress.com/scan/historyfetch with the existing ignored-threats endpoint (Scan::get_history_threats()), so the History tab makes one request instead of two. The cache is cleared when a scan starts, a threat is ignored or unignored, or a fix completes.?tab=historyfalls back to Overview.Scan card and threat lists
Related product discussion/links
Does this pull request change what data or activity we track or use?
No.
Testing instructions
On a connected site running this branch:
wp companion feature-flag enable jetpack-protect-dashboard(or thejetpack_feature_flag_enabled_jetpack-protect-dashboardfilter), thenwp jetpack module activate protect-dashboard. If the module is not found, runwp eval 'Jetpack_Options::delete_option("available_modules");'first.admin.php?page=jetpack-protect&p=%2F%3Ftab%3Dhistorylands on Overview;GET /wp-json/jetpack/v4/protect-dashboard/historyreturns 403no_scan_plan. The Scan card's subheading reads "Daily vulnerability checks for WordPress, plugins and themes."Screenshots
Captured on a live Jurassic Ninja site with a Jetpack Security plan, at 1440×900.