From 3ac8f5daa01df1f91f87a5a9fa63129255fa19a0 Mon Sep 17 00:00:00 2001 From: dkmyta <43220201+dkmyta@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:35:09 -0800 Subject: [PATCH] ThreatsDataViews: ThreatModal integration (#40202) --- ...nents-threats-data-views-modal-integration | 4 + .../threat-modal/fixer-state-notice.tsx | 36 ++- .../components/threat-modal/index.tsx | 10 + .../threat-modal/stories/index.stories.tsx | 5 +- .../threat-modal/styles.module.scss | 30 ++- .../threat-modal/threat-actions.tsx | 21 +- .../threat-modal/threat-fix-confirmation.tsx | 18 +- .../threat-modal/threat-fix-details.tsx | 13 +- .../threat-modal/threat-ignore-details.tsx | 42 ++++ .../components/threat-modal/threat-notice.tsx | 68 +++--- .../threat-modal/threat-technical-details.tsx | 9 +- .../components/threats-data-views/index.tsx | 179 ++++++++++++--- .../threats-data-views/stories/data.tsx | 129 +++++++++++ .../stories/index.stories.tsx | 217 ++++++------------ .../threats-data-views/styles.module.scss | 9 +- .../threats-data-views/test/index.test.tsx | 23 +- ...nents-threats-data-views-modal-integration | 4 + .../components/ignore-threat-modal/index.jsx | 25 +- .../src/js/data/use-credentials-query.ts | 5 +- .../scan/current-threats-data-views.tsx | 32 +++ .../history/history-admin-section-hero.tsx | 1 - .../scan/history/history-data-views.tsx | 47 ++-- .../src/js/routes/scan/history/index.jsx | 2 +- .../protect/src/js/routes/scan/index.jsx | 4 +- .../src/js/routes/scan/scan-data-views.tsx | 125 ++++++++++ .../plugins/protect/src/js/types/global.d.ts | 2 +- 26 files changed, 747 insertions(+), 313 deletions(-) create mode 100644 projects/js-packages/components/changelog/add-components-threats-data-views-modal-integration create mode 100644 projects/js-packages/components/components/threat-modal/threat-ignore-details.tsx create mode 100644 projects/js-packages/components/components/threats-data-views/stories/data.tsx create mode 100644 projects/js-packages/scan/changelog/add-components-threats-data-views-modal-integration create mode 100644 projects/plugins/protect/src/js/routes/scan/current-threats-data-views.tsx create mode 100644 projects/plugins/protect/src/js/routes/scan/scan-data-views.tsx diff --git a/projects/js-packages/components/changelog/add-components-threats-data-views-modal-integration b/projects/js-packages/components/changelog/add-components-threats-data-views-modal-integration new file mode 100644 index 0000000000000..809bb1cd3a788 --- /dev/null +++ b/projects/js-packages/components/changelog/add-components-threats-data-views-modal-integration @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Integrates ThreatModal in ThreatsDataViews diff --git a/projects/js-packages/components/components/threat-modal/fixer-state-notice.tsx b/projects/js-packages/components/components/threat-modal/fixer-state-notice.tsx index 8f130fba87421..861095cf0c363 100644 --- a/projects/js-packages/components/components/threat-modal/fixer-state-notice.tsx +++ b/projects/js-packages/components/components/threat-modal/fixer-state-notice.tsx @@ -1,5 +1,8 @@ +import { CONTACT_SUPPORT_URL } from '@automattic/jetpack-scan'; +import { createInterpolateElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { useMemo } from 'react'; +import { Button } from '@automattic/jetpack-components'; import styles from './styles.module.scss'; import ThreatNotice from './threat-notice'; @@ -19,14 +22,28 @@ const FixerStateNotice = ( { }: { fixerState: { inProgress: boolean; error: boolean; stale: boolean }; } ) => { + const getInterpolatedContent = (): JSX.Element => { + return createInterpolateElement( + __( 'Please try again or contact support.', 'jetpack-components' ), + { + supportLink: - { isOpen ? : null } + { isOpen ? ( + + ) : null } ); }; diff --git a/projects/js-packages/components/components/threat-modal/styles.module.scss b/projects/js-packages/components/components/threat-modal/styles.module.scss index 1720d59b1f90c..a3cfb182c4eb3 100644 --- a/projects/js-packages/components/components/threat-modal/styles.module.scss +++ b/projects/js-packages/components/components/threat-modal/styles.module.scss @@ -8,19 +8,19 @@ display: flex; flex-direction: column; gap: calc( var( --spacing-base ) * 2 ); // 16px -} - -.section .section__toggle { - text-decoration: none; - - &:hover { - text-decoration: underline; - } - &__content { - display: flex; - gap: calc( var( --spacing-base ) / 2 ); // 4px - align-items: center; + .section__toggle { + text-decoration: none; + + &:hover { + text-decoration: underline; + } + + &__content { + display: flex; + gap: calc( var( --spacing-base ) / 2 ); // 4px + align-items: center; + } } } @@ -43,6 +43,7 @@ .threat-actions { display: flex; justify-content: flex-end; + flex-wrap: wrap; gap: calc( var( --spacing-base ) * 2 ); // 16px; } } @@ -55,10 +56,6 @@ &__title { display: flex; gap: calc( var( --spacing-base ) / 2 ); // 4px - - p { - font-weight: bold; - } } &__actions { @@ -77,5 +74,4 @@ svg.spinner { width: 20px; margin-left: calc( var( --spacing-base ) / 2 ); // 4px; margin-right: 6px; - } \ No newline at end of file diff --git a/projects/js-packages/components/components/threat-modal/threat-actions.tsx b/projects/js-packages/components/components/threat-modal/threat-actions.tsx index e73f0450cc83f..f9feae4f694dc 100644 --- a/projects/js-packages/components/components/threat-modal/threat-actions.tsx +++ b/projects/js-packages/components/components/threat-modal/threat-actions.tsx @@ -15,6 +15,7 @@ const ThreatActions = (): JSX.Element => { const { closeModal, threat, + actionToConfirm, handleFixThreatClick, handleIgnoreThreatClick, handleUnignoreThreatClick, @@ -64,15 +65,17 @@ const ThreatActions = (): JSX.Element => { ) } { threat.status === 'current' && ( <> - - { threat.fixable && ( + { [ 'all', 'ignore' ].includes( actionToConfirm ) && ( + + ) } + { threat.fixable && [ 'all', 'fix' ].includes( actionToConfirm ) && ( - ) } - { siteCredentialsNeeded && ( - - ) } - + { showActions && ( +
+ { userConnectionNeeded && ( + + ) } + { siteCredentialsNeeded && ( + + ) } +
+ ) } } /> diff --git a/projects/js-packages/components/components/threat-modal/threat-technical-details.tsx b/projects/js-packages/components/components/threat-modal/threat-technical-details.tsx index 0e46fb8c38f6f..e2b7c5caa70eb 100644 --- a/projects/js-packages/components/components/threat-modal/threat-technical-details.tsx +++ b/projects/js-packages/components/components/threat-modal/threat-technical-details.tsx @@ -17,6 +17,11 @@ const ThreatTechnicalDetails = (): JSX.Element => { const [ open, setOpen ] = useState( false ); + let toggleContent = __( 'Show the technical details', 'jetpack-components' ); + if ( open ) { + toggleContent = __( 'Hide the technical details', 'jetpack-components' ); + } + const toggleOpen = useCallback( () => { setOpen( ! open ); }, [ open ] ); @@ -37,9 +42,7 @@ const ThreatTechnicalDetails = (): JSX.Element => { >
- { open - ? __( 'Hide the technical details', 'jetpack-components' ) - : __( 'Show the technical details', 'jetpack-components' ) } + { toggleContent }
diff --git a/projects/js-packages/components/components/threats-data-views/index.tsx b/projects/js-packages/components/components/threats-data-views/index.tsx index eead872a61ece..71b385f0bc530 100644 --- a/projects/js-packages/components/components/threats-data-views/index.tsx +++ b/projects/js-packages/components/components/threats-data-views/index.tsx @@ -1,7 +1,6 @@ import { getFixerAction, getThreatType, type Threat } from '@automattic/jetpack-scan'; import { type Action, - type ActionButton, type Field, type FieldType, type Filter, @@ -18,6 +17,7 @@ import { useCallback, useMemo, useState } from 'react'; import Badge from '../badge'; import useBreakpointMatch from '../layout/use-breakpoint-match'; import ThreatFixerButton from '../threat-fixer-button'; +import ThreatModal from '../threat-modal'; import ThreatSeverityBadge from '../threat-severity-badge'; import { CURRENT_TABLE_FIELDS, @@ -53,43 +53,73 @@ export { HISTORIC_TABLE_FIELDS } from './constants'; * @param {string} props.status - Flag to indicate if the threats are current or historic. * @param {Array} props.data - Threats data. * @param {Array} props.initialFilters - Initial DataView filters. - * @param {Array} props.initialFields - Initial DataView fields. - * @param {Function} props.onChangeSelection - Callback function run when an item is selected. + * @param {Array} props.initialFields - Initial DataView fields. - Initial DataView filters. + * @param {boolean} props.isSupportedEnvironment - Whether the environment is supported. + * @param {Function} props.handleUpgradeClick - Callback function run when the upgrade button is clicked. * @param {Function} props.onFixThreats - Threat fix action callback. * @param {Function} props.onIgnoreThreats - Threat ignore action callback. * @param {Function} props.onUnignoreThreats - Threat unignore action callback. * @param {Function} props.isThreatEligibleForFix - Function to determine if a threat is eligible for fixing. * @param {Function} props.isThreatEligibleForIgnore - Function to determine if a threat is eligible for ignoring. * @param {Function} props.isThreatEligibleForUnignore - Function to determine if a threat is eligible for unignoring. - * @param {JSX.Element} props.header - Header component. + * @param {boolean} props.isUserConnected - Whether the user is connected. + * @param {boolean} props.hasConnectedOwner - Whether the site has a connected owner. + * @param {boolean} props.userIsConnecting - Whether the user is connecting. + * @param {Function} props.handleConnectUser - Function to handle the user connection process. + * @param {object[]} props.credentials - The credentials. + * @param {boolean} props.credentialsIsFetching - Whether the credentials are fetching. + * @param {string} props.credentialsRedirectUrl - The credentials redirect URL. + * @param {Function} props.onModalOpen - Callback function on modal open. + * @param {Function} props.onModalClose - Callback function on modal close. + * @param {JSX.Element} props.header - The header element. * * @return {JSX.Element} The ThreatsDataViews component. */ export default function ThreatsDataViews( { status = 'current', data, - initialFields = CURRENT_TABLE_FIELDS, - initialFilters = [], - onChangeSelection, - isThreatEligibleForFix, - isThreatEligibleForIgnore, - isThreatEligibleForUnignore, + initialFields, + initialFilters, + isSupportedEnvironment = true, + handleUpgradeClick, onFixThreats, onIgnoreThreats, onUnignoreThreats, + isThreatEligibleForFix, + isThreatEligibleForIgnore, + isThreatEligibleForUnignore, + isUserConnected, + hasConnectedOwner, + userIsConnecting, + handleConnectUser, + credentials, + credentialsIsFetching, + credentialsRedirectUrl, + onModalOpen, + onModalClose, header, }: { status?: string; data: Threat[]; initialFields?: string[]; initialFilters?: Filter[]; - onChangeSelection?: ( selectedItemIds: string[] ) => void; + isSupportedEnvironment?: boolean; + handleUpgradeClick?: () => void; + onFixThreats?: ( threats: Threat[] ) => void; + onIgnoreThreats?: ( threats: Threat[] ) => void; + onUnignoreThreats?: ( threats: Threat[] ) => void; isThreatEligibleForFix?: ( threat: Threat ) => boolean; isThreatEligibleForIgnore?: ( threat: Threat ) => boolean; isThreatEligibleForUnignore?: ( threat: Threat ) => boolean; - onFixThreats?: ( threats: Threat[] ) => void; - onIgnoreThreats?: ActionButton< Threat >[ 'callback' ]; - onUnignoreThreats?: ActionButton< Threat >[ 'callback' ]; + isUserConnected: boolean; + hasConnectedOwner: boolean; + userIsConnecting: boolean; + handleConnectUser: () => void; + credentials: false | Record< string, unknown >[]; + credentialsIsFetching: boolean; + credentialsRedirectUrl: string; + onModalOpen: () => void; + onModalClose: () => void; header?: JSX.Element; } ): JSX.Element { const [ isSm ] = useBreakpointMatch( [ 'sm', 'lg' ], [ null, '<' ] ); @@ -100,7 +130,7 @@ export default function ThreatsDataViews( { direction: 'desc' as SortDirection, }, search: '', - filters: initialFilters, + filters: initialFilters || [], page: 1, perPage: 20, }; @@ -115,7 +145,7 @@ export default function ThreatsDataViews( { const defaultLayouts: SupportedLayouts = { table: { ...baseView, - fields: initialFields, + fields: initialFields || CURRENT_TABLE_FIELDS, titleField: THREAT_FIELD_TITLE, descriptionField: THREAT_FIELD_DESCRIPTION, showMedia: false, @@ -146,6 +176,24 @@ export default function ThreatsDataViews( { ...defaultLayouts[ defaultViewType ], } ); + const [ selectedThreat, setSelectedThreat ] = useState< Threat | null >( null ); + const [ actionToConfirm, setActionToConfirm ] = useState< string >( 'all' ); + + const showThreatModal = useCallback( + ( threat: Threat, action: string ) => () => { + onModalOpen?.(); + setSelectedThreat( threat ); + setActionToConfirm( action ); + }, + [ onModalOpen ] + ); + + const hideThreatModal = useCallback( () => { + onModalClose?.(); + setSelectedThreat( null ); + setActionToConfirm( 'all' ); + }, [ onModalClose ] ); + /** * Compute values from the provided threats data. * @@ -414,7 +462,13 @@ export default function ThreatsDataViews( { return null; } - return ; + if ( isThreatEligibleForFix && ! isThreatEligibleForFix( item ) ) { + return null; + } + + return ( + + ); }, }, ] @@ -422,7 +476,7 @@ export default function ThreatsDataViews( { ]; return result; - }, [ dataFields, plugins, themes, signatures, status, onFixThreats ] ); + }, [ plugins, themes, dataFields, signatures, status, isThreatEligibleForFix, showThreatModal ] ); /** * DataView actions - collection of operations that can be performed upon each record. @@ -439,7 +493,9 @@ export default function ThreatsDataViews( { return getFixerAction( items[ 0 ] ); }, isPrimary: true, - callback: onFixThreats, + callback: ( items: Threat[] ) => { + showThreatModal( items[ 0 ], 'fix' )(); + }, isEligible( item ) { if ( ! onFixThreats ) { return false; @@ -456,9 +512,9 @@ export default function ThreatsDataViews( { result.push( { id: THREAT_ACTION_IGNORE, label: __( 'Ignore', 'jetpack-components' ), - isPrimary: true, - isDestructive: true, - callback: onIgnoreThreats, + callback: ( items: Threat[] ) => { + showThreatModal( items[ 0 ], 'ignore' )(); + }, isEligible( item ) { if ( ! onIgnoreThreats ) { return false; @@ -475,9 +531,9 @@ export default function ThreatsDataViews( { result.push( { id: THREAT_ACTION_UNIGNORE, label: __( 'Unignore', 'jetpack-components' ), - isPrimary: true, - isDestructive: true, - callback: onUnignoreThreats, + callback: ( items: Threat[] ) => { + showThreatModal( items[ 0 ], 'unignore' )(); + }, isEligible( item ) { if ( ! onUnignoreThreats ) { return false; @@ -494,6 +550,7 @@ export default function ThreatsDataViews( { }, [ view.type, dataFields, + showThreatModal, onFixThreats, onIgnoreThreats, onUnignoreThreats, @@ -521,24 +578,70 @@ export default function ThreatsDataViews( { }, [] ); /** - * DataView getItemId function - returns the unique ID for each record in the dataset. + * DataViews getItemId function - returns the unique ID for each record in the dataset. * * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#getitemid-function */ const getItemId = useCallback( ( item: Threat ) => item.id.toString(), [] ); + /** + * DataViews onClickItem function - render the threat modal on media or primary field click. + */ + const onClickItem = useCallback( + ( item: Threat ) => { + showThreatModal( item, 'all' )(); + }, + [ showThreatModal ] + ); + + /** + * DataViews onChangeSelection function - render the threat modal on list row selection. + */ + const onChangeSelection = useCallback( + ( selectedItemIds: string[] ) => { + const selectedItem = data.find( item => item.id === parseInt( selectedItemIds[ 0 ] ) ); + + if ( selectedItem ) { + showThreatModal( selectedItem, 'all' )(); + } + }, + [ data, showThreatModal ] + ); + return ( - + <> + + { selectedThreat ? ( + + ) : null } + ); } diff --git a/projects/js-packages/components/components/threats-data-views/stories/data.tsx b/projects/js-packages/components/components/threats-data-views/stories/data.tsx new file mode 100644 index 0000000000000..60cdbf63acd77 --- /dev/null +++ b/projects/js-packages/components/components/threats-data-views/stories/data.tsx @@ -0,0 +1,129 @@ +export const currentData = [ + { + id: 185869885, + signature: 'EICAR_AV_Test', + title: 'Malicious code found in file: index.php', + description: + "This is the standard EICAR antivirus test code, and not a real infection. If your site contains this code when you don't expect it to, contact Jetpack support for some help.", + firstDetected: '2024-10-07T20:45:06.000Z', + fixedIn: null, + severity: 8, + fixable: { fixer: 'delete' }, + fixer: { status: 'not_started' }, + status: 'current', + filename: '/var/www/html/wp-content/index.php', + context: { + '1': 'echo << ; Current.args = { - data: [ - { - id: 185869885, - signature: 'EICAR_AV_Test', - title: 'Malicious code found in file: index.php', - description: - "This is the standard EICAR antivirus test code, and not a real infection. If your site contains this code when you don't expect it to, contact Jetpack support for some help.", - firstDetected: '2024-10-07T20:45:06.000Z', - fixedIn: null, - severity: 8, - fixable: { fixer: 'delete' }, - fixer: { status: 'not_started' }, - status: 'current', - filename: '/var/www/html/wp-content/index.php', - context: { - '1': 'echo << + alert( 'Connect user action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + credentials: [ { type: 'managed', role: 'main', still_valid: true } ], + isThreatEligibleForFix: () => true, onFixThreats: () => alert( 'Threat fix action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert onIgnoreThreats: () => alert( 'Ignore threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert - onUnignoreThreats: () => - // eslint-disable-next-line no-alert - alert( - 'Unignore threat action callback triggered! This is handled by the component consumer.' - ), }; export const Historic = args => ; Historic.args = { status: 'historic', - data: [ - { - id: 185869883, - signature: 'Suspicious.Files', - title: 'Malicious code found in file: fuzzy.php', - description: - 'Our security scanners detected that this file is identical to a previously identified malicious file', - firstDetected: '2024-10-07T20:45:06.000Z', - fixedIn: null, - severity: 4, - fixable: false, - status: 'ignored', - filename: '/var/www/html/wp-content/fuzzy.php', - context: '', - }, - { - id: 13216959, - signature: 'Vulnerable.WP.Core', - title: 'Vulnerable WordPress Version (6.4.3)', - description: 'The installed version of WordPress (6.4.3) has a known vulnerability. ', - firstDetected: '2024-07-15T21:56:50.000Z', - severity: 4, - fixedOn: '2024-07-15T22:01:42.000Z', - status: 'fixed', - fixable: false, - version: '6.4.3', - source: '', - }, - ], + data: historicData, initialFields: HISTORIC_TABLE_FIELDS, - onFixThreats: () => - alert( 'Threat fix action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert - onIgnoreThreats: () => - alert( 'Ignore threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + isSupportedEnvironment: true, + isUserConnected: true, + hasConnectedOwner: true, + handleConnectUser: () => + alert( 'Connect user action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + credentials: [ { type: 'managed', role: 'main', still_valid: true } ], onUnignoreThreats: () => // eslint-disable-next-line no-alert alert( @@ -271,15 +150,66 @@ FixerStatuses.args = { }, }, ], + isSupportedEnvironment: true, + isUserConnected: true, + hasConnectedOwner: true, + userIsConnecting: false, + handleConnectUser: () => + alert( 'Connect user action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + credentials: [ { type: 'managed', role: 'main', still_valid: true } ], + credentialsIsFetching: false, + credentialsRedirectUrl: '', + isThreatEligibleForFix: () => true, onFixThreats: () => alert( 'Fix threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert onIgnoreThreats: () => alert( 'Ignore threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert - onUnignoreThreats: () => - // eslint-disable-next-line no-alert - alert( - 'Unignore threat action callback triggered! This is handled by the component consumer.' - ), +}; + +export const AdditionalConnectionsNeeded = args => ; +AdditionalConnectionsNeeded.args = { + data: currentData, + isSupportedEnvironment: true, + isUserConnected: false, + handleConnectUser: () => + alert( 'Connect user action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + isThreatEligibleForFix: () => true, + onFixThreats: () => + alert( 'Threat fix action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + onIgnoreThreats: () => + alert( 'Ignore threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert +}; + +export const UserConnectionNeeded = args => ; +UserConnectionNeeded.args = { + data: currentData, + isSupportedEnvironment: true, + isUserConnected: false, + handleConnectUser: () => + alert( 'Connect user action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + credentials: [ { type: 'managed', role: 'main', still_valid: true } ], + isThreatEligibleForFix: () => true, + onFixThreats: () => + alert( 'Threat fix action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + onIgnoreThreats: () => + alert( 'Ignore threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert +}; + +export const CredentialsRequired = args => ; +CredentialsRequired.args = { + data: currentData, + isSupportedEnvironment: true, + isUserConnected: true, + hasConnectedOwner: true, + handleConnectUser: () => + alert( 'Connect user action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + credentials: false, + credentialsRedirectUrl: '#', + isThreatEligibleForFix: () => true, + onFixThreats: () => + alert( 'Threat fix action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + onIgnoreThreats: () => + alert( 'Ignore threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert }; export const FreeResults = args => ; @@ -329,4 +259,7 @@ FreeResults.args = { }, }, ], + isSupportedEnvironment: true, + handleUpgradeClick: () => + alert( 'Upgrade action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert }; diff --git a/projects/js-packages/components/components/threats-data-views/styles.module.scss b/projects/js-packages/components/components/threats-data-views/styles.module.scss index 7da6717e97f97..a0bbe786a6329 100644 --- a/projects/js-packages/components/components/threats-data-views/styles.module.scss +++ b/projects/js-packages/components/components/threats-data-views/styles.module.scss @@ -1,15 +1,12 @@ @import '@wordpress/dataviews/build-style/style.css'; -.threat__title { - color: var( --jp-gray-80 ); - font-weight: 510; - white-space: initial; +:root { + --spacing-base: 8px; } .threat__description { - color: var( --jp-gray-80 ); font-size: 12px; - white-space: initial; + white-space: wrap; } .threat__fixedOn, diff --git a/projects/js-packages/components/components/threats-data-views/test/index.test.tsx b/projects/js-packages/components/components/threats-data-views/test/index.test.tsx index e5154b472664d..0757479d5c3a9 100644 --- a/projects/js-packages/components/components/threats-data-views/test/index.test.tsx +++ b/projects/js-packages/components/components/threats-data-views/test/index.test.tsx @@ -45,9 +45,30 @@ const data = [ }, ]; +const mockProps = { + filters: [], + isSupportedEnvironment: true, + handleUpgradeClick: () => {}, + onFixThreats: () => {}, + onIgnoreThreats: () => {}, + onUnignoreThreats: () => {}, + isThreatEligibleForFix: () => true, + isThreatEligibleForIgnore: () => true, + isThreatEligibleForUnignore: () => true, + isUserConnected: true, + hasConnectedOwner: true, + userIsConnecting: false, + handleConnectUser: () => {}, + credentials: [], + credentialsIsFetching: false, + credentialsRedirectUrl: '/redirect-url', + onModalOpen: () => {}, + onModalClose: () => {}, +}; + describe( 'ThreatsDataViews', () => { it( 'renders threat data', () => { - render( ); + render( ); expect( screen.getByText( 'Malicious code found in file: index.php' ) ).toBeInTheDocument(); expect( screen.getByText( 'WooCommerce <= 3.2.3 - Authenticated PHP Object Injection' ) diff --git a/projects/js-packages/scan/changelog/add-components-threats-data-views-modal-integration b/projects/js-packages/scan/changelog/add-components-threats-data-views-modal-integration new file mode 100644 index 0000000000000..ccbcd7068d542 --- /dev/null +++ b/projects/js-packages/scan/changelog/add-components-threats-data-views-modal-integration @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Adds utility for retrieving a detailed action description diff --git a/projects/plugins/protect/src/js/components/ignore-threat-modal/index.jsx b/projects/plugins/protect/src/js/components/ignore-threat-modal/index.jsx index 0788eb8bd7a41..1d6f4457b39a4 100644 --- a/projects/plugins/protect/src/js/components/ignore-threat-modal/index.jsx +++ b/projects/plugins/protect/src/js/components/ignore-threat-modal/index.jsx @@ -5,10 +5,12 @@ import { createInterpolateElement, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import useIgnoreThreatMutation from '../../data/scan/use-ignore-threat-mutation'; import useModal from '../../hooks/use-modal'; +import useWafData from '../../hooks/use-waf-data'; import UserConnectionGate from '../user-connection-gate'; import styles from './styles.module.scss'; const IgnoreThreatModal = ( { threat } ) => { + const { wafSupported } = useWafData(); const { setModal } = useModal(); const ignoreThreatMutation = useIgnoreThreatMutation(); const codeableURL = getRedirectUrl( 'jetpack-protect-codeable-referral' ); @@ -54,15 +56,20 @@ const IgnoreThreatModal = ( { threat } ) => { - { createInterpolateElement( - __( - 'By choosing to ignore this threat, you acknowledge that you have reviewed the detected code. You are accepting the risks of maintaining a potentially malicious or vulnerable file on your site. If you are unsure, please request an estimate with Codeable.', - 'jetpack-protect' - ), - { - codeableLink: