Repository navigation
Protect Dashboard: Add the Scan section - #53195
Conversation
…pack Protect plugin
|
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 3 files.
17 files are newly checked for coverage. Only the first 5 are listed here.
Full summary · PHP report · JS report Coverage check overridden by
Covered by non-unit tests
|
1899cf9 to
aabb392
Compare
aabb392 to
f445192
Compare
With the protect-dashboard module active and no standalone plugin, the link went to protect-details or Jetpack Cloud instead of the Protect page.
… retry A failed save no longer resets the IP list field to the saved value, and a failed settings load now clears its error on retry and offers a Try again button. Section types take an optional state type, the tab search param is narrowed at runtime, and the settings hook returns a stable object.
ProtectCard and CardRow keep their props and now render through Card from the WordPress UI package, so border, radius, background and header spacing come from the design system.
… section registry Adds routes/ to the client Jest roots so route tests run.
…to add/protect-dashboard-foundation
The base branch added a Jetpack_Protect_Dashboard_Test class of its own, so the section registry tests now live in that class and the threats test moves into the same suite directory.
My Jetpack no longer depends on the feature flags package, so the Jetpack plugin now reports the flag through jetpack_my_jetpack_protect_in_jetpack.
…to add/protect-dashboard-foundation
ProtectCard now wraps its children in Card.Content, so the stats grid and the threat list picked up the card's inline padding: their dividers stopped short of the card edges and the stats were padded twice. Render both as CardRow so they bleed like every other row, and pad the stats with the card's own padding so they line up with the header.
Pass ScanState to ProtectSection and DashboardContext now that they take a state type, and drop the casts.
…' into add/protect-dashboard-scan
b531356 to
6567e4b
Compare
…d-scan # Conflicts: # projects/packages/my-jetpack/src/class-main-features.php # projects/packages/my-jetpack/src/products/class-protect.php # projects/packages/my-jetpack/tests/php/Protect_Product_Test.php # projects/plugins/jetpack/_inc/lib/class-jetpack-protect-dashboard-feature-flags.php # projects/plugins/jetpack/composer.json # projects/plugins/jetpack/composer.lock # projects/plugins/jetpack/modules/protect-dashboard.php # projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Protect_Dashboard_Feature_Flags_Test.php
…r, and fix, ignore or unignore them
There was a problem hiding this comment.
🟡 Changes recommended
Scan startup, free-plan action gating, request throttling, and the documented disclosure behavior have unresolved defects.
4 open findings
What changed in this PR
Adds the Scan section to the feature-gated Protect dashboard, including reporting, scan controls, and threat management.
Changes:
- Adds PHP Scan REST endpoints and threat formatting.
- Adds Scan overview/settings UI, polling, threat details, and actions.
- Adds dependencies, tests, styles, and changelogs.
| File | Description |
|---|---|
projects/plugins/jetpack/changelog/add-protect-dashboard-scan |
Records the Jetpack-facing change. |
projects/packages/protect/tests/php/Dashboard_Threats_Test.php |
Tests threat formatting and icons. |
projects/packages/protect/src/sections/class-scan.php |
Implements Scan state and REST endpoints. |
projects/packages/protect/src/class-dashboard-threats.php |
Enriches formatted threat data. |
projects/packages/protect/routes/dashboard/sections/types.ts |
Adds inspector support to sections. |
projects/packages/protect/routes/dashboard/sections/scan/types.ts |
Defines Scan state and threat types. |
projects/packages/protect/routes/dashboard/sections/scan/threats-list.tsx |
Renders threat tables and actions. |
projects/packages/protect/routes/dashboard/sections/scan/threat-media.tsx |
Renders threat icons and images. |
projects/packages/protect/routes/dashboard/sections/scan/threat-details.tsx |
Implements threat details and controls. |
projects/packages/protect/routes/dashboard/sections/scan/threat-actions.ts |
Handles fix and ignore operations. |
projects/packages/protect/routes/dashboard/sections/scan/test/threat-actions.test.ts |
Tests ignore and undo behavior. |
projects/packages/protect/routes/dashboard/sections/scan/style.scss |
Styles Scan cards and threat UI. |
projects/packages/protect/routes/dashboard/sections/scan/store.ts |
Shares Scan and inspector state. |
projects/packages/protect/routes/dashboard/sections/scan/settings-card.tsx |
Adds the Scan settings card. |
projects/packages/protect/routes/dashboard/sections/scan/scanning-state.tsx |
Displays scanning progress. |
projects/packages/protect/routes/dashboard/sections/scan/scan-card.tsx |
Implements the overview Scan card. |
projects/packages/protect/routes/dashboard/sections/scan/scan-button.tsx |
Controls scan availability and explanation. |
projects/packages/protect/routes/dashboard/sections/scan/safe-state.tsx |
Displays the no-threat state. |
projects/packages/protect/routes/dashboard/sections/scan/labels.ts |
Provides localized Scan labels. |
projects/packages/protect/routes/dashboard/sections/scan/inspector.tsx |
Selects inspector threat details. |
projects/packages/protect/routes/dashboard/sections/scan/index.tsx |
Registers Scan dashboard components. |
projects/packages/protect/routes/dashboard/sections/inspector-params.ts |
Lists inspector URL parameters. |
projects/packages/protect/routes/dashboard/route.tsx |
Activates the route inspector. |
projects/packages/protect/routes/dashboard/package.json |
Adds data and notices dependencies. |
projects/packages/protect/routes/dashboard/inspector.tsx |
Renders the selected section inspector. |
projects/packages/protect/package.json |
Declares package dependencies. |
projects/packages/protect/changelog/add-protect-dashboard-scan |
Records the package feature. |
pnpm-lock.yaml |
Locks the added dependencies. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if ( $has_plan && ! get_transient( self::REQUEST_LOCK ) ) { | ||
| $api_url = Scan_Status::get_api_url(); | ||
| $response = is_wp_error( $api_url ) | ||
| ? $api_url | ||
| : Client::wpcom_json_api_request_as_blog( $api_url . '/enqueue', '2', array( 'method' => 'POST' ), null, 'wpcom' ); |
There was a problem hiding this comment.
Fixed in 216aa3d: the lock is claimed before the WordPress.com request and released if it fails. With a persistent object cache it uses wp_cache_add(), which is atomic; without one, transients can't be made atomic, but claiming first shrinks the race from a WordPress.com round trip to a few microseconds.
| const startScan = useCallback( () => { | ||
| const requested = Date.now(); | ||
| setIsStarting( true ); | ||
| setStartError( null ); | ||
| apiFetch< ScanState >( { path: SCAN_PATH, method: 'POST' } ) | ||
| .then( next => { | ||
| if ( next.hasPlan && ! next.error ) { | ||
| setRequestedAt( requested ); | ||
| } | ||
| setPolls( 0 ); | ||
| mergeScan( next ); | ||
| } ) | ||
| .catch( ( e: { message?: string } ) => | ||
| setStartError( e?.message || __( 'The scan couldn’t be started.', 'jetpack-protect-pkg' ) ) | ||
| ) | ||
| .finally( () => setIsStarting( false ) ); | ||
| }, [] ); |
There was a problem hiding this comment.
Fixed in 216aa3d: "Scan now" sets the requested-at time from the plan the card already has, before the request, and clears it if the request fails.
| <CardRow className="jp-protect-card__threats"> | ||
| <ThreatsList | ||
| threats={ threats } | ||
| ignored={ ignored } | ||
| empty={ <SafeState scan={ scan } isStarting={ isStarting } onScan={ startScan } /> } | ||
| /> |
There was a problem hiding this comment.
The disclosure was removed on purpose: the threats now always show in a table, with a details sidebar. The PR description was stale, and I've updated it to match.
| <Menu.Separator /> | ||
| <Menu.Item onClick={ onToggleIgnore }> | ||
| <Menu.ItemLabel> | ||
| { isIgnored | ||
| ? __( 'Unignore threat', 'jetpack-protect-pkg' ) | ||
| : __( 'Ignore threat', 'jetpack-protect-pkg' ) } | ||
| </Menu.ItemLabel> | ||
| </Menu.Item> |
There was a problem hiding this comment.
Fixed in 216aa3d: ThreatsList takes canAct (the Scan plan), and the separator plus Ignore/Unignore item, and the row's Auto-fix button, only render when it's true.
…on a Scan plan, and fix CI
… Scan section's lock, gate and fix status


Fixes JETPACK-2932
Part of JETPACK-2881, JETPACK-2880
Proposed changes
Adds the Scan section to the flag-gated Protect dashboard (
jetpack-protect-dashboardflag,protect-dashboardmodule), inpackages/protect.Scan card (Overview)
Threat details sidebar
?threat=<id>: what Jetpack found, the file and flagged lines or the affected plugin, how to resolve it, and update/deactivate/WordPress.org links.core/noticessnackbars; ignore and unignore offer Undo.PHP
src/sections/class-scan.php:GET/POST jetpack/v4/protect-dashboard/scan(report, start a scan at most once a minute),POST …/threats/<id>/ignore|unignore,GET|POST …/threats/<id>/fix(start, then status), andGET …/scan/ignored. Fix and ignore routes need a Scan plan.src/class-dashboard-threats.phpadds the installed name, icon, admin action links, code context and vulnerability links to each threat, reading plugins and update checks once per list.inspector({ param, Panel }); Scan is the first.Settings card: Scan has no settings here, so the card explains what runs and links to Scan management or the upgrade page.
Related product discussion/links
Does this pull request change what data or activity we track or use?
No.
Testing instructions
wp companion feature-flag enable jetpack-protect-dashboard(or thejetpack_feature_flag_enabled_jetpack-protect-dashboardfilter)wp jetpack module activate protect-dashboardpublic-api.wordpress.com(e.g. apre_http_requestfilter returning aWP_Error) and reload: the card shows "We couldn’t check your site…" with "Try again". Ignore/fix failures show as error snackbars.Tip: to try the free path on a site that has a plan, a temporary mu-plugin with a
pre_option_jetpack_active_planfilter returning a free plan works; delete it afterwards and runwp eval 'Automattic\Jetpack\Current_Plan::refresh_from_wpcom();'to restore the cached plan.Review follow-ups
Fixed in the latest round:
wp_cache_add()when there is a persistent object cache) and released if the request fails.Dashboard_Testclears the real sectionsDashboard::init()now registers.nullarray offset when a threat has no plugin slug.Coverage: the rest of
class-scan.phpproxies WordPress.com (scan, fix, ignore, history) and the rest of the new JS is React views; both were exercised on a live Scan-plan site (screenshots and recording below) rather than mocked in unit tests, henceCovered by non-unit tests.Deferred:
protect-status, shared with the Protect plugin.Screenshots
Captured on a Jurassic Ninja site with Jetpack Scan at Jetpack › Protect, 1440px wide. Threats come from the Jetpack threat tester, a few known-vulnerable plugins, and a malicious
wp_optionsrow.Screen recording: ignore a threat and undo it, then auto-fix a vulnerable theme.
scan-recording.mov
Scan card: threats in a table with WordPress.org plugin icons, Active / Ignored views, and a row menu (view, update, deactivate, ignore).
Details sidebar for a file threat: what Jetpack found, the file and the flagged lines, and how to resolve it.
Details sidebar for a vulnerable plugin: links to update the plugin or view it on WordPress.org, plus Ignore it and Auto-fix it.
After ignoring a threat: the sidebar marks it Ignored and offers Unignore it, and a snackbar offers Undo.
Ignored view:
While a scan runs: Scan's reported progress, and what is being checked.
Settings → Scan card (unchanged):