foreman_openscap 7.1.1 → 8.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.
- 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
|