Skip to content

Commit

Permalink
Update threats data handling in Protect
Browse files Browse the repository at this point in the history
changelog

Update My Jetpack for latest compatibility with protect-status package

changelog
  • Loading branch information
nateweller committed Oct 27, 2024
1 parent 5ec1327 commit 624c734
Show file tree
Hide file tree
Showing 59 changed files with 681 additions and 3,624 deletions.
38 changes: 8 additions & 30 deletions pnpm-lock.yaml

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

Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { __ } from '@wordpress/i18n';

export const PAID_PLUGIN_SUPPORT_URL = 'https://jetpack.com/contact-support/?rel=support';

export const THREAT_STATUSES = [
{ value: 'current', label: __( 'Active', 'jetpack' ) },
{ value: 'fixed', label: __( 'Fixed', 'jetpack' ) },
{ value: 'ignored', label: __( 'Ignored', 'jetpack' ) },
];
export const THREAT_STATUSES: { value: string; label: string; variant?: 'success' | 'warning' }[] =
[
{ value: 'current', label: __( 'Active', 'jetpack' ), variant: 'warning' },
{ value: 'fixed', label: __( 'Fixed', 'jetpack' ), variant: 'success' },
{ value: 'ignored', label: __( 'Ignored', 'jetpack' ) },
];

