@automattic/jetpack-scan 0.5.9 → 1.0.1

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/build/components/index.d.ts +4 -0
  3. package/build/components/index.js +4 -0
  4. package/build/components/threat-fixer-button/index.d.ts +16 -0
  5. package/build/components/threat-fixer-button/index.js +56 -0
  6. package/build/components/threat-modal/fixer-state-notice.d.ts +19 -0
  7. package/build/components/threat-modal/fixer-state-notice.js +44 -0
  8. package/build/components/threat-modal/index.d.ts +51 -0
  9. package/build/components/threat-modal/index.js +45 -0
  10. package/build/components/threat-modal/threat-actions.d.ts +7 -0
  11. package/build/components/threat-modal/threat-actions.js +40 -0
  12. package/build/components/threat-modal/threat-fix-confirmation.d.ts +7 -0
  13. package/build/components/threat-modal/threat-fix-confirmation.js +19 -0
  14. package/build/components/threat-modal/threat-fix-details.d.ts +7 -0
  15. package/build/components/threat-modal/threat-fix-details.js +40 -0
  16. package/build/components/threat-modal/threat-notice.d.ts +16 -0
  17. package/build/components/threat-modal/threat-notice.js +23 -0
  18. package/build/components/threat-modal/threat-summary.d.ts +7 -0
  19. package/build/components/threat-modal/threat-summary.js +16 -0
  20. package/build/components/threat-modal/threat-technical-details.d.ts +7 -0
  21. package/build/components/threat-modal/threat-technical-details.js +26 -0
  22. package/build/components/threat-severity-badge/index.d.ts +4 -0
  23. package/build/components/threat-severity-badge/index.js +13 -0
  24. package/build/components/threat-severity-badge/test/index.test.d.ts +1 -0
  25. package/build/components/threat-severity-badge/test/index.test.js +9 -0
  26. package/build/components/threats-data-views/constants.d.ts +33 -0
  27. package/build/components/threats-data-views/constants.js +37 -0
  28. package/build/components/threats-data-views/index.d.ts +29 -0
  29. package/build/components/threats-data-views/index.js +412 -0
  30. package/build/components/threats-data-views/test/index.test.d.ts +1 -0
  31. package/build/components/threats-data-views/test/index.test.js +51 -0
  32. package/build/components/threats-data-views/threats-status-toggle-group-control.d.ts +15 -0
  33. package/build/components/threats-data-views/threats-status-toggle-group-control.js +95 -0
  34. package/build/index.d.ts +4 -3
  35. package/build/index.js +4 -3
  36. package/build/types/index.d.ts +3 -3
  37. package/build/types/index.js +3 -3
  38. package/build/types/status.d.ts +1 -1
  39. package/build/types/threats.d.ts +1 -1
  40. package/build/utils/index.d.ts +2 -2
  41. package/build/utils/index.js +7 -9
  42. package/package.json +19 -14
  43. package/src/components/index.js +4 -0
  44. package/src/components/threat-fixer-button/index.tsx +101 -0
  45. package/src/components/threat-fixer-button/styles.module.scss +16 -0
  46. package/src/components/threat-modal/fixer-state-notice.tsx +63 -0
  47. package/src/components/threat-modal/index.tsx +109 -0
  48. package/src/components/threat-modal/styles.module.scss +82 -0
  49. package/src/components/threat-modal/threat-actions.tsx +93 -0
  50. package/src/components/threat-modal/threat-fix-confirmation.tsx +54 -0
  51. package/src/components/threat-modal/threat-fix-details.tsx +64 -0
  52. package/src/components/threat-modal/threat-notice.tsx +84 -0
  53. package/src/components/threat-modal/threat-summary.tsx +29 -0
  54. package/src/components/threat-modal/threat-technical-details.tsx +65 -0
  55. package/src/components/threat-severity-badge/index.tsx +24 -0
  56. package/src/components/threat-severity-badge/styles.module.scss +27 -0
  57. package/src/components/threat-severity-badge/test/index.test.jsx +9 -0
  58. package/src/components/threats-data-views/constants.ts +49 -0
  59. package/src/components/threats-data-views/index.tsx +533 -0
  60. package/src/components/threats-data-views/styles.module.scss +40 -0
  61. package/src/components/threats-data-views/test/index.test.jsx +56 -0
  62. package/src/components/threats-data-views/threats-status-toggle-group-control.tsx +158 -0
  63. package/src/index.ts +4 -3
  64. package/src/types/index.ts +3 -3
  65. package/src/types/status.ts +1 -1
  66. package/src/types/threats.ts +1 -1
  67. package/src/utils/index.ts +3 -3
  68. /package/src/{index.test.ts → index.test.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.1] - 2025-06-05
