Skip to content

Commit

Permalink
Protect: Integrate ThreatsDataViews
Browse files Browse the repository at this point in the history
changelog
  • Loading branch information
nateweller committed Nov 8, 2024
1 parent 6dcbb38 commit 922fef5
Show file tree
Hide file tree
Showing 19 changed files with 164 additions and 82 deletions.
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions projects/plugins/protect/changelog/add-threats-data-views
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: minor
Type: changed

Added DataViews component for viewing scan results.

1 change: 1 addition & 0 deletions projects/plugins/protect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@automattic/jetpack-base-styles": "workspace:*",
"@automattic/jetpack-components": "workspace:*",
"@automattic/jetpack-connection": "workspace:*",
"@automattic/jetpack-scan": "workspace:*",
"@tanstack/react-query": "5.20.5",
"@tanstack/react-query-devtools": "5.20.5",
"@wordpress/api-fetch": "7.11.0",
Expand Down
28 changes: 3 additions & 25 deletions projects/plugins/protect/src/class-scan-history.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,37 +213,15 @@ public static function fetch_from_api() {
* @return History_Model
*/
private static function normalize_api_data( $scan_data ) {

Check failure on line 215 in projects/plugins/protect/src/class-scan-history.php

View workflow job for this annotation

GitHub Actions / Static analysis

Plugin UnusedSuppression Element \Automattic\Jetpack\Protect\Scan_History::normalize_api_data suppresses issue PhanDeprecatedProperty but does not use it
$history = new History_Model();
$history->num_threats = 0;
$history->num_core_threats = 0;
$history->num_plugins_threats = 0;
$history->num_themes_threats = 0;

$history = new History_Model();
$history->last_checked = $scan_data->last_checked;

if ( empty( $scan_data->threats ) || ! is_array( $scan_data->threats ) ) {
return $history;
}

foreach ( $scan_data->threats as $threat ) {
if ( isset( $threat->extension->type ) ) {
if ( 'plugin' === $threat->extension->type ) {
self::handle_extension_threats( $threat, $history, 'plugin' );
continue;
}

if ( 'theme' === $threat->extension->type ) {
self::handle_extension_threats( $threat, $history, 'theme' );
continue;
}
}

if ( 'Vulnerable.WP.Core' === $threat->signature ) {
self::handle_core_threats( $threat, $history );
continue;
}

self::handle_additional_threats( $threat, $history );
foreach ( $scan_data->threats as $source_threat ) {
$history->threats[] = new Threat_Model( $source_threat );
}

return $history;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ThreatFixHeader from '../threat-fix-header';
import UserConnectionGate from '../user-connection-gate';
import styles from './styles.module.scss';

const FixThreatModal = ( { id, fixable, label, icon, severity } ) => {
const FixThreatModal = ( { threat } ) => {
const { setModal } = useModal();
const { fixThreats, isLoading: isFixersLoading } = useFixers();

Expand All @@ -21,7 +21,7 @@ const FixThreatModal = ( { id, fixable, label, icon, severity } ) => {
const handleFixClick = () => {
return async event => {
event.preventDefault();
await fixThreats( [ id ] );
await fixThreats( [ threat.id ] );
setModal( { type: null } );
};
};
Expand All @@ -37,10 +37,7 @@ const FixThreatModal = ( { id, fixable, label, icon, severity } ) => {
</Text>

<div className={ styles.list }>
<ThreatFixHeader
threat={ { id, fixable, label, icon, severity } }
fixAllDialog={ false }
/>
<ThreatFixHeader threat={ threat } fixAllDialog={ false } />
</div>

<div className={ styles.footer }>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button, getRedirectUrl, Text, ThreatSeverityBadge } from '@automattic/jetpack-components';
import { getThreatIcon, getThreatSubtitle } from '@automattic/jetpack-scan';
import { createInterpolateElement, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Icon } from '@wordpress/icons';
Expand All @@ -7,10 +8,11 @@ import useModal from '../../hooks/use-modal';
import UserConnectionGate from '../user-connection-gate';
import styles from './styles.module.scss';

const IgnoreThreatModal = ( { id, title, label, icon, severity } ) => {
const IgnoreThreatModal = ( { threat } ) => {
const { setModal } = useModal();
const ignoreThreatMutation = useIgnoreThreatMutation();
const codeableURL = getRedirectUrl( 'jetpack-protect-codeable-referral' );
const icon = getThreatIcon( threat );

const [ isIgnoring, setIsIgnoring ] = useState( false );

Expand All @@ -25,7 +27,7 @@ const IgnoreThreatModal = ( { id, title, label, icon, severity } ) => {
return async event => {
event.preventDefault();
setIsIgnoring( true );
await ignoreThreatMutation.mutateAsync( id );
await ignoreThreatMutation.mutateAsync( threat.id );
setModal( { type: null } );
setIsIgnoring( false );
};
Expand All @@ -42,12 +44,12 @@ const IgnoreThreatModal = ( { id, title, label, icon, severity } ) => {
<Icon icon={ icon } className={ styles.threat__icon } />
<div className={ styles.threat__summary }>
<Text className={ styles.threat__summary__label } mb={ 1 }>
{ label }
{ getThreatSubtitle( threat ) }
</Text>
<Text className={ styles.threat__summary__title }>{ title }</Text>
<Text className={ styles.threat__summary__title }>{ threat.title }</Text>
</div>
<div className={ styles.threat__severity }>
<ThreatSeverityBadge severity={ severity } />
<ThreatSeverityBadge severity={ threat.severity } />
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { __ } from '@wordpress/i18n';
import React, { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import useConnectSiteMutation from '../../data/use-connection-mutation';
import useProductDataQuery from '../../data/use-product-data-query';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
import usePlan from '../../hooks/use-plan';
import useProtectData from '../../hooks/use-protect-data';

/**
* Product Detail component.
Expand All @@ -30,7 +30,7 @@ const ConnectedPricingTable = () => {
} );

// Access paid protect product data
const { jetpackScan } = useProtectData();
const { data: jetpackScan } = useProductDataQuery();
const { pricingForUi } = jetpackScan;
const { introductoryOffer, currencyCode: currency = 'USD' } = pricingForUi;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text, ThreatSeverityBadge } from '@automattic/jetpack-components';
import { getThreatSubtitle } from '@automattic/jetpack-scan';
import { __, sprintf } from '@wordpress/i18n';
import { Icon } from '@wordpress/icons';
import React, { useState, useCallback } from 'react';
Expand Down Expand Up @@ -68,7 +69,7 @@ export default function ThreatFixHeader( { threat, fixAllDialog, onCheckFix } )
<Icon icon={ threat.icon } className={ styles.threat__icon } />
<div className={ styles.threat__summary }>
<Text className={ styles.threat__summary__label } mb={ 1 }>
{ threat.label }
{ getThreatSubtitle( threat ) }
</Text>
<Text className={ styles.threat__summary__title }>
{ getFixerMessage( threat.fixable ) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button, Text, ThreatSeverityBadge } from '@automattic/jetpack-components';
import { getThreatIcon, getThreatSubtitle } from '@automattic/jetpack-scan';
import { __ } from '@wordpress/i18n';
import { Icon } from '@wordpress/icons';
import { useState } from 'react';
Expand All @@ -7,23 +8,26 @@ import useModal from '../../hooks/use-modal';
import UserConnectionGate from '../user-connection-gate';
import styles from './styles.module.scss';

const UnignoreThreatModal = ( { id, title, label, icon, severity } ) => {
const UnignoreThreatModal = ( { threat } ) => {
const { setModal } = useModal();

const icon = getThreatIcon( threat );

const [ isUnignoring, setIsUnignoring ] = useState( false );
const unignoreThreatMutation = useUnIgnoreThreatMutation();

const handleCancelClick = () => {
return event => {
event.preventDefault();
setModal( { type: null } );
};
};

const [ isUnignoring, setIsUnignoring ] = useState( false );

const handleUnignoreClick = () => {
return async event => {
event.preventDefault();
setIsUnignoring( true );
await unignoreThreatMutation.mutateAsync( id );
await unignoreThreatMutation.mutateAsync( threat.id );
setModal( { type: null } );
setIsUnignoring( false );
};
Expand All @@ -40,12 +44,12 @@ const UnignoreThreatModal = ( { id, title, label, icon, severity } ) => {
<Icon icon={ icon } className={ styles.threat__icon } />
<div className={ styles.threat__summary }>
<Text className={ styles.threat__summary__label } mb={ 1 }>
{ label }
{ getThreatSubtitle( threat ) }
</Text>
<Text className={ styles.threat__summary__title }>{ title }</Text>
<Text className={ styles.threat__summary__title }>{ threat.title }</Text>
</div>
<div className={ styles.threat__severity }>
<ThreatSeverityBadge severity={ severity } />
<ThreatSeverityBadge severity={ threat.severity } />
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useConnection } from '@automattic/jetpack-connection';
import { ScanStatus } from '@automattic/jetpack-scan';
import { useQuery, UseQueryResult, useQueryClient } from '@tanstack/react-query';
import camelize from 'camelize';
import API from '../../api';
Expand All @@ -7,7 +8,6 @@ import {
SCAN_STATUS_IDLE,
SCAN_STATUS_UNAVAILABLE,
} from '../../constants';
import { ScanStatus } from '../../types/scans';
import { QUERY_SCAN_STATUS_KEY } from './../../constants';

export const isRequestedScanNotStarted = ( status: ScanStatus ) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useConnection } from '@automattic/jetpack-connection';
import { ScanStatus } from '@automattic/jetpack-scan';
import { useMutation, UseMutationResult, useQueryClient } from '@tanstack/react-query';
import { __ } from '@wordpress/i18n';
import {
Expand All @@ -10,7 +11,6 @@ import {
SCAN_STATUS_OPTIMISTICALLY_SCANNING,
} from '../constants';
import useNotices from '../hooks/use-notices';
import { ScanStatus } from '../types/scans';

/**
* Connect Site Mutation
Expand Down
7 changes: 3 additions & 4 deletions projects/plugins/protect/src/js/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ThemeProvider } from '@automattic/jetpack-components';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import * as WPElement from '@wordpress/element';
import React, { useEffect } from 'react';
import { useEffect } from 'react';
import { HashRouter, Routes, Route, useLocation, Navigate } from 'react-router-dom';
import Modal from './components/modal';
import PaidPlanGate from './components/paid-plan-gate';
Expand All @@ -12,7 +12,6 @@ import { OnboardingRenderedContextProvider } from './hooks/use-onboarding';
import { CheckoutProvider } from './hooks/use-plan';
import FirewallRoute from './routes/firewall';
import ScanRoute from './routes/scan';
import ScanHistoryRoute from './routes/scan/history';
import SetupRoute from './routes/setup';
import './styles.module.scss';

Expand Down Expand Up @@ -62,15 +61,15 @@ function render() {
path="/scan/history"
element={
<PaidPlanGate>
<ScanHistoryRoute />
<ScanRoute />
</PaidPlanGate>
}
/>
<Route
path="/scan/history/:filter"
element={
<PaidPlanGate>
<ScanHistoryRoute />
<ScanRoute />
</PaidPlanGate>
}
/>
Expand Down
51 changes: 33 additions & 18 deletions projects/plugins/protect/src/js/routes/scan/index.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { AdminSection, Container, Col } from '@automattic/jetpack-components';
import { useMemo } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import AdminPage from '../../components/admin-page';
import ThreatsList from '../../components/threats-list';
import useScanStatusQuery from '../../data/scan/use-scan-status-query';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
import { OnboardingContext } from '../../hooks/use-onboarding';
import usePlan from '../../hooks/use-plan';
import useProtectData from '../../hooks/use-protect-data';
import onboardingSteps from './onboarding-steps';
import ScanAdminSectionHero from './scan-admin-section-hero';
import ScanFooter from './scan-footer';
import ScanResultsDataView from './scan-results-data-view';

/**
* Scan Page
Expand All @@ -19,23 +20,39 @@ import ScanFooter from './scan-footer';
*/
const ScanPage = () => {
const { hasPlan } = usePlan();
const {
counts: {
current: { threats: numThreats },
},
lastChecked,
} = useProtectData();
const location = useLocation();
const { filter } = useParams();
const { data: status } = useScanStatusQuery( { usePolling: true } );

let currentScanStatus;
if ( status.error ) {
currentScanStatus = 'error';
} else if ( ! lastChecked ) {
} else if ( ! status.lastChecked ) {
currentScanStatus = 'in_progress';
} else {
currentScanStatus = 'active';
}

const filters = useMemo( () => {
if ( location.pathname.includes( '/scan/history' ) ) {
return [
{
field: 'status',
value: filter ? [ filter ] : [ 'fixed', 'ignored' ],
operator: 'isAny',
},
];
}

return [
{
field: 'status',
value: 'current',
operator: 'is',
},
];
}, [ filter, location.pathname ] );

// Track view for Protect admin page.
useAnalyticsTracks( {
pageViewEventName: 'protect_admin',
Expand All @@ -49,15 +66,13 @@ const ScanPage = () => {
<OnboardingContext.Provider value={ onboardingSteps }>
<AdminPage>
<ScanAdminSectionHero />
{ ( ! status.error || numThreats ) && (
<AdminSection>
<Container horizontalSpacing={ 7 } horizontalGap={ 4 }>
<Col>
<ThreatsList />
</Col>
</Container>
</AdminSection>
) }
<AdminSection>
<Container horizontalSpacing={ 5 } horizontalGap={ 4 }>
<Col>
<ScanResultsDataView filters={ filters } />
</Col>
</Container>
</AdminSection>
<ScanFooter />
</AdminPage>
</OnboardingContext.Provider>
Expand Down
Loading

0 comments on commit 922fef5

Please sign in to comment.