export const THREAT_TYPES = [
{ value: 'plugin', label: __( 'Plugin', 'jetpack' ) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default function FixerStatusIcon( {
<IconTooltip
icon={ info }
iconClassName={ styles[ 'icon-info' ] }
iconSize={ 24 }
iconSize={ size }
text={ createInterpolateElement(
__(
'An error occurred auto-fixing this threat. Please try again or <supportLink>contact support</supportLink>.',
Expand Down Expand Up @@ -124,7 +124,7 @@ function FixerStatusText( { fixer }: { fixer?: ThreatFixStatus } ): JSX.Element
export function FixerStatusBadge( { fixer }: { fixer?: ThreatFixStatus } ): JSX.Element {
return (
<div className={ styles[ 'fixer-status' ] }>
<FixerStatusIcon fixer={ fixer } />
<FixerStatusIcon fixer={ fixer } size={ 20 } />
<FixerStatusText fixer={ fixer } />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { Icon } from '@wordpress/components';
import {
Action,
DataViews,
Field,
FieldType,
Filter,
filterSortAndPaginate,
SortDirection,
SupportedLayouts,
type View,
} from '@wordpress/dataviews';
import { dateI18n } from '@wordpress/date';
import { __, _x } from '@wordpress/i18n';
import { Icon } from '@wordpress/icons';
import { useCallback, useMemo, useState } from 'react';
import Badge from '../badge';
import { THREAT_STATUSES, THREAT_TYPES } from './constants';
Expand Down Expand Up @@ -62,7 +64,7 @@ export default function ThreatsDataView( {
search: '',
filters: filters || [],
page: 1,
perPage: 25,
perPage: 20,
};

/**
Expand Down Expand Up @@ -209,6 +211,15 @@ export default function ThreatsDataView( {
THREAT_STATUSES.find( ( { value } ) => value === item.status )?.value ?? item.status
);
},
render( { item }: { item: DataViewThreat } ) {
if ( item.status ) {
const status = THREAT_STATUSES.find( ( { value } ) => value === item.status );
if ( status ) {
return <Badge variant={ status?.variant }>{ status?.label }</Badge>;
}
}
return <Badge variant="warning">{ __( 'Active', 'jetpack' ) }</Badge>;
},
},
{
id: 'extension',
Expand Down Expand Up @@ -322,6 +333,44 @@ export default function ThreatsDataView( {
},
]
: [] ),
...( dataFields.includes( 'firstDetected' )
? [
{
id: 'first-detected',
label: __( 'First Detected', 'jetpack' ),
type: 'datetime' as FieldType,
getValue( { item }: { item: DataViewThreat } ) {
return new Date( item.firstDetected );
},
render( { item }: { item: DataViewThreat } ) {
return (
<span className={ styles.threat__firstDetected }>
{ dateI18n( 'F j Y', item.firstDetected, false ) }
</span>
);
},
},
]
: [] ),
...( dataFields.includes( 'fixedOn' )
? [
{
id: 'fixed-on',
label: __( 'Fixed On', 'jetpack' ),
type: 'datetime' as FieldType,
getValue( { item }: { item: DataViewThreat } ) {
return new Date( item.firstDetected );
},
render( { item }: { item: DataViewThreat } ) {
return (
<span className={ styles.threat__fixedOn }>
{ dateI18n( 'F j Y', item.firstDetected, false ) }
</span>
);
},
},
]
: [] ),
];

return result;
Expand Down Expand Up @@ -411,6 +460,7 @@ export default function ThreatsDataView( {
* @see https://github.com/WordPress/gutenberg/blob/trunk/packages/dataviews/src/filter-and-sort-data-view.ts
*/
const { data: processedData, paginationInfo } = useMemo( () => {
// to do: secondary sort for status, detected on, etc.
return filterSortAndPaginate( data, view, fields );
}, [ data, view, fields ] );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@
font-size: 12px;
}

.threat__fixedOn,
.threat__firstDetected {
white-space: nowrap;
}

.threat__fixedOn {
color: var( --jp-green-70 );
}

.icon-spinner {
svg {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions projects/js-packages/scan/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@wordpress/api-fetch": "7.9.0",
"@wordpress/element": "6.9.0",
"@wordpress/i18n": "5.9.0",
"@wordpress/icons": "10.9.0",
"@wordpress/url": "4.9.0",
"debug": "4.3.4",
"react": "^18.2.0",
Expand Down
1 change: 1 addition & 0 deletions projects/js-packages/scan/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './types/index.js';
export * from './utils.js';
23 changes: 23 additions & 0 deletions projects/js-packages/scan/src/types/fixers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,26 @@ export type ThreatFixStatusSuccess = {
};

export type ThreatFixStatus = ThreatFixStatusError | ThreatFixStatusSuccess;

/**
* Fixers Status
*
* Overall status of all fixers.
*/
type FixersStatusBase = {
ok: boolean; // Discriminator for overall success
};

export type FixersStatusError = FixersStatusBase & {
ok: false;
error: string;
};

export type FixersStatusSuccess = FixersStatusBase & {
ok: true;
threats: {
[ key: number ]: ThreatFixStatus;
};
};

export type FixersStatus = FixersStatusSuccess | FixersStatusError;
8 changes: 8 additions & 0 deletions projects/js-packages/scan/src/types/threat.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,12 @@ export type Threat = {

/** The diff showing the threat's modified file contents. */
diff?: string;

/** The affected extension. */
extension?: {
slug: string;
name: string;
version: string;
type: 'plugin' | 'theme' | 'core';
};
};
77 changes: 77 additions & 0 deletions projects/js-packages/scan/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { code, color, grid, plugins, shield, wordpress } from '@wordpress/icons';
import { ThreatFixStatus } from './types/fixers.js';
import { Threat } from './types/threat.js';

export const getThreatIcon = ( threat: Threat ) => {
const type = getThreatType( threat );

switch ( type ) {
case 'plugin':
return plugins;
case 'theme':
return color;
case 'core':
return wordpress;
case 'file':
return code;
case 'database':
return grid;
default:
return shield;
}
};

export const getThreatType = ( threat: Threat ) => {
if ( threat.signature === 'Vulnerable.WP.Core' ) {
return 'core';
}
if ( threat.extension ) {
return threat.extension.type;
}
if ( threat.filename ) {
return 'file';
}
if ( threat.table ) {
return 'database';
}

return null;
};

export const getThreatSubtitle = ( threat: Threat ) => {
const type = getThreatType( threat );

switch ( type ) {
case 'plugin':
case 'theme':
return `${ threat.extension?.name } (${ threat.extension?.version })`;
case 'core':
return 'WordPress Core';
case 'file':
// Trim leading slash
if ( threat.filename.startsWith( '/' ) ) {
return threat.filename.slice( 1 );
}
return threat.filename;
case 'database':
return threat.table;
default:
return '';
}
};

const FIXER_IS_STALE_THRESHOLD = 1000 * 60 * 60 * 24; // 24 hours

export const fixerTimestampIsStale = ( lastUpdatedTimestamp: string ) => {
const now = new Date();
const lastUpdated = new Date( lastUpdatedTimestamp );
return now.getTime() - lastUpdated.getTime() >= FIXER_IS_STALE_THRESHOLD;
};

export const fixerStatusIsStale = ( fixerStatus: ThreatFixStatus ) => {
return (
'status' in fixerStatus &&
fixerStatus.status === 'in_progress' &&
fixerTimestampIsStale( fixerStatus.last_updated )
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,14 @@ export const ScanAndThreatStatus = () => {
const {
protect: { scanData },
} = getMyJetpackWindowInitialState();
const { plugins, themes, num_threats: numThreats = 0 } = scanData || {};
const numThreats = scanData.threats.length;

const criticalScanThreatCount = useMemo( () => {
const { core, database, files, num_plugins_threats, num_themes_threats } = scanData || {};
const pluginsThreats = num_plugins_threats
? plugins.reduce( ( accum, plugin ) => accum.concat( plugin.threats ), [] )
: [];
const themesThreats = num_themes_threats
? themes.reduce( ( accum, theme ) => accum.concat( theme.threats ), [] )
: [];
const allThreats = [
...pluginsThreats,
...themesThreats,
...( core?.threats ?? [] ),
...database,
...files,
];
return allThreats.reduce(
return scanData.threats.reduce(
( accum, threat ) => ( threat.severity >= 5 ? ( accum += 1 ) : accum ),
0
);
}, [ plugins, themes, scanData ] );
}, [ scanData.threats ] );

if ( isPluginActive && isSiteConnected ) {
if ( hasProtectPaidPlan ) {
Expand Down
Loading

0 comments on commit 624c734

Please sign in to comment.