9
+ ### Changed
10
+ - Update package dependencies. [#43766]
11
+
12
+ ## [1.0.0] - 2025-06-03
13
+ ### Added
14
+ - Add threat components. [#41654]
15
+
16
+ ### Changed
17
+ - Adjust relative imports in TypeScript sources to use correct extensions after enabling TypeScript's new `rewriteRelativeImportExtensions` option. The built JS should be unaffected, so this is not a breaking change. [#42990]
18
+ - Code: First pass of style coding standards. [#42734]
19
+ - Update package dependencies. [#42762] [#42806] [#42809] [#43326] [#43578] [#43711] [#43718]
20
+
21
+ ### Fixed
22
+ - Code: Update stylesheets to use WordPress font styles. [#42928]
23
+ - Fixed TS type checking in the monorepo [#42817]
24
+
8
25
  ## [0.5.9] - 2025-03-18
9
26
  ### Changed
10
27
  - Update package dependencies. [#42509]
@@ -115,6 +132,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
115
132
  ### Removed
116
133
  - Updated dependencies. [#39754]
117
134
 
135
+ [1.0.1]: https://github.com/Automattic/jetpack-scan/compare/v1.0.0...v1.0.1
136
+ [1.0.0]: https://github.com/Automattic/jetpack-scan/compare/v0.5.9...v1.0.0
118
137
  [0.5.9]: https://github.com/Automattic/jetpack-scan/compare/v0.5.8...v0.5.9
119
138
  [0.5.8]: https://github.com/Automattic/jetpack-scan/compare/v0.5.7...v0.5.8
120
139
  [0.5.7]: https://github.com/Automattic/jetpack-scan/compare/v0.5.6...v0.5.7
@@ -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,4 @@
1
+ export { default as ThreatFixerButton } from "./threat-fixer-button/index.js";
2
+ export { default as ThreatModal } from "./threat-modal/index.js";
3
+ export { default as ThreatSeverityBadge } from "./threat-severity-badge/index.js";
4
+ export { default as ThreatsDataViews } from "./threats-data-views/index.js";
@@ -0,0 +1,16 @@
1
+ import { type Threat } from '@automattic/jetpack-scan';
2
+ /**
3
+ * Threat Fixer Button component.
4
+ *
5
+ * @param {object} props - Component props.
6
+ * @param {object} props.threat - The threat.
7
+ * @param {Function} props.onClick - The onClick function.
8
+ * @param {string} props.className - The className.
9
+ *
10
+ * @return {JSX.Element} The component.
11
+ */
12
+ export default function ThreatFixerButton({ threat, className, onClick, }: {
13
+ threat: Threat;
14
+ onClick: (items: Threat[]) => void;
15
+ className?: string;
16
+ }): JSX.Element;
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Button } from '@automattic/jetpack-components';
3
+ import { Tooltip } from '@wordpress/components';
4
+ import { useCallback, useMemo } from '@wordpress/element';
5
+ import { __ } from '@wordpress/i18n';
6
+ import { getFixerState, getFixerAction, getFixerDescription, } from '@automattic/jetpack-scan';
7
+ import styles from './styles.module.scss';
8
+ /**
9
+ * Threat Fixer Button component.
10
+ *
11
+ * @param {object} props - Component props.
12
+ * @param {object} props.threat - The threat.
13
+ * @param {Function} props.onClick - The onClick function.
14
+ * @param {string} props.className - The className.
15
+ *
16
+ * @return {JSX.Element} The component.
17
+ */
18
+ export default function ThreatFixerButton({ threat, className, onClick, }) {
19
+ const fixerState = useMemo(() => {
20
+ return getFixerState(threat.fixer);
21
+ }, [threat.fixer]);
22
+ const tooltipText = useMemo(() => {
23
+ if (!threat.fixable) {
24
+ return null;
25
+ }
26
+ if (fixerState.error) {
27
+ return __('An error occurred auto-fixing this threat.', 'jetpack-scan');
28
+ }
29
+ if (fixerState.stale) {
30
+ return __('The auto-fixer is taking longer than expected.', 'jetpack-scan');
31
+ }
32
+ if (fixerState.inProgress) {
33
+ return __('An auto-fixer is in progress.', 'jetpack-scan');
34
+ }
35
+ return getFixerDescription(threat);
36
+ }, [threat, fixerState]);
37
+ const buttonText = useMemo(() => {
38
+ if (!threat.fixable) {
39
+ return null;
40
+ }
41
+ if (fixerState.error) {
42
+ return __('Error', 'jetpack-scan');
43
+ }
44
+ return getFixerAction(threat);
45
+ }, [threat, fixerState.error]);
46
+ const handleClick = useCallback((event) => {
47
+ event.stopPropagation();
48
+ onClick([threat]);
49
+ }, [onClick, threat]);
50
+ if (!threat.fixable) {
51
+ return null;
52
+ }
53
+ return (_jsx("div", { children: _jsx(Tooltip, { className: styles.tooltip, text: tooltipText, children: _jsx(Button, { size: "small", weight: "regular", variant: "secondary", onClick: handleClick, children: buttonText, className: className, isLoading: fixerState.inProgress, isDestructive: (threat.fixable && threat.fixable.fixer === 'delete') ||
54
+ fixerState.error ||
55
+ fixerState.stale, style: { minWidth: '72px' } }) }) }));
56
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * FixerStateNotice component
3
+ *
4
+ * @param {object} props - The component props.
5
+ * @param {object} props.fixerState - The state of the fixer (inProgress, error, stale).
6
+ * @param {boolean} props.fixerState.inProgress - Whether the fixer is in progress.
7
+ * @param {boolean} props.fixerState.error - Whether the fixer encountered an error.
8
+ * @param {boolean} props.fixerState.stale - Whether the fixer is stale.
9
+ *
10
+ * @return {JSX.Element | null} The rendered fixer notice or null if no notice is available.
11
+ */
12
+ declare const FixerStateNotice: ({ fixerState, }: {
13
+ fixerState: {
14
+ inProgress: boolean;
15
+ error: boolean;
16
+ stale: boolean;
17
+ };
18
+ }) => import("react/jsx-runtime").JSX.Element;
19
+ export default FixerStateNotice;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { __ } from '@wordpress/i18n';
3
+ import { useMemo } from 'react';
4
+ import styles from './styles.module.scss';
5
+ import ThreatNotice from "./threat-notice.js";
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 = ({ fixerState, }) => {
18
+ const { status, title, content } = useMemo(() => {
19
+ if (fixerState.error) {
20
+ return {
21
+ status: 'error',
22
+ title: __('An error occurred auto-fixing this threat', 'jetpack-scan'),
23
+ content: __('Jetpack encountered a filesystem error while attempting to auto-fix this threat. Please try again later or contact support.', 'jetpack-scan'),
24
+ };
25
+ }
26
+ if (fixerState.stale) {
27
+ return {
28
+ status: 'error',
29
+ title: __('The auto-fixer is taking longer than expected', 'jetpack-scan'),
30
+ content: __('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.', 'jetpack-scan'),
31
+ };
32
+ }
33
+ if (fixerState.inProgress) {
34
+ return {
35
+ status: 'success',
36
+ title: __('An auto-fixer is in progress', 'jetpack-scan'),
37
+ content: __('Please wait while Jetpack auto-fixes the threat.', 'jetpack-scan'),
38
+ };
39
+ }
40
+ return {};
41
+ }, [fixerState]);
42
+ return title ? (_jsx("div", { className: styles['fixer-notice'], children: _jsx(ThreatNotice, { status: status, title: title, content: content }) })) : null;
43
+ };
44
+ export default FixerStateNotice;
@@ -0,0 +1,51 @@
1
+ import { Modal } from '@wordpress/components';
2
+ import { type Threat } from '@automattic/jetpack-scan';
3
+ interface ThreatModalContextType {
4
+ closeModal: () => void;
5
+ threat: Threat;
6
+ handleUpgradeClick?: () => void;
7
+ userConnectionNeeded: boolean;
8
+ handleConnectUser: () => void;
9
+ userIsConnecting: boolean;
10
+ siteCredentialsNeeded: boolean;
11
+ credentialsIsFetching: boolean;
12
+ credentialsRedirectUrl: string;
13
+ handleFixThreatClick?: (threats: Threat[]) => void;
14
+ handleIgnoreThreatClick?: (threats: Threat[]) => void;
15
+ handleUnignoreThreatClick?: (threats: Threat[]) => void;
16
+ }
17
+ export declare const ThreatModalContext: import("react").Context<ThreatModalContextType>;
18
+ /**
19
+ * ThreatModal component
20
+ *
21
+ * @param {object} props - The props.
22
+ * @param {object} props.threat - The threat.
23
+ * @param {boolean} props.isUserConnected - Whether the user is connected.
24
+ * @param {boolean} props.hasConnectedOwner - Whether the user has a connected owner.
25
+ * @param {boolean} props.userIsConnecting - Whether the user is connecting.
26
+ * @param {Function} props.handleConnectUser - The handleConnectUser function.
27
+ * @param {object} props.credentials - The credentials.
28
+ * @param {boolean} props.credentialsIsFetching - Whether the credentials are fetching.
29
+ * @param {string} props.credentialsRedirectUrl - The credentials redirect URL.
30
+ * @param {Function} props.handleUpgradeClick - The handleUpgradeClick function.
31
+ * @param {Function} props.handleFixThreatClick - The handleFixThreatClick function.
32
+ * @param {Function} props.handleIgnoreThreatClick - The handleIgnoreThreatClick function.
33
+ * @param {Function} props.handleUnignoreThreatClick - The handleUnignoreThreatClick function.
34
+ *
35
+ * @return {JSX.Element} The threat modal.
36
+ */
37
+ export default function ThreatModal({ threat, isUserConnected, hasConnectedOwner, userIsConnecting, handleConnectUser, credentials, credentialsIsFetching, credentialsRedirectUrl, handleUpgradeClick, handleFixThreatClick, handleIgnoreThreatClick, handleUnignoreThreatClick, ...modalProps }: {
38
+ threat: Threat;
39
+ isUserConnected: boolean;
40
+ hasConnectedOwner: boolean;
41
+ userIsConnecting: boolean;
42
+ handleConnectUser: () => void;
43
+ credentials: false | Record<string, unknown>[];
44
+ credentialsIsFetching: boolean;
45
+ credentialsRedirectUrl: string;
46
+ handleUpgradeClick?: () => void;
47
+ handleFixThreatClick?: (threats: Threat[]) => void;
48
+ handleIgnoreThreatClick?: (threats: Threat[]) => void;
49
+ handleUnignoreThreatClick?: (threats: Threat[]) => void;
50
+ } & React.ComponentProps<typeof Modal>): JSX.Element;
51
+ export {};
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text } from '@automattic/jetpack-components';
3
+ import { Modal } from '@wordpress/components';
4
+ import { createContext } from 'react';
5
+ import ThreatSeverityBadge from "../threat-severity-badge/index.js";
6
+ import styles from './styles.module.scss';
7
+ import ThreatFixConfirmation from "./threat-fix-confirmation.js";
8
+ export const ThreatModalContext = createContext(null);
9
+ /**
10
+ * ThreatModal component
11
+ *
12
+ * @param {object} props - The props.
13
+ * @param {object} props.threat - The threat.
14
+ * @param {boolean} props.isUserConnected - Whether the user is connected.
15
+ * @param {boolean} props.hasConnectedOwner - Whether the user has a connected owner.
16
+ * @param {boolean} props.userIsConnecting - Whether the user is connecting.
17
+ * @param {Function} props.handleConnectUser - The handleConnectUser function.
18
+ * @param {object} props.credentials - The credentials.
19
+ * @param {boolean} props.credentialsIsFetching - Whether the credentials are fetching.
20
+ * @param {string} props.credentialsRedirectUrl - The credentials redirect URL.
21
+ * @param {Function} props.handleUpgradeClick - The handleUpgradeClick function.
22
+ * @param {Function} props.handleFixThreatClick - The handleFixThreatClick function.
23
+ * @param {Function} props.handleIgnoreThreatClick - The handleIgnoreThreatClick function.
24
+ * @param {Function} props.handleUnignoreThreatClick - The handleUnignoreThreatClick function.
25
+ *
26
+ * @return {JSX.Element} The threat modal.
27
+ */
28
+ export default function ThreatModal({ threat, isUserConnected, hasConnectedOwner, userIsConnecting, handleConnectUser, credentials, credentialsIsFetching, credentialsRedirectUrl, handleUpgradeClick, handleFixThreatClick, handleIgnoreThreatClick, handleUnignoreThreatClick, ...modalProps }) {
29
+ const userConnectionNeeded = !isUserConnected || !hasConnectedOwner;
30
+ const siteCredentialsNeeded = !credentials || credentials.length === 0;
31
+ return (_jsx(Modal, { title: _jsxs("div", { className: styles.title, children: [_jsx(Text, { variant: "title-small", children: threat.title }), !!threat.severity && _jsx(ThreatSeverityBadge, { severity: threat.severity })] }), size: "large", ...modalProps, children: _jsx("div", { className: styles['threat-details'], children: _jsx(ThreatModalContext.Provider, { value: {
32
+ closeModal: modalProps.onRequestClose,
33
+ threat,
34
+ handleUpgradeClick,
35
+ userConnectionNeeded,
36
+ handleConnectUser,
37
+ userIsConnecting,
38
+ siteCredentialsNeeded,
39
+ credentialsIsFetching,
40
+ credentialsRedirectUrl,
41
+ handleFixThreatClick,
42
+ handleIgnoreThreatClick,
43
+ handleUnignoreThreatClick,
44
+ }, children: _jsx(ThreatFixConfirmation, {}) }) }) }));
45
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ThreatActions component
3
+ *
4
+ * @return {JSX.Element | null} The rendered action buttons or null if no actions are available.
5
+ */
6
+ declare const ThreatActions: () => JSX.Element;
7
+ export default ThreatActions;
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@automattic/jetpack-components';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { useCallback, useContext, useMemo } from 'react';
5
+ import { getFixerState, getDetailedFixerAction } from '@automattic/jetpack-scan';
6
+ import FixerStateNotice from "./fixer-state-notice.js";
7
+ import { ThreatModalContext } from "./index.js";
8
+ import styles from './styles.module.scss';
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 = () => {
15
+ const { closeModal, threat, handleFixThreatClick, handleIgnoreThreatClick, handleUnignoreThreatClick, userConnectionNeeded, siteCredentialsNeeded, } = useContext(ThreatModalContext);
16
+ const disabled = userConnectionNeeded || siteCredentialsNeeded;
17
+ const fixerState = useMemo(() => {
18
+ return getFixerState(threat.fixer);
19
+ }, [threat.fixer]);
20
+ const detailedFixerAction = useMemo(() => getDetailedFixerAction(threat), [threat]);
21
+ const onFixClick = useCallback(() => {
22
+ handleFixThreatClick?.([threat]);
23
+ closeModal();
24
+ }, [threat, handleFixThreatClick, closeModal]);
25
+ const onIgnoreClick = useCallback(() => {
26
+ handleIgnoreThreatClick?.([threat]);
27
+ closeModal();
28
+ }, [threat, handleIgnoreThreatClick, closeModal]);
29
+ const onUnignoreClick = useCallback(() => {
30
+ handleUnignoreThreatClick?.([threat]);
31
+ closeModal();
32
+ }, [threat, handleUnignoreThreatClick, closeModal]);
33
+ if (!threat?.status || threat.status === 'fixed') {
34
+ return null;
35
+ }
36
+ return (_jsxs("div", { className: styles['modal-footer'], children: [_jsx(FixerStateNotice, { fixerState: fixerState }), _jsxs("div", { className: styles['threat-actions'], children: [threat.status === 'ignored' && (_jsx(Button, { disabled: disabled, isDestructive: true, variant: "secondary", onClick: onUnignoreClick, children: __('Un-ignore threat', 'jetpack-scan') })), threat.status === 'current' && (_jsxs(_Fragment, { children: [_jsx(Button, { isDestructive: true, variant: "secondary", onClick: onIgnoreClick, disabled: disabled || (fixerState.inProgress && !fixerState.stale), children: __('Ignore threat', 'jetpack-scan') }), threat.fixable && (_jsx(Button, { isPrimary: true, disabled: disabled || (fixerState.inProgress && !fixerState.stale), onClick: onFixClick, children: fixerState.error || fixerState.stale
37
+ ? __('Retry fixer', 'jetpack-scan')
38
+ : detailedFixerAction }))] }))] })] }));
39
+ };
40
+ export default ThreatActions;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ThreatFixConfirmation component
3
+ *
4
+ * @return {JSX.Element} The rendered fix confirmation.
5
+ */
6
+ declare const ThreatFixConfirmation: () => import("react/jsx-runtime").JSX.Element;
7
+ export default ThreatFixConfirmation;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { __ } from '@wordpress/i18n';
3
+ import { useContext } from 'react';
4
+ import { ThreatModalContext } from "./index.js";
5
+ import ThreatActions from "./threat-actions.js";
6
+ import ThreatFixDetails from "./threat-fix-details.js";
7
+ import ThreatNotice from "./threat-notice.js";
8
+ import ThreatSummary from "./threat-summary.js";
9
+ import ThreatTechnicalDetails from "./threat-technical-details.js";
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 (_jsxs(_Fragment, { children: [_jsx(ThreatSummary, {}), _jsx(ThreatTechnicalDetails, {}), _jsx(ThreatFixDetails, {}), siteCredentialsNeeded && userConnectionNeeded && (_jsx(ThreatNotice, { title: 'Additional connections needed', content: __('A user connection and server credentials provide Jetpack the access necessary to ignore and auto-fix threats on your site.', 'jetpack-scan') })), !siteCredentialsNeeded && userConnectionNeeded && (_jsx(ThreatNotice, { title: __('User connection needed', 'jetpack-scan'), content: __('A user connection provides Jetpack the access necessary to ignore and auto-fix threats on your site.', 'jetpack-scan') })), siteCredentialsNeeded && !userConnectionNeeded && (_jsx(ThreatNotice, { title: __('Site credentials needed', 'jetpack-scan'), content: __('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.', 'jetpack-scan') })), _jsx(ThreatActions, {})] }));
18
+ };
19
+ export default ThreatFixConfirmation;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ThreatFixDetails component
3
+ *
4
+ * @return {JSX.Element | null} The rendered fix details or null if no fixable details are available.
5
+ */
6
+ declare const ThreatFixDetails: () => JSX.Element;
7
+ export default ThreatFixDetails;
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ContextualUpgradeTrigger, Text } from '@automattic/jetpack-components';
3
+ import { __, sprintf } from '@wordpress/i18n';
4
+ import { useMemo, useContext } from 'react';
5
+ import { getFixerDescription } from '@automattic/jetpack-scan';
6
+ import { ThreatModalContext } from "./index.js";
7
+ import styles from './styles.module.scss';
8
+ /**
9
+ * ThreatFixDetails component
10
+ *
11
+ * @return {JSX.Element | null} The rendered fix details or null if no fixable details are available.
12
+ */
13
+ const ThreatFixDetails = () => {
14
+ const { threat, handleUpgradeClick } = useContext(ThreatModalContext);
15
+ const title = useMemo(() => {
16
+ if (threat.status === 'fixed') {
17
+ return __('How did Jetpack fix it?', 'jetpack-scan');
18
+ }
19
+ if (threat.status === 'current' && threat.fixable) {
20
+ return __('How can Jetpack auto-fix this threat?', 'jetpack-scan');
21
+ }
22
+ return __('How to fix it?', 'jetpack-scan');
23
+ }, [threat]);
24
+ const fix = useMemo(() => {
25
+ // The threat has a fixed version available, but no auto-fix is available.
26
+ // The user needs to update the extension to the fixed version.
27
+ if (!threat.fixable && threat.fixedIn) {
28
+ return sprintf(
29
+ /* translators: Translates to Updates to version. %1$s: Name. %2$s: Fixed version */
30
+ __('Update %1$s to version %2$s.', 'jetpack-scan'), threat.extension.name, threat.fixedIn);
31
+ }
32
+ // The threat has an auto-fix available.
33
+ return getFixerDescription(threat);
34
+ }, [threat]);
35
+ if (!threat.fixable && !threat.fixedIn) {
36
+ return null;
37
+ }
38
+ return (_jsxs("div", { className: styles.section, children: [_jsx(Text, { variant: "title-small", children: title }), _jsx(Text, { children: fix }), handleUpgradeClick && (_jsx(ContextualUpgradeTrigger, { description: __('Looking for advanced scan results and one-click fixes?', 'jetpack-scan'), cta: __('Upgrade Jetpack now', 'jetpack-scan'), onClick: handleUpgradeClick }))] }));
39
+ };
40
+ export default ThreatFixDetails;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ThreatNotice component
3
+ *
4
+ * @param {object} props - The component props.
5
+ * @param {string} props.status - The status of the notice.
6
+ * @param {string} props.title - The title of the notice.
7
+ * @param {string} props.content - The content of the notice.
8
+ *
9
+ * @return {JSX.Element} The rendered ThreatNotice component.
10
+ */
11
+ declare const ThreatNotice: ({ status, title, content, }: {
12
+ status?: "warning" | "error" | "success" | undefined;
13
+ title: string;
14
+ content: string;
15
+ }) => JSX.Element;
16
+ export default ThreatNotice;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text, Button } from '@automattic/jetpack-components';
3
+ import { Notice, Spinner } from '@wordpress/components';
4
+ import { __ } from '@wordpress/i18n';
5
+ import { Icon, warning } from '@wordpress/icons';
6
+ import { useContext } from 'react';
7
+ import { ThreatModalContext } from "./index.js";
8
+ import styles from './styles.module.scss';
9
+ /**
10
+ * ThreatNotice component
11
+ *
12
+ * @param {object} props - The component props.
13
+ * @param {string} props.status - The status of the notice.
14
+ * @param {string} props.title - The title of the notice.
15
+ * @param {string} props.content - The content of the notice.
16
+ *
17
+ * @return {JSX.Element} The rendered ThreatNotice component.
18
+ */
19
+ const ThreatNotice = ({ status = 'warning', title, content, }) => {
20
+ const { userConnectionNeeded, userIsConnecting, handleConnectUser, siteCredentialsNeeded, credentialsRedirectUrl, credentialsIsFetching, } = useContext(ThreatModalContext);
21
+ return (_jsx(Notice, { status: status, isDismissible: false, children: _jsxs("div", { className: styles.notice, children: [_jsxs("div", { className: styles.notice__title, children: [status === 'success' ? (_jsx(Spinner, { className: styles.spinner })) : (_jsx(Icon, { icon: warning, size: 30 })), _jsx(Text, { variant: "title-small", mb: 2, children: title })] }), _jsx(Text, { children: content }), _jsxs("div", { className: styles.notice__actions, children: [userConnectionNeeded && (_jsx(Button, { className: styles.notice__action, isExternalLink: true, weight: "regular", isLoading: userIsConnecting, onClick: handleConnectUser, children: __('Connect your user account', 'jetpack-scan') })), siteCredentialsNeeded && (_jsx(Button, { className: styles.notice__action, isExternalLink: true, weight: "regular", href: credentialsRedirectUrl, isLoading: credentialsIsFetching, children: __('Enter server credentials', 'jetpack-scan') }))] })] }) }));
22
+ };
23
+ export default ThreatNotice;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ThreatSummary component
3
+ *
4
+ * @return {JSX.Element} The rendered threat summary.
5
+ */
6
+ declare const ThreatSummary: () => JSX.Element;
7
+ export default ThreatSummary;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Text } from '@automattic/jetpack-components';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { useContext } from 'react';
5
+ import { ThreatModalContext } from "./index.js";
6
+ import styles from './styles.module.scss';
7
+ /**
8
+ * ThreatSummary component
9
+ *
10
+ * @return {JSX.Element} The rendered threat summary.
11
+ */
12
+ const ThreatSummary = () => {
13
+ const { threat } = useContext(ThreatModalContext);
14
+ return (_jsxs("div", { className: styles.section, children: [!!threat.description && _jsx(Text, { children: threat.description }), !!threat.source && (_jsx("div", { children: _jsx(Button, { variant: "link", isExternalLink: true, weight: "regular", href: threat.source, children: __('See more technical details of this threat', 'jetpack-scan') }) }))] }));
15
+ };
16
+ export default ThreatSummary;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ThreatTechnicalDetails component
3
+ *
4
+ * @return {JSX.Element | null} The rendered technical details or null if no details are available.
5
+ */
6
+ declare const ThreatTechnicalDetails: () => JSX.Element;
7
+ export default ThreatTechnicalDetails;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Text, Button, DiffViewer, MarkedLines } from '@automattic/jetpack-components';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { chevronDown, chevronUp, Icon } from '@wordpress/icons';
5
+ import { useState, useCallback, useContext } from 'react';
6
+ import { ThreatModalContext } from "./index.js";
7
+ import styles from './styles.module.scss';
8
+ /**
9
+ * ThreatTechnicalDetails component
10
+ *
11
+ * @return {JSX.Element | null} The rendered technical details or null if no details are available.
12
+ */
13
+ const ThreatTechnicalDetails = () => {
14
+ const { threat } = useContext(ThreatModalContext);
15
+ const [open, setOpen] = useState(false);
16
+ const toggleOpen = useCallback(() => {
17
+ setOpen(!open);
18
+ }, [open]);
19
+ if (!threat.filename && !threat.context && !threat.diff) {
20
+ return null;
21
+ }
22
+ return (_jsxs("div", { className: styles.section, children: [_jsx("div", { className: styles.section__title, children: _jsx(Button, { variant: "link", className: styles.section__toggle, "aria-expanded": open, "aria-controls": `threat-details-${threat.id}`, onClick: toggleOpen, children: _jsxs("div", { className: styles.section__toggle__content, children: [_jsx(Text, { variant: "title-small", mb: 0, children: open
23
+ ? __('Hide the technical details', 'jetpack-scan')
24
+ : __('Show the technical details', 'jetpack-scan') }), _jsx(Icon, { icon: open ? chevronUp : chevronDown, size: 24 })] }) }) }), open && (_jsxs("div", { className: open ? styles.section__open : styles.section__closed, id: `threat-details-${threat.id}`, children: [threat.filename && (_jsxs(_Fragment, { children: [_jsx(Text, { children: __('Threat found in file:', 'jetpack-scan') }), _jsx("pre", { className: styles.filename, children: threat.filename })] })), threat.context && _jsx(MarkedLines, { context: threat.context }), threat.diff && _jsx(DiffViewer, { diff: threat.diff })] }))] }));
25
+ };
26
+ export default ThreatTechnicalDetails;
@@ -0,0 +1,4 @@
1
+ declare const ThreatSeverityBadge: ({ severity }: {
2
+ severity: any;
3
+ }) => import("react/jsx-runtime").JSX.Element;
4
+ export default ThreatSeverityBadge;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Badge } from '@automattic/jetpack-components';
3
+ import { _x } from '@wordpress/i18n';
4
+ const ThreatSeverityBadge = ({ severity }) => {
5
+ if (severity >= 5) {
6
+ return (_jsx(Badge, { variant: "danger", children: _x('Critical', 'Severity label for issues rated 5 or higher.', 'jetpack-scan') }));
7
+ }
8
+ if (severity >= 3 && severity < 5) {
9
+ return (_jsx(Badge, { variant: "warning", children: _x('High', 'Severity label for issues rated between 3 and 5.', 'jetpack-scan') }));
10
+ }
11
+ return _jsx(Badge, { children: _x('Low', 'Severity label for issues rated below 3.', 'jetpack-scan') });
12
+ };
13
+ export default ThreatSeverityBadge;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen } from '@testing-library/react';
3
+ import ThreatSeverityBadge from "../index.js";
4
+ describe('ThreatSeverityBadge', () => {
5
+ it('renders the correct severity label', () => {
6
+ render(_jsx(ThreatSeverityBadge, { severity: 4 }));
7
+ expect(screen.getByText('High')).toBeInTheDocument();
8
+ });
9
+ });
@@ -0,0 +1,33 @@
1
+ export declare const THREAT_STATUSES: {
2
+ value: string;
3
+ label: string;
4
+ variant?: 'success' | 'warning';
5
+ }[];
6
+ export declare const THREAT_TYPES: {
7
+ value: string;
8
+ label: string;
9
+ }[];
10
+ export declare const THREAT_ICONS: {
11
+ plugins: import("react").JSX.Element;
12
+ themes: import("react").JSX.Element;
13
+ core: import("react").JSX.Element;
14
+ file: import("react").JSX.Element;
15
+ default: import("react").JSX.Element;
16
+ };
17
+ export declare const THREAT_FIELD_THREAT = "threat";
18
+ export declare const THREAT_FIELD_TITLE = "title";
19
+ export declare const THREAT_FIELD_DESCRIPTION = "description";
20
+ export declare const THREAT_FIELD_ICON = "icon";
21
+ export declare const THREAT_FIELD_STATUS = "status";
22
+ export declare const THREAT_FIELD_TYPE = "type";
23
+ export declare const THREAT_FIELD_EXTENSION = "extension";
24
+ export declare const THREAT_FIELD_PLUGIN = "plugin";
25
+ export declare const THREAT_FIELD_THEME = "theme";
26
+ export declare const THREAT_FIELD_SEVERITY = "severity";
27
+ export declare const THREAT_FIELD_SIGNATURE = "signature";
28
+ export declare const THREAT_FIELD_FIRST_DETECTED = "first-detected";
29
+ export declare const THREAT_FIELD_FIXED_ON = "fixed-on";
30
+ export declare const THREAT_FIELD_AUTO_FIX = "auto-fix";
31
+ export declare const THREAT_ACTION_FIX = "fix";
32
+ export declare const THREAT_ACTION_IGNORE = "ignore";
33
+ export declare const THREAT_ACTION_UNIGNORE = "unignore";
@@ -0,0 +1,37 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import { code as fileIcon, color as themeIcon, plugins as pluginIcon, shield as shieldIcon, wordpress as coreIcon, } from '@wordpress/icons';
3
+ export const THREAT_STATUSES = [
4
+ { value: 'current', label: __('Active', 'jetpack-scan'), variant: 'warning' },
5
+ { value: 'fixed', label: __('Fixed', 'jetpack-scan'), variant: 'success' },
6
+ { value: 'ignored', label: __('Ignored', 'jetpack-scan') },
7
+ ];
8
+ export const THREAT_TYPES = [
9
+ { value: 'plugins', label: __('Plugin', 'jetpack-scan') },
10
+ { value: 'themes', label: __('Theme', 'jetpack-scan') },
11
+ { value: 'core', label: __('WordPress', 'jetpack-scan') },
12
+ { value: 'file', label: __('File', 'jetpack-scan') },
13
+ ];
14
+ export const THREAT_ICONS = {
15
+ plugins: pluginIcon,
16
+ themes: themeIcon,
17
+ core: coreIcon,
18
+ file: fileIcon,
19
+ default: shieldIcon,
20
+ };
21
+ export const THREAT_FIELD_THREAT = 'threat';
22
+ export const THREAT_FIELD_TITLE = 'title';
23
+ export const THREAT_FIELD_DESCRIPTION = 'description';
24
+ export const THREAT_FIELD_ICON = 'icon';
25
+ export const THREAT_FIELD_STATUS = 'status';
26
+ export const THREAT_FIELD_TYPE = 'type';
27
+ export const THREAT_FIELD_EXTENSION = 'extension';
28
+ export const THREAT_FIELD_PLUGIN = 'plugin';
29
+ export const THREAT_FIELD_THEME = 'theme';
30
+ export const THREAT_FIELD_SEVERITY = 'severity';
31
+ export const THREAT_FIELD_SIGNATURE = 'signature';
32
+ export const THREAT_FIELD_FIRST_DETECTED = 'first-detected';
33
+ export const THREAT_FIELD_FIXED_ON = 'fixed-on';
34
+ export const THREAT_FIELD_AUTO_FIX = 'auto-fix';
35
+ export const THREAT_ACTION_FIX = 'fix';
36
+ export const THREAT_ACTION_IGNORE = 'ignore';
37
+ export const THREAT_ACTION_UNIGNORE = 'unignore';