foreman_openscap 7.1.1 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/foreman_openscap/locale/cs_CZ/foreman_openscap.js +517 -421
- data/app/assets/javascripts/foreman_openscap/locale/de/foreman_openscap.js +907 -814
- data/app/assets/javascripts/foreman_openscap/locale/en/foreman_openscap.js +517 -421
- data/app/assets/javascripts/foreman_openscap/locale/en_GB/foreman_openscap.js +549 -453
- data/app/assets/javascripts/foreman_openscap/locale/es/foreman_openscap.js +911 -818
- data/app/assets/javascripts/foreman_openscap/locale/fr/foreman_openscap.js +911 -818
- data/app/assets/javascripts/foreman_openscap/locale/gl/foreman_openscap.js +556 -460
- data/app/assets/javascripts/foreman_openscap/locale/it/foreman_openscap.js +601 -505
- data/app/assets/javascripts/foreman_openscap/locale/ja/foreman_openscap.js +909 -816
- data/app/assets/javascripts/foreman_openscap/locale/ka/foreman_openscap.js +96 -0
- data/app/assets/javascripts/foreman_openscap/locale/ko/foreman_openscap.js +704 -611
- data/app/assets/javascripts/foreman_openscap/locale/pt_BR/foreman_openscap.js +911 -818
- data/app/assets/javascripts/foreman_openscap/locale/ru/foreman_openscap.js +706 -613
- data/app/assets/javascripts/foreman_openscap/locale/sv_SE/foreman_openscap.js +556 -460
- data/app/assets/javascripts/foreman_openscap/locale/zh_CN/foreman_openscap.js +909 -816
- data/app/assets/javascripts/foreman_openscap/locale/zh_TW/foreman_openscap.js +704 -611
- data/app/assets/javascripts/foreman_openscap/reports.js +5 -0
- data/app/controllers/arf_reports_controller.rb +23 -2
- data/app/helpers/arf_reports_helper.rb +17 -4
- data/app/models/foreman_openscap/arf_report.rb +11 -2
- data/app/views/arf_reports/_output.html.erb +5 -1
- data/app/views/job_templates/run_openscap_remediation_-_ansible_default.erb +27 -0
- data/app/views/job_templates/run_openscap_remediation_-_script_default.erb +24 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20230912122310_add_fixes_to_message.rb +5 -0
- data/lib/foreman_openscap/engine.rb +18 -2
- data/lib/foreman_openscap/version.rb +1 -1
- data/lib/tasks/foreman_openscap_tasks.rake +5 -16
- data/locale/cs_CZ/foreman_openscap.edit.po +145 -17
- data/locale/cs_CZ/foreman_openscap.po +96 -0
- data/locale/de/foreman_openscap.edit.po +248 -282
- data/locale/de/foreman_openscap.po +96 -0
- data/locale/en/foreman_openscap.edit.po +145 -17
- data/locale/en/foreman_openscap.po +96 -0
- data/locale/en_GB/foreman_openscap.edit.po +145 -17
- data/locale/en_GB/foreman_openscap.po +96 -0
- data/locale/es/foreman_openscap.edit.po +241 -279
- data/locale/es/foreman_openscap.po +96 -0
- data/locale/foreman_openscap.pot +164 -12
- data/locale/fr/foreman_openscap.edit.po +330 -363
- data/locale/fr/foreman_openscap.po +96 -0
- data/locale/gl/foreman_openscap.edit.po +145 -17
- data/locale/gl/foreman_openscap.po +96 -0
- data/locale/it/foreman_openscap.edit.po +145 -17
- data/locale/it/foreman_openscap.po +96 -0
- data/locale/ja/foreman_openscap.edit.po +314 -347
- data/locale/ja/foreman_openscap.po +96 -0
- data/locale/ka/foreman_openscap.edit.po +289 -328
- data/locale/ka/foreman_openscap.po +96 -0
- data/locale/ka/foreman_openscap.po.time_stamp +0 -0
- data/locale/ko/foreman_openscap.edit.po +146 -18
- data/locale/ko/foreman_openscap.po +96 -0
- data/locale/pt_BR/foreman_openscap.edit.po +247 -279
- data/locale/pt_BR/foreman_openscap.po +96 -0
- data/locale/ru/foreman_openscap.edit.po +146 -18
- data/locale/ru/foreman_openscap.po +96 -0
- data/locale/sv_SE/foreman_openscap.edit.po +145 -17
- data/locale/sv_SE/foreman_openscap.po +96 -0
- data/locale/zh_CN/foreman_openscap.edit.po +314 -347
- data/locale/zh_CN/foreman_openscap.po +96 -0
- data/locale/zh_TW/foreman_openscap.edit.po +146 -18
- data/locale/zh_TW/foreman_openscap.po +96 -0
- data/webpack/components/EmptyState.js +6 -2
- data/webpack/components/OpenscapRemediationWizard/OpenscapRemediationSelectors.js +16 -0
- data/webpack/components/OpenscapRemediationWizard/OpenscapRemediationWizardContext.js +4 -0
- data/webpack/components/OpenscapRemediationWizard/ViewSelectedHostsLink.js +38 -0
- data/webpack/components/OpenscapRemediationWizard/WizardHeader.js +43 -0
- data/webpack/components/OpenscapRemediationWizard/constants.js +14 -0
- data/webpack/components/OpenscapRemediationWizard/helpers.js +33 -0
- data/webpack/components/OpenscapRemediationWizard/index.js +160 -0
- data/webpack/components/OpenscapRemediationWizard/steps/Finish.js +131 -0
- data/webpack/components/OpenscapRemediationWizard/steps/ReviewHosts.js +217 -0
- data/webpack/components/OpenscapRemediationWizard/steps/ReviewRemediation.js +176 -0
- data/webpack/components/OpenscapRemediationWizard/steps/ReviewRemediation.scss +4 -0
- data/webpack/components/OpenscapRemediationWizard/steps/SnippetSelect.js +160 -0
- data/webpack/components/OpenscapRemediationWizard/steps/index.js +4 -0
- data/webpack/index.js +8 -3
- data/webpack/testHelper.js +6 -5
- metadata +20 -3
@@ -0,0 +1,217 @@
|
|
1
|
+
import React, { useContext, useState, useEffect } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import {
|
4
|
+
Spinner,
|
5
|
+
Toolbar,
|
6
|
+
ToolbarContent,
|
7
|
+
ToolbarGroup,
|
8
|
+
ToolbarItem,
|
9
|
+
} from '@patternfly/react-core';
|
10
|
+
import { Td } from '@patternfly/react-table';
|
11
|
+
import { toArray } from 'lodash';
|
12
|
+
|
13
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
14
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
15
|
+
import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
|
16
|
+
import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
|
17
|
+
import { useBulkSelect } from 'foremanReact/components/PF4/TableIndexPage/Table/TableHooks';
|
18
|
+
import { getPageStats } from 'foremanReact/components/PF4/TableIndexPage/Table/helpers';
|
19
|
+
import { STATUS } from 'foremanReact/constants';
|
20
|
+
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
21
|
+
|
22
|
+
import OpenscapRemediationWizardContext from '../OpenscapRemediationWizardContext';
|
23
|
+
import WizardHeader from '../WizardHeader';
|
24
|
+
import { HOSTS_API_PATH, HOSTS_API_REQUEST_KEY } from '../constants';
|
25
|
+
|
26
|
+
const ReviewHosts = () => {
|
27
|
+
const {
|
28
|
+
hostId,
|
29
|
+
setHostIdsParam,
|
30
|
+
defaultFailedHostsSearch,
|
31
|
+
setIsAllHostsSelected,
|
32
|
+
savedHostSelectionsRef,
|
33
|
+
} = useContext(OpenscapRemediationWizardContext);
|
34
|
+
|
35
|
+
const defaultParams = {
|
36
|
+
search: defaultFailedHostsSearch,
|
37
|
+
};
|
38
|
+
const defaultHostsArry = [hostId];
|
39
|
+
|
40
|
+
const [params, setParams] = useState(defaultParams);
|
41
|
+
|
42
|
+
const response = useAPI('get', `${HOSTS_API_PATH}?include_permissions=true`, {
|
43
|
+
key: HOSTS_API_REQUEST_KEY,
|
44
|
+
params: defaultParams,
|
45
|
+
});
|
46
|
+
const {
|
47
|
+
response: {
|
48
|
+
search: apiSearchQuery,
|
49
|
+
results,
|
50
|
+
per_page: perPage,
|
51
|
+
page,
|
52
|
+
subtotal,
|
53
|
+
message: errorMessage,
|
54
|
+
},
|
55
|
+
status = STATUS.PENDING,
|
56
|
+
setAPIOptions,
|
57
|
+
} = response;
|
58
|
+
|
59
|
+
const subtotalCount = Number(subtotal ?? 0);
|
60
|
+
|
61
|
+
const setParamsAndAPI = newParams => {
|
62
|
+
setParams(newParams);
|
63
|
+
setAPIOptions({ key: HOSTS_API_REQUEST_KEY, params: newParams });
|
64
|
+
};
|
65
|
+
|
66
|
+
const { pageRowCount } = getPageStats({
|
67
|
+
total: subtotalCount,
|
68
|
+
page,
|
69
|
+
perPage,
|
70
|
+
});
|
71
|
+
const { fetchBulkParams, ...selectAllOptions } = useBulkSelect({
|
72
|
+
results,
|
73
|
+
metadata: { total: subtotalCount, page },
|
74
|
+
initialSearchQuery: apiSearchQuery || defaultFailedHostsSearch,
|
75
|
+
isSelectable: () => true,
|
76
|
+
defaultArry: defaultHostsArry,
|
77
|
+
initialArry: toArray(
|
78
|
+
savedHostSelectionsRef.current.inclusionSet || defaultHostsArry
|
79
|
+
),
|
80
|
+
initialExclusionArry: toArray(
|
81
|
+
savedHostSelectionsRef.current.exclusionSet || []
|
82
|
+
),
|
83
|
+
initialSelectAllMode: savedHostSelectionsRef.current.selectAllMode || false,
|
84
|
+
});
|
85
|
+
const {
|
86
|
+
selectPage,
|
87
|
+
selectedCount,
|
88
|
+
selectOne,
|
89
|
+
selectNone,
|
90
|
+
selectDefault,
|
91
|
+
selectAll,
|
92
|
+
areAllRowsOnPageSelected,
|
93
|
+
areAllRowsSelected,
|
94
|
+
isSelected,
|
95
|
+
inclusionSet,
|
96
|
+
exclusionSet,
|
97
|
+
selectAllMode,
|
98
|
+
} = selectAllOptions;
|
99
|
+
|
100
|
+
useEffect(() => {
|
101
|
+
if (selectedCount) {
|
102
|
+
setHostIdsParam(fetchBulkParams());
|
103
|
+
savedHostSelectionsRef.current = {
|
104
|
+
inclusionSet,
|
105
|
+
exclusionSet,
|
106
|
+
selectAllMode,
|
107
|
+
};
|
108
|
+
}
|
109
|
+
}, [selectedCount, fetchBulkParams, setHostIdsParam]);
|
110
|
+
|
111
|
+
const selectionToolbar = (
|
112
|
+
<ToolbarItem key="selectAll">
|
113
|
+
<SelectAllCheckbox
|
114
|
+
{...{
|
115
|
+
selectAll: () => {
|
116
|
+
selectAll(true);
|
117
|
+
setIsAllHostsSelected(true);
|
118
|
+
},
|
119
|
+
selectPage: () => {
|
120
|
+
selectPage();
|
121
|
+
setIsAllHostsSelected(false);
|
122
|
+
},
|
123
|
+
selectDefault: () => {
|
124
|
+
selectDefault();
|
125
|
+
setIsAllHostsSelected(false);
|
126
|
+
},
|
127
|
+
selectNone: () => {
|
128
|
+
selectNone();
|
129
|
+
setIsAllHostsSelected(false);
|
130
|
+
},
|
131
|
+
selectedCount,
|
132
|
+
pageRowCount,
|
133
|
+
}}
|
134
|
+
totalCount={subtotalCount}
|
135
|
+
selectedDefaultCount={1} // The default host (hostId) is always selected
|
136
|
+
areAllRowsOnPageSelected={areAllRowsOnPageSelected()}
|
137
|
+
areAllRowsSelected={areAllRowsSelected()}
|
138
|
+
/>
|
139
|
+
</ToolbarItem>
|
140
|
+
);
|
141
|
+
|
142
|
+
const RowSelectTd = ({ rowData }) => (
|
143
|
+
<Td
|
144
|
+
select={{
|
145
|
+
rowIndex: rowData.id,
|
146
|
+
onSelect: (_event, isSelecting) => {
|
147
|
+
selectOne(isSelecting, rowData.id, rowData);
|
148
|
+
// If at least one was unselected, then it's not all selected
|
149
|
+
if (!isSelecting) setIsAllHostsSelected(false);
|
150
|
+
},
|
151
|
+
isSelected: rowData.id === hostId || isSelected(rowData.id),
|
152
|
+
disable: rowData.id === hostId || false,
|
153
|
+
}}
|
154
|
+
/>
|
155
|
+
);
|
156
|
+
RowSelectTd.propTypes = {
|
157
|
+
rowData: PropTypes.object.isRequired,
|
158
|
+
};
|
159
|
+
|
160
|
+
const columns = {
|
161
|
+
name: {
|
162
|
+
title: __('Name'),
|
163
|
+
wrapper: ({ id, name }) => <a href={foremanUrl(`hosts/${id}`)}>{name}</a>,
|
164
|
+
isSorted: true,
|
165
|
+
},
|
166
|
+
operatingsystem_name: {
|
167
|
+
title: __('OS'),
|
168
|
+
},
|
169
|
+
};
|
170
|
+
|
171
|
+
return (
|
172
|
+
<>
|
173
|
+
<WizardHeader
|
174
|
+
title={__('Review hosts')}
|
175
|
+
description={__(
|
176
|
+
'By default, remediation is applied to the current host. Optionally, remediate any additional hosts that fail the rule.'
|
177
|
+
)}
|
178
|
+
/>
|
179
|
+
<Toolbar ouiaId="table-toolbar" className="table-toolbar">
|
180
|
+
<ToolbarContent>
|
181
|
+
<ToolbarGroup>
|
182
|
+
{selectionToolbar}
|
183
|
+
{status === STATUS.PENDING && (
|
184
|
+
<ToolbarItem>
|
185
|
+
<Spinner size="sm" />
|
186
|
+
</ToolbarItem>
|
187
|
+
)}
|
188
|
+
</ToolbarGroup>
|
189
|
+
</ToolbarContent>
|
190
|
+
</Toolbar>
|
191
|
+
<Table
|
192
|
+
ouiaId="hosts-review-table"
|
193
|
+
isEmbedded
|
194
|
+
params={params}
|
195
|
+
setParams={setParamsAndAPI}
|
196
|
+
itemCount={subtotalCount}
|
197
|
+
results={results}
|
198
|
+
url={HOSTS_API_PATH}
|
199
|
+
refreshData={() =>
|
200
|
+
setAPIOptions({
|
201
|
+
key: HOSTS_API_REQUEST_KEY,
|
202
|
+
params: { defaultFailedHostsSearch },
|
203
|
+
})
|
204
|
+
}
|
205
|
+
columns={columns}
|
206
|
+
errorMessage={
|
207
|
+
status === STATUS.ERROR && errorMessage ? errorMessage : null
|
208
|
+
}
|
209
|
+
isPending={status === STATUS.PENDING}
|
210
|
+
showCheckboxes
|
211
|
+
rowSelectTd={RowSelectTd}
|
212
|
+
/>
|
213
|
+
</>
|
214
|
+
);
|
215
|
+
};
|
216
|
+
|
217
|
+
export default ReviewHosts;
|
@@ -0,0 +1,176 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
2
|
+
import React, { useContext, useState } from 'react';
|
3
|
+
import { some } from 'lodash';
|
4
|
+
import {
|
5
|
+
CodeBlock,
|
6
|
+
CodeBlockAction,
|
7
|
+
CodeBlockCode,
|
8
|
+
ClipboardCopyButton,
|
9
|
+
Button,
|
10
|
+
Grid,
|
11
|
+
GridItem,
|
12
|
+
Alert,
|
13
|
+
Checkbox,
|
14
|
+
} from '@patternfly/react-core';
|
15
|
+
import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
|
16
|
+
|
17
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
18
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
19
|
+
import { getHostsPageUrl } from 'foremanReact/Root/Context/ForemanContext';
|
20
|
+
|
21
|
+
import OpenscapRemediationWizardContext from '../OpenscapRemediationWizardContext';
|
22
|
+
import WizardHeader from '../WizardHeader';
|
23
|
+
import ViewSelectedHostsLink from '../ViewSelectedHostsLink';
|
24
|
+
import { HOSTS_PATH, FAIL_RULE_SEARCH } from '../constants';
|
25
|
+
import { findFixBySnippet } from '../helpers';
|
26
|
+
|
27
|
+
import './ReviewRemediation.scss';
|
28
|
+
|
29
|
+
const ReviewRemediation = () => {
|
30
|
+
const {
|
31
|
+
fixes,
|
32
|
+
snippet,
|
33
|
+
method,
|
34
|
+
hostName,
|
35
|
+
source,
|
36
|
+
isRebootSelected,
|
37
|
+
setIsRebootSelected,
|
38
|
+
isAllHostsSelected,
|
39
|
+
hostIdsParam,
|
40
|
+
defaultFailedHostsSearch,
|
41
|
+
} = useContext(OpenscapRemediationWizardContext);
|
42
|
+
const [copied, setCopied] = useState(false);
|
43
|
+
const selectedFix = findFixBySnippet(fixes, snippet);
|
44
|
+
const snippetText = selectedFix?.full_text;
|
45
|
+
// can be one of null, "true", "false"
|
46
|
+
// if null, it may indicate that reboot might be needed
|
47
|
+
const checkForReboot = () => !some(fixes, { reboot: 'false' });
|
48
|
+
const isRebootRequired = () => some(fixes, { reboot: 'true' });
|
49
|
+
|
50
|
+
const copyToClipboard = (e, text) => {
|
51
|
+
navigator.clipboard.writeText(text.toString());
|
52
|
+
};
|
53
|
+
|
54
|
+
const onCopyClick = (e, text) => {
|
55
|
+
copyToClipboard(e, text);
|
56
|
+
setCopied(true);
|
57
|
+
};
|
58
|
+
|
59
|
+
const description =
|
60
|
+
method === 'manual'
|
61
|
+
? __('Review the remediation snippet and apply it to the host manually.')
|
62
|
+
: __(
|
63
|
+
'Review the remediation snippet that will be applied to selected host(s).'
|
64
|
+
);
|
65
|
+
|
66
|
+
const rebootAlertTitle = isRebootRequired()
|
67
|
+
? __('A reboot is required after applying remediation.')
|
68
|
+
: __('A reboot might be required after applying remediation.');
|
69
|
+
|
70
|
+
const actions = (
|
71
|
+
<React.Fragment>
|
72
|
+
<CodeBlockAction>
|
73
|
+
<ClipboardCopyButton
|
74
|
+
id="basic-copy-button"
|
75
|
+
textId="code-content"
|
76
|
+
aria-label="Copy to clipboard"
|
77
|
+
onClick={e => onCopyClick(e, snippetText)}
|
78
|
+
exitDelay={copied ? 1500 : 600}
|
79
|
+
maxWidth="110px"
|
80
|
+
variant="plain"
|
81
|
+
onTooltipHidden={() => setCopied(false)}
|
82
|
+
>
|
83
|
+
{copied
|
84
|
+
? __('Successfully copied to clipboard!')
|
85
|
+
: __('Copy to clipboard')}
|
86
|
+
</ClipboardCopyButton>
|
87
|
+
</CodeBlockAction>
|
88
|
+
</React.Fragment>
|
89
|
+
);
|
90
|
+
|
91
|
+
return (
|
92
|
+
<>
|
93
|
+
<WizardHeader
|
94
|
+
title={__('Review remediation')}
|
95
|
+
description={description}
|
96
|
+
/>
|
97
|
+
<Grid hasGutter>
|
98
|
+
<br />
|
99
|
+
<GridItem>
|
100
|
+
<Alert
|
101
|
+
ouiaId="review-alert"
|
102
|
+
variant="danger"
|
103
|
+
title={`${__(
|
104
|
+
'Do not implement any of the recommended remedial actions or scripts without first testing them in a non-production environment.'
|
105
|
+
)}
|
106
|
+
${__('Remediation might render the system non-functional.')}`}
|
107
|
+
/>
|
108
|
+
</GridItem>
|
109
|
+
<GridItem md={12} span={4} rowSpan={1}>
|
110
|
+
<ViewSelectedHostsLink
|
111
|
+
isAllHostsSelected={isAllHostsSelected}
|
112
|
+
hostIdsParam={hostIdsParam}
|
113
|
+
defaultFailedHostsSearch={defaultFailedHostsSearch}
|
114
|
+
/>
|
115
|
+
</GridItem>
|
116
|
+
<GridItem md={12} span={4} rowSpan={1}>
|
117
|
+
<Button
|
118
|
+
variant="link"
|
119
|
+
icon={<ExternalLinkSquareAltIcon />}
|
120
|
+
iconPosition="right"
|
121
|
+
target="_blank"
|
122
|
+
component="a"
|
123
|
+
href={foremanUrl(`${getHostsPageUrl(true)}/${hostName}`)}
|
124
|
+
>
|
125
|
+
{hostName}
|
126
|
+
</Button>{' '}
|
127
|
+
</GridItem>
|
128
|
+
<GridItem md={12} span={8} rowSpan={1}>
|
129
|
+
<Button
|
130
|
+
variant="link"
|
131
|
+
icon={<ExternalLinkSquareAltIcon />}
|
132
|
+
iconPosition="right"
|
133
|
+
target="_blank"
|
134
|
+
component="a"
|
135
|
+
href={foremanUrl(
|
136
|
+
`${HOSTS_PATH}/?search=${FAIL_RULE_SEARCH} = ${source}`
|
137
|
+
)}
|
138
|
+
>
|
139
|
+
{__('Other hosts failing this rule')}
|
140
|
+
</Button>
|
141
|
+
</GridItem>
|
142
|
+
{checkForReboot() ? (
|
143
|
+
<>
|
144
|
+
<GridItem span={12} rowSpan={1}>
|
145
|
+
<Alert
|
146
|
+
ouiaId="reboot-alert"
|
147
|
+
variant={isRebootRequired() ? 'warning' : 'info'}
|
148
|
+
title={rebootAlertTitle}
|
149
|
+
/>
|
150
|
+
</GridItem>
|
151
|
+
{method === 'manual' ? null : (
|
152
|
+
<GridItem span={4} rowSpan={1}>
|
153
|
+
<Checkbox
|
154
|
+
id="reboot-checkbox"
|
155
|
+
label={__('Reboot the system(s)')}
|
156
|
+
name="reboot-checkbox"
|
157
|
+
isChecked={isRebootSelected}
|
158
|
+
onChange={selected => setIsRebootSelected(selected)}
|
159
|
+
/>
|
160
|
+
</GridItem>
|
161
|
+
)}
|
162
|
+
</>
|
163
|
+
) : null}
|
164
|
+
<GridItem>
|
165
|
+
<CodeBlock actions={actions}>
|
166
|
+
<CodeBlockCode id="code-content" className="remediation-code">
|
167
|
+
{snippetText}
|
168
|
+
</CodeBlockCode>
|
169
|
+
</CodeBlock>
|
170
|
+
</GridItem>
|
171
|
+
</Grid>
|
172
|
+
</>
|
173
|
+
);
|
174
|
+
};
|
175
|
+
|
176
|
+
export default ReviewRemediation;
|
@@ -0,0 +1,160 @@
|
|
1
|
+
import React, { useContext } from 'react';
|
2
|
+
import { map, split, capitalize, join, slice, isEmpty } from 'lodash';
|
3
|
+
import {
|
4
|
+
Form,
|
5
|
+
FormGroup,
|
6
|
+
FormSelect,
|
7
|
+
FormSelectOption,
|
8
|
+
Radio,
|
9
|
+
Alert,
|
10
|
+
} from '@patternfly/react-core';
|
11
|
+
|
12
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
13
|
+
import { STATUS } from 'foremanReact/constants';
|
14
|
+
import Loading from 'foremanReact/components/Loading';
|
15
|
+
|
16
|
+
import OpenscapRemediationWizardContext from '../OpenscapRemediationWizardContext';
|
17
|
+
import WizardHeader from '../WizardHeader';
|
18
|
+
import EmptyState from '../../EmptyState';
|
19
|
+
import { errorMsg, supportedRemediationSnippets } from '../helpers';
|
20
|
+
|
21
|
+
const SnippetSelect = () => {
|
22
|
+
const {
|
23
|
+
fixes,
|
24
|
+
snippet,
|
25
|
+
setSnippet,
|
26
|
+
method,
|
27
|
+
setMethod,
|
28
|
+
logStatus,
|
29
|
+
logError,
|
30
|
+
supportedJobSnippets,
|
31
|
+
} = useContext(OpenscapRemediationWizardContext);
|
32
|
+
|
33
|
+
const snippetNameMap = {
|
34
|
+
'urn:xccdf:fix:script:ansible': 'Ansible',
|
35
|
+
'urn:xccdf:fix:script:puppet': 'Puppet',
|
36
|
+
'urn:xccdf:fix:script:sh': 'Shell',
|
37
|
+
'urn:xccdf:fix:script:kubernetes': 'Kubernetes',
|
38
|
+
'urn:redhat:anaconda:pre': 'Anaconda',
|
39
|
+
'urn:redhat:osbuild:blueprint': 'OSBuild Blueprint',
|
40
|
+
};
|
41
|
+
|
42
|
+
const snippetName = system => {
|
43
|
+
const mapped = snippetNameMap[system];
|
44
|
+
if (mapped) return mapped;
|
45
|
+
|
46
|
+
return join(
|
47
|
+
map(slice(split(system, ':'), -2), n => capitalize(n)),
|
48
|
+
' '
|
49
|
+
);
|
50
|
+
};
|
51
|
+
|
52
|
+
const resetSnippet = meth => {
|
53
|
+
const snip = supportedRemediationSnippets(
|
54
|
+
fixes,
|
55
|
+
meth,
|
56
|
+
supportedJobSnippets
|
57
|
+
)[0];
|
58
|
+
setSnippet(snip);
|
59
|
+
return snip;
|
60
|
+
};
|
61
|
+
|
62
|
+
const setMethodResetSnippet = meth => {
|
63
|
+
setMethod(meth);
|
64
|
+
resetSnippet(meth);
|
65
|
+
};
|
66
|
+
|
67
|
+
const body =
|
68
|
+
logStatus === STATUS.RESOLVED ? (
|
69
|
+
<Form>
|
70
|
+
<FormGroup
|
71
|
+
label={__('Method')}
|
72
|
+
type="string"
|
73
|
+
fieldId="method"
|
74
|
+
isRequired={false}
|
75
|
+
>
|
76
|
+
<Radio
|
77
|
+
label={__('Remote job')}
|
78
|
+
id="job"
|
79
|
+
name="job"
|
80
|
+
ouiaId="job"
|
81
|
+
aria-label="job"
|
82
|
+
isChecked={method === 'job'}
|
83
|
+
onChange={() => setMethodResetSnippet('job')}
|
84
|
+
/>
|
85
|
+
<Radio
|
86
|
+
label={__('Manual')}
|
87
|
+
id="manual"
|
88
|
+
name="manual"
|
89
|
+
ouiaId="manual"
|
90
|
+
aria-label="manual"
|
91
|
+
isChecked={method === 'manual'}
|
92
|
+
onChange={() => setMethodResetSnippet('manual')}
|
93
|
+
/>
|
94
|
+
</FormGroup>
|
95
|
+
{isEmpty(
|
96
|
+
supportedRemediationSnippets(fixes, method, supportedJobSnippets)
|
97
|
+
) ? (
|
98
|
+
<Alert
|
99
|
+
ouiaId="snippet-alert"
|
100
|
+
variant="info"
|
101
|
+
title={__(
|
102
|
+
'There is no job to remediate with. Please remediate manually.'
|
103
|
+
)}
|
104
|
+
/>
|
105
|
+
) : (
|
106
|
+
<FormGroup
|
107
|
+
label={__('Snippet')}
|
108
|
+
type="string"
|
109
|
+
fieldId="snippet"
|
110
|
+
isRequired
|
111
|
+
>
|
112
|
+
<FormSelect
|
113
|
+
ouiaId="snippet-select"
|
114
|
+
isRequired
|
115
|
+
value={snippet}
|
116
|
+
onChange={value => setSnippet(value)}
|
117
|
+
aria-label="FormSelect Input"
|
118
|
+
>
|
119
|
+
<FormSelectOption
|
120
|
+
isDisabled
|
121
|
+
key={0}
|
122
|
+
value=""
|
123
|
+
label={__('Select snippet')}
|
124
|
+
/>
|
125
|
+
{map(
|
126
|
+
supportedRemediationSnippets(
|
127
|
+
fixes,
|
128
|
+
method,
|
129
|
+
supportedJobSnippets
|
130
|
+
),
|
131
|
+
fix => (
|
132
|
+
<FormSelectOption
|
133
|
+
key={fix}
|
134
|
+
value={fix}
|
135
|
+
label={snippetName(fix)}
|
136
|
+
/>
|
137
|
+
)
|
138
|
+
)}
|
139
|
+
</FormSelect>
|
140
|
+
</FormGroup>
|
141
|
+
)}
|
142
|
+
</Form>
|
143
|
+
) : (
|
144
|
+
<EmptyState error title={__('Error!')} body={errorMsg(logError)} />
|
145
|
+
);
|
146
|
+
|
147
|
+
return (
|
148
|
+
<>
|
149
|
+
<WizardHeader
|
150
|
+
title={__('Select remediation method')}
|
151
|
+
description={__(
|
152
|
+
'You can remediate by running a remote job or you can display a snippet for manual remediation.'
|
153
|
+
)}
|
154
|
+
/>
|
155
|
+
{logStatus === STATUS.PENDING ? <Loading /> : body}
|
156
|
+
</>
|
157
|
+
);
|
158
|
+
};
|
159
|
+
|
160
|
+
export default SnippetSelect;
|
data/webpack/index.js
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
import componentRegistry from 'foremanReact/components/componentRegistry';
|
2
2
|
|
3
3
|
import RuleSeverity from './components/RuleSeverity';
|
4
|
+
import OpenscapRemediationWizard from './components/OpenscapRemediationWizard';
|
4
5
|
|
5
|
-
|
6
|
-
name: 'RuleSeverity',
|
7
|
-
type:
|
6
|
+
const components = [
|
7
|
+
{ name: 'RuleSeverity', type: RuleSeverity },
|
8
|
+
{ name: 'OpenscapRemediationWizard', type: OpenscapRemediationWizard },
|
9
|
+
];
|
10
|
+
|
11
|
+
components.forEach(component => {
|
12
|
+
componentRegistry.register(component);
|
8
13
|
});
|
data/webpack/testHelper.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useState } from 'react';
|
2
2
|
import { Provider } from 'react-redux';
|
3
3
|
import store from 'foremanReact/redux';
|
4
4
|
import { MockedProvider } from '@apollo/react-testing';
|
@@ -19,20 +19,21 @@ export const withRouter = Component => props => (
|
|
19
19
|
);
|
20
20
|
|
21
21
|
export const withMockedProvider = Component => props => {
|
22
|
-
const ForemanContext = getForemanContext(ctx);
|
23
22
|
// eslint-disable-next-line react/prop-types
|
24
23
|
const { mocks, ...rest } = props;
|
25
24
|
|
26
|
-
const
|
25
|
+
const [context, setContext] = useState({
|
27
26
|
metadata: {
|
28
27
|
UISettings: {
|
29
28
|
perPage: 20,
|
30
29
|
},
|
31
30
|
},
|
32
|
-
};
|
31
|
+
});
|
33
32
|
|
33
|
+
const contextData = { context, setContext };
|
34
|
+
const ForemanContext = getForemanContext(contextData);
|
34
35
|
return (
|
35
|
-
<ForemanContext.Provider value={
|
36
|
+
<ForemanContext.Provider value={contextData}>
|
36
37
|
<MockedProvider mocks={mocks}>
|
37
38
|
<Component {...rest} />
|
38
39
|
</MockedProvider>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_openscap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 8.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- slukasik@redhat.com
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -224,6 +224,8 @@ files:
|
|
224
224
|
- app/views/foreman_openscap/policy_mailer/_policy.erb
|
225
225
|
- app/views/foreman_openscap/policy_mailer/policy_summary.erb
|
226
226
|
- app/views/hosts/select_multiple_openscap_proxy.html.erb
|
227
|
+
- app/views/job_templates/run_openscap_remediation_-_ansible_default.erb
|
228
|
+
- app/views/job_templates/run_openscap_remediation_-_script_default.erb
|
227
229
|
- app/views/job_templates/run_openscap_scans.erb
|
228
230
|
- app/views/job_templates/run_oval_scans.erb
|
229
231
|
- app/views/policies/_form.html.erb
|
@@ -323,6 +325,7 @@ files:
|
|
323
325
|
- db/migrate/20201217161511_add_url_to_oval_content.rb
|
324
326
|
- db/migrate/20210409095625_add_oval_policy_reference_to_cve.rb
|
325
327
|
- db/migrate/20210819143316_drop_unused_tables.rb
|
328
|
+
- db/migrate/20230912122310_add_fixes_to_message.rb
|
326
329
|
- db/seeds.d/75-job_templates.rb
|
327
330
|
- db/seeds.d/openscap_feature.rb
|
328
331
|
- db/seeds.d/openscap_policy_notification.rb
|
@@ -376,6 +379,7 @@ files:
|
|
376
379
|
- locale/ka/LC_MESSAGES/foreman_openscap.mo
|
377
380
|
- locale/ka/foreman_openscap.edit.po
|
378
381
|
- locale/ka/foreman_openscap.po
|
382
|
+
- locale/ka/foreman_openscap.po.time_stamp
|
379
383
|
- locale/ko/LC_MESSAGES/foreman_openscap.mo
|
380
384
|
- locale/ko/foreman_openscap.edit.po
|
381
385
|
- locale/ko/foreman_openscap.po
|
@@ -474,6 +478,19 @@ files:
|
|
474
478
|
- webpack/components/IndexTable/IndexTableHelper.js
|
475
479
|
- webpack/components/IndexTable/index.js
|
476
480
|
- webpack/components/LinkButton.js
|
481
|
+
- webpack/components/OpenscapRemediationWizard/OpenscapRemediationSelectors.js
|
482
|
+
- webpack/components/OpenscapRemediationWizard/OpenscapRemediationWizardContext.js
|
483
|
+
- webpack/components/OpenscapRemediationWizard/ViewSelectedHostsLink.js
|
484
|
+
- webpack/components/OpenscapRemediationWizard/WizardHeader.js
|
485
|
+
- webpack/components/OpenscapRemediationWizard/constants.js
|
486
|
+
- webpack/components/OpenscapRemediationWizard/helpers.js
|
487
|
+
- webpack/components/OpenscapRemediationWizard/index.js
|
488
|
+
- webpack/components/OpenscapRemediationWizard/steps/Finish.js
|
489
|
+
- webpack/components/OpenscapRemediationWizard/steps/ReviewHosts.js
|
490
|
+
- webpack/components/OpenscapRemediationWizard/steps/ReviewRemediation.js
|
491
|
+
- webpack/components/OpenscapRemediationWizard/steps/ReviewRemediation.scss
|
492
|
+
- webpack/components/OpenscapRemediationWizard/steps/SnippetSelect.js
|
493
|
+
- webpack/components/OpenscapRemediationWizard/steps/index.js
|
477
494
|
- webpack/components/RuleSeverity/RuleSeverity.scss
|
478
495
|
- webpack/components/RuleSeverity/RuleSeverity.test.js
|
479
496
|
- webpack/components/RuleSeverity/__snapshots__/RuleSeverity.test.js.snap
|
@@ -570,7 +587,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
570
587
|
- !ruby/object:Gem::Version
|
571
588
|
version: '0'
|
572
589
|
requirements: []
|
573
|
-
rubygems_version: 3.
|
590
|
+
rubygems_version: 3.2.33
|
574
591
|
signing_key:
|
575
592
|
specification_version: 4
|
576
593
|
summary: Foreman plug-in for displaying OpenSCAP audit reports
|