Skip to content

Commit

Permalink
Add ThreatsDataView
Browse files Browse the repository at this point in the history
Add support for list view in threats data table

Minor adjustments

Add badge component and integrate with threats data view

Update stories and align auto-fix column

Update ThreatDataView list view fixer status (#39854)

Add ThreatsDataView

changelog

Update threats data handling in Protect

changelog

Update My Jetpack for latest compatibility with protect-status package

changelog
  • Loading branch information
nateweller committed Oct 27, 2024
1 parent 25fb234 commit 88440c5
Show file tree
Hide file tree
Showing 58 changed files with 681 additions and 3,588 deletions.
10 changes: 8 additions & 2 deletions pnpm-lock.yaml

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

4 changes: 4 additions & 0 deletions projects/js-packages/scan/changelog/add-threat-types
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add threat TypeScript types
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;
67 changes: 67 additions & 0 deletions projects/js-packages/scan/src/types/threat.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export type ThreatStatus = 'fixed' | 'ignored' | 'current';

export type ThreatFixType = 'replace' | 'delete' | 'update' | string;

export type Threat = {
/** The threat's unique ID. */
id: number;

/** The threat's signature. */
signature: string;

/** The threat's title. */
title: string;

/** The threat's description. */
description: string;

/** The threat's current status. */
status: ThreatStatus;

/** The threat's severity level (0-10). */
severity: number;

/** The date the threat was first detected on the site, in YYYY-MM-DDTHH:MM:SS.000Z format. */
firstDetected: string;

/** The version the threat is fixed in. */
fixedIn?: string | null;

/** The date the threat was fixed, in YYYY-MM-DDTHH:MM:SS.000Z format. */
fixedOn?: string | null;

/** The fixable details. */
fixable:
| {
fixer: ThreatFixType;
target?: string | null;
extensionStatus?: string | null;
}
| false;

/** The threat's source. */
source?: string;

/** The threat's context. */
context?: Record< string, unknown > | null;

/** The name of the affected file. */
filename: string | null;

/** The rows affected by the database threat. */
rows?: unknown;

/** The table name of the database threat. */
table?: string;

/** 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
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ export const useLastScanText = () => {
themes,
protect: { scanData },
} = getMyJetpackWindowInitialState();
const {
plugins: fromScanPlugins,
themes: fromScanThemes,
last_checked: lastScanTime = null,
} = scanData || {};
const { last_checked: lastScanTime = null } = scanData || {};

const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length;
const themesCount = fromScanThemes.length || Object.keys( themes ).length;
const pluginsCount = Object.keys( plugins ).length;
const themesCount = Object.keys( themes ).length;

const timeSinceLastScan = lastScanTime ? timeSince( Date.parse( lastScanTime ) ) : false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,15 @@ export function useProtectTooltipCopy(): TooltipContent {
themes,
protect: { scanData, wafConfig: wafData },
} = getMyJetpackWindowInitialState();
const {
plugins: fromScanPlugins,
themes: fromScanThemes,
num_threats: numThreats = 0,
} = scanData || {};
const numThreats = scanData.threats.length;
const {
jetpack_waf_automatic_rules: isAutoFirewallEnabled,
blocked_logins: blockedLoginsCount,
brute_force_protection: hasBruteForceProtection,
} = wafData || {};

const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length;
const themesCount = fromScanThemes.length || Object.keys( themes ).length;
const pluginsCount = Object.keys( plugins ).length;
const themesCount = Object.keys( themes ).length;

const settingsLink = useMemo( () => {
if ( isProtectPluginActive ) {
Expand Down
5 changes: 5 additions & 0 deletions projects/packages/my-jetpack/changelog/protect-status-compat
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Package compatibility updates, no functional changes.


24 changes: 7 additions & 17 deletions projects/packages/my-jetpack/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ type ThreatItem = {
fixed_in: string;
description: string | null;
source: string | null;
extension: {
slug: string;
name: string;
version: string;
type: 'plugin' | 'theme' | 'core';
};
// Scan API properties (paid plan)
context: string | null;
filename: string | null;
Expand All @@ -58,15 +64,6 @@ type ThreatItem = {
status: number | null;
};

type ScanItem = {
checked: boolean;
name: string;
slug: string;
threats: ThreatItem[];
type: string;
version: string;
};

interface Window {
myJetpackInitialState?: {
siteSuffix: string;
Expand Down Expand Up @@ -211,22 +208,15 @@ interface Window {
};
protect: {
scanData: {
core: ScanItem;
threats: ThreatItem[];
current_progress?: string;
data_source: string;
database: string[];
error: boolean;
error_code?: string;
error_message?: string;
files: string[];
has_unchecked_items: boolean;
last_checked: string;
num_plugins_threats: number;
num_themes_threats: number;
num_threats: number;
plugins: ScanItem[];
status: string;
themes: ScanItem[];
};
wafConfig: {
automatic_rules_available: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: major
Type: changed

Changed the formatting of threat data.
Loading

0 comments on commit 88440c5

Please sign in to comment.