@automattic/jetpack-scan 0.5.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/build/components/index.d.ts +4 -0
- package/build/components/index.js +4 -0
- package/build/components/threat-fixer-button/index.d.ts +16 -0
- package/build/components/threat-fixer-button/index.js +56 -0
- package/build/components/threat-modal/fixer-state-notice.d.ts +19 -0
- package/build/components/threat-modal/fixer-state-notice.js +44 -0
- package/build/components/threat-modal/index.d.ts +51 -0
- package/build/components/threat-modal/index.js +45 -0
- package/build/components/threat-modal/threat-actions.d.ts +7 -0
- package/build/components/threat-modal/threat-actions.js +40 -0
- package/build/components/threat-modal/threat-fix-confirmation.d.ts +7 -0
- package/build/components/threat-modal/threat-fix-confirmation.js +19 -0
- package/build/components/threat-modal/threat-fix-details.d.ts +7 -0
- package/build/components/threat-modal/threat-fix-details.js +40 -0
- package/build/components/threat-modal/threat-notice.d.ts +16 -0
- package/build/components/threat-modal/threat-notice.js +23 -0
- package/build/components/threat-modal/threat-summary.d.ts +7 -0
- package/build/components/threat-modal/threat-summary.js +16 -0
- package/build/components/threat-modal/threat-technical-details.d.ts +7 -0
- package/build/components/threat-modal/threat-technical-details.js +26 -0
- package/build/components/threat-severity-badge/index.d.ts +4 -0
- package/build/components/threat-severity-badge/index.js +13 -0
- package/build/components/threat-severity-badge/test/index.test.d.ts +1 -0
- package/build/components/threat-severity-badge/test/index.test.js +9 -0
- package/build/components/threats-data-views/constants.d.ts +33 -0
- package/build/components/threats-data-views/constants.js +37 -0
- package/build/components/threats-data-views/index.d.ts +29 -0
- package/build/components/threats-data-views/index.js +412 -0
- package/build/components/threats-data-views/test/index.test.d.ts +1 -0
- package/build/components/threats-data-views/test/index.test.js +51 -0
- package/build/components/threats-data-views/threats-status-toggle-group-control.d.ts +15 -0
- package/build/components/threats-data-views/threats-status-toggle-group-control.js +95 -0
- package/build/index.d.ts +4 -3
- package/build/index.js +4 -3
- package/build/types/index.d.ts +3 -3
- package/build/types/index.js +3 -3
- package/build/types/status.d.ts +1 -1
- package/build/types/threats.d.ts +1 -1
- package/build/utils/index.d.ts +2 -2
- package/build/utils/index.js +7 -9
- package/package.json +19 -14
- package/src/components/index.js +4 -0
- package/src/components/threat-fixer-button/index.tsx +101 -0
- package/src/components/threat-fixer-button/styles.module.scss +16 -0
- package/src/components/threat-modal/fixer-state-notice.tsx +63 -0
- package/src/components/threat-modal/index.tsx +109 -0
- package/src/components/threat-modal/styles.module.scss +82 -0
- package/src/components/threat-modal/threat-actions.tsx +93 -0
- package/src/components/threat-modal/threat-fix-confirmation.tsx +54 -0
- package/src/components/threat-modal/threat-fix-details.tsx +64 -0
- package/src/components/threat-modal/threat-notice.tsx +84 -0
- package/src/components/threat-modal/threat-summary.tsx +29 -0
- package/src/components/threat-modal/threat-technical-details.tsx +65 -0
- package/src/components/threat-severity-badge/index.tsx +24 -0
- package/src/components/threat-severity-badge/styles.module.scss +27 -0
- package/src/components/threat-severity-badge/test/index.test.jsx +9 -0
- package/src/components/threats-data-views/constants.ts +49 -0
- package/src/components/threats-data-views/index.tsx +533 -0
- package/src/components/threats-data-views/styles.module.scss +40 -0
- package/src/components/threats-data-views/test/index.test.jsx +56 -0
- package/src/components/threats-data-views/threats-status-toggle-group-control.tsx +158 -0
- package/src/index.ts +4 -3
- package/src/types/index.ts +3 -3
- package/src/types/status.ts +1 -1
- package/src/types/threats.ts +1 -1
- package/src/utils/index.ts +3 -3
- /package/src/{index.test.ts → index.test.js} +0 -0
package/build/utils/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __, sprintf } from '@wordpress/i18n';
|
|
2
|
-
import { FIXER_IS_STALE_THRESHOLD } from
|
|
2
|
+
import { FIXER_IS_STALE_THRESHOLD } from "../constants/index.js";
|
|
3
3
|
export const getThreatType = (threat) => {
|
|
4
4
|
if (threat.signature === 'Vulnerable.WP.Core') {
|
|
5
5
|
return 'core';
|
|
@@ -49,24 +49,23 @@ export const getFixerAction = (threat) => {
|
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
export const getDetailedFixerAction = (threat) => {
|
|
52
|
-
var _a, _b, _c, _d;
|
|
53
52
|
switch (threat.fixable && threat.fixable.fixer) {
|
|
54
53
|
case 'delete':
|
|
55
54
|
if (threat.filename) {
|
|
56
55
|
return __('Delete file', 'jetpack-scan');
|
|
57
56
|
}
|
|
58
|
-
if (
|
|
57
|
+
if (threat.extension?.type === 'plugins') {
|
|
59
58
|
return __('Delete plugin from site', 'jetpack-scan');
|
|
60
59
|
}
|
|
61
|
-
if (
|
|
60
|
+
if (threat.extension?.type === 'themes') {
|
|
62
61
|
return __('Delete theme from site', 'jetpack-scan');
|
|
63
62
|
}
|
|
64
63
|
break;
|
|
65
64
|
case 'update':
|
|
66
|
-
if (
|
|
65
|
+
if (threat.extension?.type === 'plugins') {
|
|
67
66
|
return __('Update plugin to newer version', 'jetpack-scan');
|
|
68
67
|
}
|
|
69
|
-
if (
|
|
68
|
+
if (threat.extension?.type === 'themes') {
|
|
70
69
|
return __('Update theme to newer version', 'jetpack-scan');
|
|
71
70
|
}
|
|
72
71
|
return __('Update', 'jetpack-scan');
|
|
@@ -84,7 +83,6 @@ export const getDetailedFixerAction = (threat) => {
|
|
|
84
83
|
}
|
|
85
84
|
};
|
|
86
85
|
export const getFixerDescription = (threat) => {
|
|
87
|
-
var _a, _b;
|
|
88
86
|
switch (threat.fixable && threat.fixable.fixer) {
|
|
89
87
|
case 'delete':
|
|
90
88
|
if (threat.filename) {
|
|
@@ -96,10 +94,10 @@ export const getFixerDescription = (threat) => {
|
|
|
96
94
|
}
|
|
97
95
|
return __('Delete the infected file.', 'jetpack-scan');
|
|
98
96
|
}
|
|
99
|
-
if (
|
|
97
|
+
if (threat.extension?.type === 'plugins') {
|
|
100
98
|
return __('Delete the plugin directory to fix the threat.', 'jetpack-scan');
|
|
101
99
|
}
|
|
102
|
-
if (
|
|
100
|
+
if (threat.extension?.type === 'themes') {
|
|
103
101
|
return __('Delete the theme directory to fix the threat.', 'jetpack-scan');
|
|
104
102
|
}
|
|
105
103
|
break;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-scan",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"description": "A JS client for consuming Jetpack Scan services",
|
|
6
6
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/scan/#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -18,23 +18,23 @@
|
|
|
18
18
|
"build": "pnpm run clean && pnpm run compile-ts",
|
|
19
19
|
"clean": "rm -rf build/",
|
|
20
20
|
"compile-ts": "tsc --pretty",
|
|
21
|
-
"test": "jest",
|
|
22
|
-
"test-coverage": "pnpm run test --coverage"
|
|
21
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
22
|
+
"test-coverage": "pnpm run test --coverage",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
23
24
|
},
|
|
24
25
|
"type": "module",
|
|
25
26
|
"devDependencies": {
|
|
26
|
-
"@jest/globals": "29.7.0",
|
|
27
27
|
"@storybook/addon-actions": "8.6.7",
|
|
28
28
|
"@storybook/blocks": "8.6.7",
|
|
29
29
|
"@storybook/react": "8.6.7",
|
|
30
30
|
"@testing-library/dom": "10.4.0",
|
|
31
31
|
"@testing-library/react": "16.2.0",
|
|
32
32
|
"@types/jest": "29.5.14",
|
|
33
|
-
"@types/react": "18.3.
|
|
33
|
+
"@types/react": "18.3.23",
|
|
34
34
|
"jest": "^29.7.0",
|
|
35
35
|
"jest-environment-jsdom": "29.7.0",
|
|
36
36
|
"storybook": "8.6.7",
|
|
37
|
-
"typescript": "5.
|
|
37
|
+
"typescript": "5.8.2"
|
|
38
38
|
},
|
|
39
39
|
"exports": {
|
|
40
40
|
".": {
|
|
@@ -45,18 +45,23 @@
|
|
|
45
45
|
"main": "./build/index.js",
|
|
46
46
|
"types": "./build/index.d.ts",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@automattic/jetpack-api": "^0.
|
|
49
|
-
"@automattic/jetpack-base-styles": "^0.
|
|
50
|
-
"@
|
|
51
|
-
"@wordpress/
|
|
52
|
-
"@wordpress/
|
|
53
|
-
"@wordpress/
|
|
54
|
-
"
|
|
48
|
+
"@automattic/jetpack-api": "^1.0.0",
|
|
49
|
+
"@automattic/jetpack-base-styles": "^1.0.0",
|
|
50
|
+
"@automattic/jetpack-components": "^1.0.0",
|
|
51
|
+
"@wordpress/api-fetch": "7.24.0",
|
|
52
|
+
"@wordpress/components": "29.10.0",
|
|
53
|
+
"@wordpress/dataviews": "4.17.0",
|
|
54
|
+
"@wordpress/date": "5.24.0",
|
|
55
|
+
"@wordpress/element": "6.24.0",
|
|
56
|
+
"@wordpress/i18n": "5.24.0",
|
|
57
|
+
"@wordpress/icons": "10.24.0",
|
|
58
|
+
"@wordpress/url": "4.24.0",
|
|
59
|
+
"debug": "4.4.1",
|
|
55
60
|
"react": "^18.2.0",
|
|
56
61
|
"react-dom": "^18.2.0"
|
|
57
62
|
},
|
|
58
63
|
"peerDependencies": {
|
|
59
|
-
"@wordpress/i18n": "5.
|
|
64
|
+
"@wordpress/i18n": "5.24.0",
|
|
60
65
|
"react": "^18.2.0",
|
|
61
66
|
"react-dom": "^18.2.0"
|
|
62
67
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as ThreatFixerButton } from './threat-fixer-button/index.tsx';
|
|
2
|
+
export { default as ThreatModal } from './threat-modal/index.tsx';
|
|
3
|
+
export { default as ThreatSeverityBadge } from './threat-severity-badge/index.tsx';
|
|
4
|
+
export { default as ThreatsDataViews } from './threats-data-views/index.tsx';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Button } from '@automattic/jetpack-components';
|
|
2
|
+
import { Tooltip } from '@wordpress/components';
|
|
3
|
+
import { useCallback, useMemo } from '@wordpress/element';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
import {
|
|
6
|
+
type Threat,
|
|
7
|
+
getFixerState,
|
|
8
|
+
getFixerAction,
|
|
9
|
+
getFixerDescription,
|
|
10
|
+
} from '@automattic/jetpack-scan';
|
|
11
|
+
import styles from './styles.module.scss';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Threat Fixer Button component.
|
|
15
|
+
*
|
|
16
|
+
* @param {object} props - Component props.
|
|
17
|
+
* @param {object} props.threat - The threat.
|
|
18
|
+
* @param {Function} props.onClick - The onClick function.
|
|
19
|
+
* @param {string} props.className - The className.
|
|
20
|
+
*
|
|
21
|
+
* @return {JSX.Element} The component.
|
|
22
|
+
*/
|
|
23
|
+
export default function ThreatFixerButton( {
|
|
24
|
+
threat,
|
|
25
|
+
className,
|
|
26
|
+
onClick,
|
|
27
|
+
}: {
|
|
28
|
+
threat: Threat;
|
|
29
|
+
onClick: ( items: Threat[] ) => void;
|
|
30
|
+
className?: string;
|
|
31
|
+
} ): JSX.Element {
|
|
32
|
+
const fixerState = useMemo( () => {
|
|
33
|
+
return getFixerState( threat.fixer );
|
|
34
|
+
}, [ threat.fixer ] );
|
|
35
|
+
|
|
36
|
+
const tooltipText = useMemo( () => {
|
|
37
|
+
if ( ! threat.fixable ) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if ( fixerState.error ) {
|
|
42
|
+
return __( 'An error occurred auto-fixing this threat.', 'jetpack-scan' );
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ( fixerState.stale ) {
|
|
46
|
+
return __( 'The auto-fixer is taking longer than expected.', 'jetpack-scan' );
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ( fixerState.inProgress ) {
|
|
50
|
+
return __( 'An auto-fixer is in progress.', 'jetpack-scan' );
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return getFixerDescription( threat );
|
|
54
|
+
}, [ threat, fixerState ] );
|
|
55
|
+
|
|
56
|
+
const buttonText = useMemo( () => {
|
|
57
|
+
if ( ! threat.fixable ) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ( fixerState.error ) {
|
|
62
|
+
return __( 'Error', 'jetpack-scan' );
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return getFixerAction( threat );
|
|
66
|
+
}, [ threat, fixerState.error ] );
|
|
67
|
+
|
|
68
|
+
const handleClick = useCallback(
|
|
69
|
+
( event: React.MouseEvent ) => {
|
|
70
|
+
event.stopPropagation();
|
|
71
|
+
onClick( [ threat ] );
|
|
72
|
+
},
|
|
73
|
+
[ onClick, threat ]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if ( ! threat.fixable ) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div>
|
|
82
|
+
<Tooltip className={ styles.tooltip } text={ tooltipText }>
|
|
83
|
+
<Button
|
|
84
|
+
size="small"
|
|
85
|
+
weight="regular"
|
|
86
|
+
variant="secondary"
|
|
87
|
+
onClick={ handleClick }
|
|
88
|
+
children={ buttonText }
|
|
89
|
+
className={ className }
|
|
90
|
+
isLoading={ fixerState.inProgress }
|
|
91
|
+
isDestructive={
|
|
92
|
+
( threat.fixable && threat.fixable.fixer === 'delete' ) ||
|
|
93
|
+
fixerState.error ||
|
|
94
|
+
fixerState.stale
|
|
95
|
+
}
|
|
96
|
+
style={ { minWidth: '72px' } }
|
|
97
|
+
/>
|
|
98
|
+
</Tooltip>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import styles from './styles.module.scss';
|
|
4
|
+
import ThreatNotice from './threat-notice.tsx';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* FixerStateNotice component
|
|
8
|
+
*
|
|
9
|
+
* @param {object} props - The component props.
|
|
10
|
+
* @param {object} props.fixerState - The state of the fixer (inProgress, error, stale).
|
|
11
|
+
* @param {boolean} props.fixerState.inProgress - Whether the fixer is in progress.
|
|
12
|
+
* @param {boolean} props.fixerState.error - Whether the fixer encountered an error.
|
|
13
|
+
* @param {boolean} props.fixerState.stale - Whether the fixer is stale.
|
|
14
|
+
*
|
|
15
|
+
* @return {JSX.Element | null} The rendered fixer notice or null if no notice is available.
|
|
16
|
+
*/
|
|
17
|
+
const FixerStateNotice = ( {
|
|
18
|
+
fixerState,
|
|
19
|
+
}: {
|
|
20
|
+
fixerState: { inProgress: boolean; error: boolean; stale: boolean };
|
|
21
|
+
} ) => {
|
|
22
|
+
const { status, title, content } = useMemo( () => {
|
|
23
|
+
if ( fixerState.error ) {
|
|
24
|
+
return {
|
|
25
|
+
status: 'error' as const,
|
|
26
|
+
title: __( 'An error occurred auto-fixing this threat', 'jetpack-scan' ),
|
|
27
|
+
content: __(
|
|
28
|
+
'Jetpack encountered a filesystem error while attempting to auto-fix this threat. Please try again later or contact support.',
|
|
29
|
+
'jetpack-scan'
|
|
30
|
+
),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ( fixerState.stale ) {
|
|
35
|
+
return {
|
|
36
|
+
status: 'error' as const,
|
|
37
|
+
title: __( 'The auto-fixer is taking longer than expected', 'jetpack-scan' ),
|
|
38
|
+
content: __(
|
|
39
|
+
'Jetpack has been attempting to auto-fix this threat for too long, and something may have gone wrong. Please try again later or contact support.',
|
|
40
|
+
'jetpack-scan'
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ( fixerState.inProgress ) {
|
|
46
|
+
return {
|
|
47
|
+
status: 'success' as const,
|
|
48
|
+
title: __( 'An auto-fixer is in progress', 'jetpack-scan' ),
|
|
49
|
+
content: __( 'Please wait while Jetpack auto-fixes the threat.', 'jetpack-scan' ),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {};
|
|
54
|
+
}, [ fixerState ] );
|
|
55
|
+
|
|
56
|
+
return title ? (
|
|
57
|
+
<div className={ styles[ 'fixer-notice' ] }>
|
|
58
|
+
<ThreatNotice status={ status } title={ title } content={ content } />
|
|
59
|
+
</div>
|
|
60
|
+
) : null;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default FixerStateNotice;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Text } from '@automattic/jetpack-components';
|
|
2
|
+
import { Modal } from '@wordpress/components';
|
|
3
|
+
import { createContext } from 'react';
|
|
4
|
+
import { type Threat } from '@automattic/jetpack-scan';
|
|
5
|
+
import ThreatSeverityBadge from '../threat-severity-badge/index.tsx';
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
import ThreatFixConfirmation from './threat-fix-confirmation.tsx';
|
|
8
|
+
|
|
9
|
+
interface ThreatModalContextType {
|
|
10
|
+
closeModal: () => void;
|
|
11
|
+
threat: Threat;
|
|
12
|
+
handleUpgradeClick?: () => void;
|
|
13
|
+
userConnectionNeeded: boolean;
|
|
14
|
+
handleConnectUser: () => void;
|
|
15
|
+
userIsConnecting: boolean;
|
|
16
|
+
siteCredentialsNeeded: boolean;
|
|
17
|
+
credentialsIsFetching: boolean;
|
|
18
|
+
credentialsRedirectUrl: string;
|
|
19
|
+
handleFixThreatClick?: ( threats: Threat[] ) => void;
|
|
20
|
+
handleIgnoreThreatClick?: ( threats: Threat[] ) => void;
|
|
21
|
+
handleUnignoreThreatClick?: ( threats: Threat[] ) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ThreatModalContext = createContext< ThreatModalContextType | null >( null );
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* ThreatModal component
|
|
28
|
+
*
|
|
29
|
+
* @param {object} props - The props.
|
|
30
|
+
* @param {object} props.threat - The threat.
|
|
31
|
+
* @param {boolean} props.isUserConnected - Whether the user is connected.
|
|
32
|
+
* @param {boolean} props.hasConnectedOwner - Whether the user has a connected owner.
|
|
33
|
+
* @param {boolean} props.userIsConnecting - Whether the user is connecting.
|
|
34
|
+
* @param {Function} props.handleConnectUser - The handleConnectUser function.
|
|
35
|
+
* @param {object} props.credentials - The credentials.
|
|
36
|
+
* @param {boolean} props.credentialsIsFetching - Whether the credentials are fetching.
|
|
37
|
+
* @param {string} props.credentialsRedirectUrl - The credentials redirect URL.
|
|
38
|
+
* @param {Function} props.handleUpgradeClick - The handleUpgradeClick function.
|
|
39
|
+
* @param {Function} props.handleFixThreatClick - The handleFixThreatClick function.
|
|
40
|
+
* @param {Function} props.handleIgnoreThreatClick - The handleIgnoreThreatClick function.
|
|
41
|
+
* @param {Function} props.handleUnignoreThreatClick - The handleUnignoreThreatClick function.
|
|
42
|
+
*
|
|
43
|
+
* @return {JSX.Element} The threat modal.
|
|
44
|
+
*/
|
|
45
|
+
export default function ThreatModal( {
|
|
46
|
+
threat,
|
|
47
|
+
isUserConnected,
|
|
48
|
+
hasConnectedOwner,
|
|
49
|
+
userIsConnecting,
|
|
50
|
+
handleConnectUser,
|
|
51
|
+
credentials,
|
|
52
|
+
credentialsIsFetching,
|
|
53
|
+
credentialsRedirectUrl,
|
|
54
|
+
handleUpgradeClick,
|
|
55
|
+
handleFixThreatClick,
|
|
56
|
+
handleIgnoreThreatClick,
|
|
57
|
+
handleUnignoreThreatClick,
|
|
58
|
+
...modalProps
|
|
59
|
+
}: {
|
|
60
|
+
threat: Threat;
|
|
61
|
+
isUserConnected: boolean;
|
|
62
|
+
hasConnectedOwner: boolean;
|
|
63
|
+
userIsConnecting: boolean;
|
|
64
|
+
handleConnectUser: () => void;
|
|
65
|
+
credentials: false | Record< string, unknown >[];
|
|
66
|
+
credentialsIsFetching: boolean;
|
|
67
|
+
credentialsRedirectUrl: string;
|
|
68
|
+
handleUpgradeClick?: () => void;
|
|
69
|
+
handleFixThreatClick?: ( threats: Threat[] ) => void;
|
|
70
|
+
handleIgnoreThreatClick?: ( threats: Threat[] ) => void;
|
|
71
|
+
handleUnignoreThreatClick?: ( threats: Threat[] ) => void;
|
|
72
|
+
} & React.ComponentProps< typeof Modal > ): JSX.Element {
|
|
73
|
+
const userConnectionNeeded = ! isUserConnected || ! hasConnectedOwner;
|
|
74
|
+
const siteCredentialsNeeded = ! credentials || credentials.length === 0;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Modal
|
|
78
|
+
title={
|
|
79
|
+
<div className={ styles.title }>
|
|
80
|
+
<Text variant="title-small">{ threat.title }</Text>
|
|
81
|
+
{ !! threat.severity && <ThreatSeverityBadge severity={ threat.severity } /> }
|
|
82
|
+
</div>
|
|
83
|
+
}
|
|
84
|
+
size="large"
|
|
85
|
+
{ ...modalProps }
|
|
86
|
+
>
|
|
87
|
+
<div className={ styles[ 'threat-details' ] }>
|
|
88
|
+
<ThreatModalContext.Provider
|
|
89
|
+
value={ {
|
|
90
|
+
closeModal: modalProps.onRequestClose,
|
|
91
|
+
threat,
|
|
92
|
+
handleUpgradeClick,
|
|
93
|
+
userConnectionNeeded,
|
|
94
|
+
handleConnectUser,
|
|
95
|
+
userIsConnecting,
|
|
96
|
+
siteCredentialsNeeded,
|
|
97
|
+
credentialsIsFetching,
|
|
98
|
+
credentialsRedirectUrl,
|
|
99
|
+
handleFixThreatClick,
|
|
100
|
+
handleIgnoreThreatClick,
|
|
101
|
+
handleUnignoreThreatClick,
|
|
102
|
+
} }
|
|
103
|
+
>
|
|
104
|
+
<ThreatFixConfirmation />
|
|
105
|
+
</ThreatModalContext.Provider>
|
|
106
|
+
</div>
|
|
107
|
+
</Modal>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
.threat-details {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: calc( var( --spacing-base ) * 3 ); // 24px
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.section {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
gap: calc( var( --spacing-base ) * 2 ); // 16px
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.section .section__toggle {
|
|
14
|
+
text-decoration: none;
|
|
15
|
+
|
|
16
|
+
&:hover {
|
|
17
|
+
text-decoration: underline;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&__content {
|
|
21
|
+
display: flex;
|
|
22
|
+
gap: calc( var( --spacing-base ) / 2 ); // 4px
|
|
23
|
+
align-items: center;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.title {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: calc( var( --spacing-base ) * 1.5 ); // 12px
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.filename {
|
|
34
|
+
background-color: var( --jp-gray-0 );
|
|
35
|
+
padding: calc( var( --spacing-base ) * 3 ); // 24px
|
|
36
|
+
overflow-x: auto;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.modal-footer {
|
|
40
|
+
padding-top: calc( var( --spacing-base ) * 3 ); // 24px
|
|
41
|
+
border-top: 1px solid var( --jp-gray-5 );
|
|
42
|
+
|
|
43
|
+
.threat-actions {
|
|
44
|
+
display: flex;
|
|
45
|
+
justify-content: flex-end;
|
|
46
|
+
gap: calc( var( --spacing-base ) * 2 ); // 16px;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.fixer-notice {
|
|
51
|
+
padding-bottom: calc( var( --spacing-base ) * 3 ); // 24px
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.notice {
|
|
55
|
+
|
|
56
|
+
&__title {
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: calc( var( --spacing-base ) / 2 ); // 4px
|
|
59
|
+
|
|
60
|
+
p {
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&__actions {
|
|
66
|
+
display: flex;
|
|
67
|
+
gap: calc( var( --spacing-base ) * 2 ); // 16px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&__action {
|
|
71
|
+
margin-top: calc( var( --spacing-base ) * 2 ); // 16px;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
svg.spinner {
|
|
76
|
+
color: var( --jp-black );
|
|
77
|
+
height: 20px;
|
|
78
|
+
width: 20px;
|
|
79
|
+
margin-left: calc( var( --spacing-base ) / 2 ); // 4px;
|
|
80
|
+
margin-right: 6px;
|
|
81
|
+
|
|
82
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Button } from '@automattic/jetpack-components';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import { useCallback, useContext, useMemo } from 'react';
|
|
4
|
+
import { getFixerState, getDetailedFixerAction } from '@automattic/jetpack-scan';
|
|
5
|
+
import FixerStateNotice from './fixer-state-notice.tsx';
|
|
6
|
+
import { ThreatModalContext } from './index.tsx';
|
|
7
|
+
import styles from './styles.module.scss';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ThreatActions component
|
|
11
|
+
*
|
|
12
|
+
* @return {JSX.Element | null} The rendered action buttons or null if no actions are available.
|
|
13
|
+
*/
|
|
14
|
+
const ThreatActions = (): JSX.Element => {
|
|
15
|
+
const {
|
|
16
|
+
closeModal,
|
|
17
|
+
threat,
|
|
18
|
+
handleFixThreatClick,
|
|
19
|
+
handleIgnoreThreatClick,
|
|
20
|
+
handleUnignoreThreatClick,
|
|
21
|
+
userConnectionNeeded,
|
|
22
|
+
siteCredentialsNeeded,
|
|
23
|
+
} = useContext( ThreatModalContext );
|
|
24
|
+
const disabled = userConnectionNeeded || siteCredentialsNeeded;
|
|
25
|
+
|
|
26
|
+
const fixerState = useMemo( () => {
|
|
27
|
+
return getFixerState( threat.fixer );
|
|
28
|
+
}, [ threat.fixer ] );
|
|
29
|
+
|
|
30
|
+
const detailedFixerAction = useMemo( () => getDetailedFixerAction( threat ), [ threat ] );
|
|
31
|
+
|
|
32
|
+
const onFixClick = useCallback( () => {
|
|
33
|
+
handleFixThreatClick?.( [ threat ] );
|
|
34
|
+
closeModal();
|
|
35
|
+
}, [ threat, handleFixThreatClick, closeModal ] );
|
|
36
|
+
|
|
37
|
+
const onIgnoreClick = useCallback( () => {
|
|
38
|
+
handleIgnoreThreatClick?.( [ threat ] );
|
|
39
|
+
closeModal();
|
|
40
|
+
}, [ threat, handleIgnoreThreatClick, closeModal ] );
|
|
41
|
+
|
|
42
|
+
const onUnignoreClick = useCallback( () => {
|
|
43
|
+
handleUnignoreThreatClick?.( [ threat ] );
|
|
44
|
+
closeModal();
|
|
45
|
+
}, [ threat, handleUnignoreThreatClick, closeModal ] );
|
|
46
|
+
|
|
47
|
+
if ( ! threat?.status || threat.status === 'fixed' ) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className={ styles[ 'modal-footer' ] }>
|
|
53
|
+
<FixerStateNotice fixerState={ fixerState } />
|
|
54
|
+
<div className={ styles[ 'threat-actions' ] }>
|
|
55
|
+
{ threat.status === 'ignored' && (
|
|
56
|
+
<Button
|
|
57
|
+
disabled={ disabled }
|
|
58
|
+
isDestructive={ true }
|
|
59
|
+
variant="secondary"
|
|
60
|
+
onClick={ onUnignoreClick }
|
|
61
|
+
>
|
|
62
|
+
{ __( 'Un-ignore threat', 'jetpack-scan' ) }
|
|
63
|
+
</Button>
|
|
64
|
+
) }
|
|
65
|
+
{ threat.status === 'current' && (
|
|
66
|
+
<>
|
|
67
|
+
<Button
|
|
68
|
+
isDestructive={ true }
|
|
69
|
+
variant="secondary"
|
|
70
|
+
onClick={ onIgnoreClick }
|
|
71
|
+
disabled={ disabled || ( fixerState.inProgress && ! fixerState.stale ) }
|
|
72
|
+
>
|
|
73
|
+
{ __( 'Ignore threat', 'jetpack-scan' ) }
|
|
74
|
+
</Button>
|
|
75
|
+
{ threat.fixable && (
|
|
76
|
+
<Button
|
|
77
|
+
isPrimary
|
|
78
|
+
disabled={ disabled || ( fixerState.inProgress && ! fixerState.stale ) }
|
|
79
|
+
onClick={ onFixClick }
|
|
80
|
+
>
|
|
81
|
+
{ fixerState.error || fixerState.stale
|
|
82
|
+
? __( 'Retry fixer', 'jetpack-scan' )
|
|
83
|
+
: detailedFixerAction }
|
|
84
|
+
</Button>
|
|
85
|
+
) }
|
|
86
|
+
</>
|
|
87
|
+
) }
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default ThreatActions;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { ThreatModalContext } from './index.tsx';
|
|
4
|
+
import ThreatActions from './threat-actions.tsx';
|
|
5
|
+
import ThreatFixDetails from './threat-fix-details.tsx';
|
|
6
|
+
import ThreatNotice from './threat-notice.tsx';
|
|
7
|
+
import ThreatSummary from './threat-summary.tsx';
|
|
8
|
+
import ThreatTechnicalDetails from './threat-technical-details.tsx';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ThreatFixConfirmation component
|
|
12
|
+
*
|
|
13
|
+
* @return {JSX.Element} The rendered fix confirmation.
|
|
14
|
+
*/
|
|
15
|
+
const ThreatFixConfirmation = () => {
|
|
16
|
+
const { userConnectionNeeded, siteCredentialsNeeded } = useContext( ThreatModalContext );
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<ThreatSummary />
|
|
20
|
+
<ThreatTechnicalDetails />
|
|
21
|
+
<ThreatFixDetails />
|
|
22
|
+
{ siteCredentialsNeeded && userConnectionNeeded && (
|
|
23
|
+
<ThreatNotice
|
|
24
|
+
title={ 'Additional connections needed' }
|
|
25
|
+
content={ __(
|
|
26
|
+
'A user connection and server credentials provide Jetpack the access necessary to ignore and auto-fix threats on your site.',
|
|
27
|
+
'jetpack-scan'
|
|
28
|
+
) }
|
|
29
|
+
/>
|
|
30
|
+
) }
|
|
31
|
+
{ ! siteCredentialsNeeded && userConnectionNeeded && (
|
|
32
|
+
<ThreatNotice
|
|
33
|
+
title={ __( 'User connection needed', 'jetpack-scan' ) }
|
|
34
|
+
content={ __(
|
|
35
|
+
'A user connection provides Jetpack the access necessary to ignore and auto-fix threats on your site.',
|
|
36
|
+
'jetpack-scan'
|
|
37
|
+
) }
|
|
38
|
+
/>
|
|
39
|
+
) }
|
|
40
|
+
{ siteCredentialsNeeded && ! userConnectionNeeded && (
|
|
41
|
+
<ThreatNotice
|
|
42
|
+
title={ __( 'Site credentials needed', 'jetpack-scan' ) }
|
|
43
|
+
content={ __(
|
|
44
|
+
'Your server credentials allow Jetpack to access the server that’s powering your website. This information is securely saved and only used to ignore and auto-fix threats detected on your site.',
|
|
45
|
+
'jetpack-scan'
|
|
46
|
+
) }
|
|
47
|
+
/>
|
|
48
|
+
) }
|
|
49
|
+
<ThreatActions />
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default ThreatFixConfirmation;
|