@mitre/inspec-objects 2.0.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/auto-approve-and-merge.yml +1 -0
- package/.github/workflows/build.yml +6 -2
- package/.github/workflows/draft-release.yml +4 -1
- package/.github/workflows/e2e-test.yml +7 -3
- package/.github/workflows/linter.yml +6 -2
- package/.github/workflows/push-to-gpr.yml +7 -2
- package/.github/workflows/push-to-npm.yml +10 -4
- package/eslint.config.js +41 -0
- package/lib/index.d.ts +2 -4
- package/lib/index.js +4 -4
- package/lib/mappings/{CciNistMappingData.js → cci-nist-mapping-data.js} +1 -1
- package/lib/objects/control.js +41 -81
- package/lib/objects/profile.js +6 -6
- package/lib/parsers/json.js +5 -7
- package/lib/parsers/oval.js +23 -24
- package/lib/parsers/xccdf.d.ts +1 -1
- package/lib/parsers/xccdf.js +122 -137
- package/lib/utilities/{diffMarkdown.js → diff-markdown.js} +10 -11
- package/lib/utilities/diff.js +46 -48
- package/lib/utilities/global.js +9 -15
- package/lib/utilities/logging.js +1 -1
- package/lib/utilities/update.js +25 -28
- package/lib/utilities/xccdf.js +21 -32
- package/package.json +14 -13
- package/tsconfig.build.json +1 -1
- package/tsconfig.json +2 -2
- package/vitest.config.ts +3 -3
- package/.eslintignore +0 -2
- package/.eslintrc +0 -41
- /package/lib/mappings/{CciNistMappingData.d.ts → cci-nist-mapping-data.d.ts} +0 -0
- /package/lib/utilities/{diffMarkdown.d.ts → diff-markdown.d.ts} +0 -0
package/lib/parsers/oval.js
CHANGED
|
@@ -19,7 +19,7 @@ const logging_1 = require("../utilities/logging");
|
|
|
19
19
|
* will be returned
|
|
20
20
|
*/
|
|
21
21
|
function searchTree(aTree, fCompair, bGreedy) {
|
|
22
|
-
let oNode; // always the current node
|
|
22
|
+
let oNode; // always the current node
|
|
23
23
|
const aInnerTree = []; // will contain the inner children
|
|
24
24
|
const aReturnNodes = []; // the nodes array which will returned
|
|
25
25
|
// 1. loop through all root nodes, store tree content locally so we don't touch the tree structure
|
|
@@ -39,7 +39,7 @@ function searchTree(aTree, fCompair, bGreedy) {
|
|
|
39
39
|
// find other objects, 1. check all properties of the node if they are arrays
|
|
40
40
|
for (const keysNode in oNode) {
|
|
41
41
|
// true if the property is an array
|
|
42
|
-
if (oNode[keysNode]
|
|
42
|
+
if (Array.isArray(oNode[keysNode])) {
|
|
43
43
|
// 2. push all array object to aInnerTree to search in those later
|
|
44
44
|
for (let i = 0; i < oNode[keysNode].length; i++) {
|
|
45
45
|
aInnerTree.push(oNode[keysNode][i]);
|
|
@@ -62,17 +62,17 @@ function searchTree(aTree, fCompair, bGreedy) {
|
|
|
62
62
|
*/
|
|
63
63
|
function extractAllCriteriaRefs(initialCriteria) {
|
|
64
64
|
const criteriaRefs = [];
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
for (const criteria of initialCriteria) {
|
|
66
|
+
if (criteria.criterion)
|
|
67
|
+
for (const criterion of criteria.criterion) {
|
|
68
|
+
if (criterion['@_test_ref']) {
|
|
69
|
+
criteriaRefs.push(criterion['@_test_ref']);
|
|
70
|
+
}
|
|
70
71
|
}
|
|
71
|
-
});
|
|
72
72
|
if (criteria.criteria) {
|
|
73
73
|
criteriaRefs.push(...extractAllCriteriaRefs(criteria.criteria));
|
|
74
74
|
}
|
|
75
|
-
}
|
|
75
|
+
}
|
|
76
76
|
return criteriaRefs;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
@@ -94,7 +94,6 @@ function extractAllCriteriaRefs(initialCriteria) {
|
|
|
94
94
|
* criteria references and resolved values, or `undefined` if no OVAL string is provided.
|
|
95
95
|
*/
|
|
96
96
|
function processOVAL(oval) {
|
|
97
|
-
var _a;
|
|
98
97
|
const logger = (0, logging_1.createWinstonLogger)('ts-inspec-objects');
|
|
99
98
|
if (!oval) {
|
|
100
99
|
return undefined;
|
|
@@ -106,18 +105,15 @@ function processOVAL(oval) {
|
|
|
106
105
|
for (const definition of definitionList.definition) {
|
|
107
106
|
extractedDefinitions[definition['@_id']] = definition;
|
|
108
107
|
extractedDefinitions[definition['@_id']].criteriaRefs = extractAllCriteriaRefs(definition.criteria);
|
|
109
|
-
extractedDefinitions[definition['@_id']].resolvedValues =
|
|
108
|
+
extractedDefinitions[definition['@_id']].resolvedValues = extractedDefinitions[definition['@_id']].criteriaRefs?.map((criteriaRef) => {
|
|
110
109
|
// Extract the original criteria from the oval file
|
|
111
110
|
const foundCriteriaRefererence = searchTree(parsed.oval_definitions[0].tests, (oNode) => oNode['@_id'] === criteriaRef, false)[0];
|
|
112
111
|
const foundObjects = [];
|
|
113
112
|
const foundStates = [];
|
|
114
113
|
if (foundCriteriaRefererence) {
|
|
115
114
|
if (foundCriteriaRefererence.object) {
|
|
116
|
-
foundCriteriaRefererence.object
|
|
117
|
-
if (
|
|
118
|
-
logger.warn(`Found object without object_ref in test ${criteriaRef}`);
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
115
|
+
for (const object of foundCriteriaRefererence.object) {
|
|
116
|
+
if (object['@_object_ref']) {
|
|
121
117
|
const objectRef = object['@_object_ref'];
|
|
122
118
|
const foundObjectReference = searchTree(parsed.oval_definitions[0].objects, (oNode) => oNode['@_id'] === objectRef, false)[0];
|
|
123
119
|
if (foundObjectReference) {
|
|
@@ -127,14 +123,14 @@ function processOVAL(oval) {
|
|
|
127
123
|
logger.warn(`Could not find object ${objectRef} for test ${criteriaRef}`);
|
|
128
124
|
}
|
|
129
125
|
}
|
|
130
|
-
|
|
126
|
+
else {
|
|
127
|
+
logger.warn(`Found object without object_ref in test ${criteriaRef}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
131
130
|
}
|
|
132
131
|
if (foundCriteriaRefererence.state) {
|
|
133
|
-
foundCriteriaRefererence.state
|
|
134
|
-
if (
|
|
135
|
-
logger.warn(`Found state without state_ref in test ${criteriaRef}`);
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
132
|
+
for (const state of foundCriteriaRefererence.state) {
|
|
133
|
+
if (state['@_state_ref']) {
|
|
138
134
|
const stateRef = state['@_state_ref'];
|
|
139
135
|
const foundStateReference = searchTree(parsed.oval_definitions[0].states, (oNode) => oNode['@_id'] === stateRef, false)[0];
|
|
140
136
|
if (foundStateReference) {
|
|
@@ -144,11 +140,14 @@ function processOVAL(oval) {
|
|
|
144
140
|
logger.warn(`Could not find state ${stateRef} for test ${criteriaRef}`);
|
|
145
141
|
}
|
|
146
142
|
}
|
|
147
|
-
|
|
143
|
+
else {
|
|
144
|
+
logger.warn(`Found state without state_ref in test ${criteriaRef}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
148
147
|
}
|
|
149
148
|
}
|
|
150
149
|
return { ...foundCriteriaRefererence, resolvedObjects: foundObjects, resolvedStates: foundStates };
|
|
151
|
-
}).filter(
|
|
150
|
+
}).filter(Boolean);
|
|
152
151
|
}
|
|
153
152
|
}
|
|
154
153
|
}
|
package/lib/parsers/xccdf.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Profile from '../objects/profile';
|
|
2
|
-
import { BenchmarkGroup, BenchmarkRule, RuleComplexCheck } from '../types/xccdf';
|
|
3
2
|
import { OvalDefinitionValue } from '../types/oval';
|
|
3
|
+
import { BenchmarkGroup, BenchmarkRule, RuleComplexCheck } from '../types/xccdf';
|
|
4
4
|
export type GroupContextualizedRule = BenchmarkRule & {
|
|
5
5
|
group: Omit<BenchmarkGroup, 'Rule' | 'Group'>;
|
|
6
6
|
};
|
package/lib/parsers/xccdf.js
CHANGED
|
@@ -4,13 +4,13 @@ exports.extractAllRules = extractAllRules;
|
|
|
4
4
|
exports.extractAllComplexChecks = extractAllComplexChecks;
|
|
5
5
|
exports.processXCCDF = processXCCDF;
|
|
6
6
|
const tslib_1 = require("tslib");
|
|
7
|
-
const
|
|
8
|
-
const xccdf_1 = require("../utilities/xccdf");
|
|
9
|
-
const control_1 = tslib_1.__importDefault(require("../objects/control"));
|
|
7
|
+
const htmlfy_1 = require("htmlfy");
|
|
10
8
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
11
|
-
const
|
|
12
|
-
const
|
|
9
|
+
const cci_nist_mapping_data_1 = require("../mappings/cci-nist-mapping-data");
|
|
10
|
+
const control_1 = tslib_1.__importDefault(require("../objects/control"));
|
|
11
|
+
const profile_1 = tslib_1.__importDefault(require("../objects/profile"));
|
|
13
12
|
const logging_1 = require("../utilities/logging");
|
|
13
|
+
const xccdf_1 = require("../utilities/xccdf");
|
|
14
14
|
/**
|
|
15
15
|
* Extracts all rules from the given benchmark groups, including nested groups.
|
|
16
16
|
*
|
|
@@ -19,19 +19,19 @@ const logging_1 = require("../utilities/logging");
|
|
|
19
19
|
*/
|
|
20
20
|
function extractAllRules(groups) {
|
|
21
21
|
const rules = [];
|
|
22
|
-
|
|
22
|
+
for (const group of groups) {
|
|
23
23
|
if (group.Rule) {
|
|
24
24
|
rules.push(...(group.Rule.map((rule) => {
|
|
25
25
|
return {
|
|
26
26
|
...rule,
|
|
27
|
-
group: lodash_1.default.omit(group, ['Rule', 'Group'])
|
|
27
|
+
group: lodash_1.default.omit(group, ['Rule', 'Group']),
|
|
28
28
|
};
|
|
29
29
|
})));
|
|
30
30
|
}
|
|
31
31
|
if (group.Group) {
|
|
32
32
|
rules.push(...extractAllRules(group.Group));
|
|
33
33
|
}
|
|
34
|
-
}
|
|
34
|
+
}
|
|
35
35
|
return rules;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
@@ -47,10 +47,10 @@ function extractAllRules(groups) {
|
|
|
47
47
|
function extractAllComplexChecks(complexCheck) {
|
|
48
48
|
const complexChecks = [lodash_1.default.omit(complexCheck, 'complex-check')];
|
|
49
49
|
if (complexCheck['complex-check']) {
|
|
50
|
-
complexChecks.push(...complexCheck['complex-check'].map(
|
|
51
|
-
complexCheck['complex-check']
|
|
50
|
+
complexChecks.push(...complexCheck['complex-check'].map(subComplexCheck => lodash_1.default.omit(subComplexCheck, 'complex-check')));
|
|
51
|
+
for (const subComplexCheck of complexCheck['complex-check']) {
|
|
52
52
|
complexChecks.push(...extractAllComplexChecks(subComplexCheck));
|
|
53
|
-
}
|
|
53
|
+
}
|
|
54
54
|
}
|
|
55
55
|
return complexChecks;
|
|
56
56
|
}
|
|
@@ -70,9 +70,9 @@ function extractAllComplexChecks(complexCheck) {
|
|
|
70
70
|
function ensureDecodedXMLStringValue(input, defaultValue) {
|
|
71
71
|
return lodash_1.default.isString(input)
|
|
72
72
|
? input
|
|
73
|
-
: lodash_1.default.isArray(input)
|
|
73
|
+
: (lodash_1.default.isArray(input)
|
|
74
74
|
? lodash_1.default.get(input, '[0].#text', defaultValue)
|
|
75
|
-
: lodash_1.default.get(input, '#text', defaultValue);
|
|
75
|
+
: lodash_1.default.get(input, '#text', defaultValue));
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* Processes an XCCDF (Extensible Configuration Checklist Description Format) XML
|
|
@@ -101,31 +101,31 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
101
101
|
// Variable used to store the profile data.
|
|
102
102
|
// The name is the benchmark Id, title and summary are from benchmark.
|
|
103
103
|
const profile = new profile_1.default({
|
|
104
|
-
//name: parsedXML.Benchmark[0]['@_id'],
|
|
104
|
+
// name: parsedXML.Benchmark[0]['@_id'],
|
|
105
105
|
// title: (parsedXML.Benchmark[0].title[0] as FrontMatter)['#text'],
|
|
106
106
|
// summary: (parsedXML.Benchmark[0].description[0] as RationaleElement)['#text']
|
|
107
107
|
name: Array.isArray(parsedXML.Benchmark[0]['@_id'])
|
|
108
|
-
? parsedXML.Benchmark[0]['@_id'].map(n => n['@_id']).join(' ') === ''
|
|
108
|
+
? (parsedXML.Benchmark[0]['@_id'].map(n => n['@_id']).join(' ') === ''
|
|
109
109
|
? parsedXML.Benchmark[0]['@_id'].map(n => n).join(' ')
|
|
110
|
-
: parsedXML.Benchmark[0]['@_id'].join(' ')
|
|
110
|
+
: parsedXML.Benchmark[0]['@_id'].join(' '))
|
|
111
111
|
: parsedXML.Benchmark[0]['@_id'],
|
|
112
112
|
title: Array.isArray(parsedXML.Benchmark[0].title)
|
|
113
|
-
? parsedXML.Benchmark[0].title.map(t => t['#text']).join(' ') === ''
|
|
113
|
+
? (parsedXML.Benchmark[0].title.map(t => t['#text']).join(' ') === ''
|
|
114
114
|
? parsedXML.Benchmark[0].title.map(t => t).join(' ')
|
|
115
|
-
: parsedXML.Benchmark[0].title.map(t => t['#text']).join(' ')
|
|
115
|
+
: parsedXML.Benchmark[0].title.map(t => t['#text']).join(' '))
|
|
116
116
|
: parsedXML.Benchmark[0].title,
|
|
117
117
|
summary: Array.isArray(parsedXML.Benchmark[0].description)
|
|
118
118
|
? parsedXML.Benchmark[0].description.map(d => d['#text']).join(' ') === ''
|
|
119
|
+
// eslint-disable-next-line unicorn/no-nested-ternary
|
|
119
120
|
? parsedXML.Benchmark[0].description.map(d => d['p'] || '').join(' ') === ''
|
|
120
121
|
? parsedXML.Benchmark[0].description.map(d => d).join(' ')
|
|
121
122
|
: parsedXML.Benchmark[0].description.map(d => d['p'] || '').join(' ')
|
|
122
123
|
: parsedXML.Benchmark[0].description.map(d => d['#text']).join(' ')
|
|
123
|
-
: parsedXML.Benchmark[0].description
|
|
124
|
+
: parsedXML.Benchmark[0].description,
|
|
124
125
|
});
|
|
125
126
|
// Process each rule, extracting the necessary
|
|
126
127
|
// data and save it to the profile variable.
|
|
127
|
-
|
|
128
|
-
var _a, _b, _c;
|
|
128
|
+
for (const rule of rules) {
|
|
129
129
|
// The description tag contains the following tags:
|
|
130
130
|
// "FalsePositives", "FalseNegatives", "Documentable", "Mitigations",
|
|
131
131
|
// "SeverityOverrideGuidance", "PotentialImpacts", "ThirdPartyTools",
|
|
@@ -137,12 +137,12 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
137
137
|
}
|
|
138
138
|
else {
|
|
139
139
|
if (typeof lodash_1.default.get(rule.description, '[0].p') === 'string') {
|
|
140
|
-
extractedDescription = (0,
|
|
140
|
+
extractedDescription = (0, htmlfy_1.prettify)(lodash_1.default.get(rule.description, '[0].p'));
|
|
141
141
|
}
|
|
142
142
|
else {
|
|
143
143
|
if (Array.isArray(lodash_1.default.get(rule.description, '[0].p'))) {
|
|
144
144
|
const joinedDescriptions = lodash_1.default.get(rule.description, '[0].p');
|
|
145
|
-
extractedDescription = (0,
|
|
145
|
+
extractedDescription = (0, htmlfy_1.prettify)(joinedDescriptions.join('\n\n'));
|
|
146
146
|
extractedDescription = (0, xccdf_1.removeHtmlTags)(extractedDescription).replace('\n', ' ');
|
|
147
147
|
}
|
|
148
148
|
else if (Array.isArray(rule.description)) {
|
|
@@ -161,25 +161,23 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
161
161
|
const control = new control_1.default();
|
|
162
162
|
// Update the control Id with the appropriate value based on the rule id.
|
|
163
163
|
switch (useRuleId) {
|
|
164
|
-
case 'group':
|
|
164
|
+
case 'group': {
|
|
165
165
|
control.id = rule.group['@_id'].toString();
|
|
166
166
|
break;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
control.id = rule['@_id'][0];
|
|
173
|
-
}
|
|
167
|
+
}
|
|
168
|
+
case 'rule': {
|
|
169
|
+
control.id = rule['@_id'][0].toLowerCase().startsWith('sv') ? rule['@_id'][0].split('r')[0] : rule['@_id'][0];
|
|
174
170
|
break;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
}
|
|
172
|
+
case 'version': {
|
|
173
|
+
if (rule.version === undefined) {
|
|
174
|
+
throw new Error('The rule type "version" did not provide an identification (Id) value');
|
|
178
175
|
}
|
|
179
176
|
else {
|
|
180
|
-
|
|
177
|
+
control.id = (lodash_1.default.isArray(rule.version)) ? rule.version[0] : rule.version;
|
|
181
178
|
}
|
|
182
179
|
break;
|
|
180
|
+
}
|
|
183
181
|
case 'cis': {
|
|
184
182
|
// Regex explained
|
|
185
183
|
// \d:
|
|
@@ -203,8 +201,9 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
203
201
|
}
|
|
204
202
|
break;
|
|
205
203
|
}
|
|
206
|
-
default:
|
|
204
|
+
default: {
|
|
207
205
|
throw new Error('useRuleId must be one of "group", "rule", "version" for STIG benchmarks, or "cis" for CIS benchmarks');
|
|
206
|
+
}
|
|
208
207
|
}
|
|
209
208
|
if (!(lodash_1.default.isArray(rule.title) && rule.title.length === 1)) {
|
|
210
209
|
throw new Error('Rule title is not an array of length 1. Investigate if the file is in the proper format.');
|
|
@@ -217,7 +216,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
217
216
|
: `[[[MISSING SEVERITY or WEIGHT FROM BENCHMARK]]] ${ensureDecodedXMLStringValue(rule.title[0], 'undefined title')}`);
|
|
218
217
|
// Update the control description (desc) with the extracted description content
|
|
219
218
|
if (typeof extractedDescription === 'object' && !Array.isArray(extractedDescription)) {
|
|
220
|
-
control.desc =
|
|
219
|
+
control.desc = extractedDescription.VulnDiscussion?.split('Satisfies: ')[0] || '';
|
|
221
220
|
}
|
|
222
221
|
else if (typeof extractedDescription === 'object') {
|
|
223
222
|
control.desc = JSON.stringify(extractedDescription);
|
|
@@ -236,11 +235,11 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
236
235
|
}
|
|
237
236
|
// Update the control descriptions (descs) check with the check text from the rule,
|
|
238
237
|
if (rule.check) {
|
|
239
|
-
if (rule.check.some(
|
|
238
|
+
if (rule.check.some(ruleValue => 'check-content' in ruleValue)) {
|
|
240
239
|
control.descs.check = (0, xccdf_1.removeXMLSpecialCharacters)(rule.check ? rule.check[0]['check-content'][0] : 'Missing description');
|
|
241
240
|
control.tags.check_id = rule.check[0]['@_system'];
|
|
242
241
|
}
|
|
243
|
-
else if (rule.check.some(
|
|
242
|
+
else if (rule.check.some(ruleValue => 'check-content-ref' in ruleValue) && ovalDefinitions) {
|
|
244
243
|
let referenceID = null;
|
|
245
244
|
for (const checkContent of rule.check) {
|
|
246
245
|
if ('check-content-ref' in checkContent && checkContent['@_system'].includes('oval')) {
|
|
@@ -261,30 +260,28 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
261
260
|
}
|
|
262
261
|
}
|
|
263
262
|
}
|
|
264
|
-
// Very CIS specific
|
|
265
|
-
else if (rule['complex-check']) {
|
|
263
|
+
else if (rule['complex-check']) { // Very CIS specific
|
|
266
264
|
const checkTexts = [];
|
|
267
265
|
for (const complexChecks of rule['complex-check']) {
|
|
268
266
|
const allComplexChecks = extractAllComplexChecks(complexChecks);
|
|
269
267
|
if (control.id === '1.1.1.5') {
|
|
270
268
|
logger.info(allComplexChecks);
|
|
271
269
|
}
|
|
272
|
-
|
|
270
|
+
for (const complexCheck of allComplexChecks) {
|
|
273
271
|
if (complexCheck.check) {
|
|
274
|
-
complexCheck.check
|
|
275
|
-
|
|
276
|
-
if ((_a = check['@_system']) === null || _a === void 0 ? void 0 : _a.toString().toLowerCase().includes('oval')) {
|
|
272
|
+
for (const check of complexCheck.check) {
|
|
273
|
+
if (check['@_system']?.toString().toLowerCase().includes('oval')) {
|
|
277
274
|
const ovalReference = check['check-content-ref'][0]['@_name'];
|
|
278
275
|
if (!ovalDefinitions) {
|
|
279
276
|
logger.warn(`Missing OVAL definitions! Unable to process OVAL reference: ${ovalReference}`);
|
|
280
277
|
}
|
|
281
278
|
else if (ovalReference && ovalReference in ovalDefinitions) {
|
|
282
|
-
ovalDefinitions[ovalReference].resolvedValues
|
|
279
|
+
for (const resolvedValue of ovalDefinitions[ovalReference].resolvedValues) {
|
|
283
280
|
const comment = resolvedValue['@_comment'];
|
|
284
281
|
if (comment) {
|
|
285
282
|
checkTexts.push(comment + '\n');
|
|
286
283
|
}
|
|
287
|
-
resolvedValue.resolvedObjects
|
|
284
|
+
for (const resolvedObject of resolvedValue.resolvedObjects) {
|
|
288
285
|
// Try to find the associated state for a resolved object
|
|
289
286
|
const resolvedId = resolvedObject['@_id'].split(':')[resolvedValue['@_id'].split(':').length - 1];
|
|
290
287
|
if (resolvedId) {
|
|
@@ -294,18 +291,18 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
294
291
|
}
|
|
295
292
|
}
|
|
296
293
|
checkTexts.push(JSON.stringify(lodash_1.default.pickBy(resolvedObject, (value, key) => !key.startsWith('@_')), null, 2));
|
|
297
|
-
}
|
|
298
|
-
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
299
296
|
}
|
|
300
297
|
}
|
|
301
298
|
else {
|
|
302
299
|
logger.warn(`Found external reference to unknown system: ${check['@_system']}, only OVAL is supported`);
|
|
303
300
|
}
|
|
304
|
-
}
|
|
301
|
+
}
|
|
305
302
|
}
|
|
306
|
-
}
|
|
303
|
+
}
|
|
307
304
|
}
|
|
308
|
-
if (checkTexts.length
|
|
305
|
+
if (checkTexts.length > 0) {
|
|
309
306
|
control.descs.check = checkTexts.join('\n');
|
|
310
307
|
}
|
|
311
308
|
}
|
|
@@ -314,7 +311,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
314
311
|
if (lodash_1.default.get(rule.fixtext, '[0]["#text"]')) {
|
|
315
312
|
control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)(rule.fixtext[0]['#text']);
|
|
316
313
|
}
|
|
317
|
-
else if (
|
|
314
|
+
else if (rule.fixtext === undefined) {
|
|
318
315
|
if (rule.fix && rule.fix[0]) {
|
|
319
316
|
control.descs.fix = (0, xccdf_1.removeHtmlTags)(rule.fix[0]['#text'] || 'Missing fix text');
|
|
320
317
|
}
|
|
@@ -323,19 +320,11 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
323
320
|
control.descs.fix = (0, xccdf_1.removeHtmlTags)(rule.fixtext[0]);
|
|
324
321
|
}
|
|
325
322
|
else if (typeof rule.fixtext[0] === 'object') {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
else {
|
|
332
|
-
return fixtext;
|
|
333
|
-
}
|
|
334
|
-
}))));
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
control.descs.fix = (0, xccdf_1.removeHtmlTags)((0, xccdf_1.removeXMLSpecialCharacters)((0, pretty_1.default)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext)))).replace('\n', ' ').trim();
|
|
338
|
-
}
|
|
323
|
+
control.descs.fix = Array.isArray(rule.fixtext[0])
|
|
324
|
+
? (0, xccdf_1.removeHtmlTags)((0, htmlfy_1.prettify)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext[0].map((fixtext) => {
|
|
325
|
+
return fixtext.div || fixtext;
|
|
326
|
+
}))))
|
|
327
|
+
: (0, xccdf_1.removeHtmlTags)((0, xccdf_1.removeXMLSpecialCharacters)((0, htmlfy_1.prettify)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext)))).replace('\n', ' ').trim();
|
|
339
328
|
}
|
|
340
329
|
else {
|
|
341
330
|
control.descs.fix = 'Missing fix text';
|
|
@@ -365,8 +354,8 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
365
354
|
// "SeverityOverrideGuidance", "PotentialImpacts", "ThirdPartyTools",
|
|
366
355
|
// "MitigationControl", "Responsibility", "IAControls"
|
|
367
356
|
if (typeof extractedDescription === 'object') {
|
|
368
|
-
control.tags.satisfies
|
|
369
|
-
|
|
357
|
+
control.tags.satisfies
|
|
358
|
+
= extractedDescription.VulnDiscussion?.includes('Satisfies: ') && extractedDescription.VulnDiscussion.split('Satisfies: ').length > 0
|
|
370
359
|
? extractedDescription.VulnDiscussion.split('Satisfies: ')[1].split(',').map(satisfaction => satisfaction.trim())
|
|
371
360
|
: undefined;
|
|
372
361
|
control.tags.false_negatives = extractedDescription.FalseNegatives || undefined;
|
|
@@ -384,7 +373,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
384
373
|
control.tags.ia_controls = extractedDescription.IAControls || undefined;
|
|
385
374
|
}
|
|
386
375
|
// Ensure that tags inside the tags array are not an array
|
|
387
|
-
control.tags = lodash_1.default.mapValues(lodash_1.default.omitBy(control.tags,
|
|
376
|
+
control.tags = lodash_1.default.mapValues(lodash_1.default.omitBy(control.tags, value => value === undefined), (value) => {
|
|
388
377
|
if (value && Array.isArray(value)) {
|
|
389
378
|
if (Array.isArray(value[0])) {
|
|
390
379
|
return (0, xccdf_1.removeXMLSpecialCharacters)(value[0][0]);
|
|
@@ -405,110 +394,106 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
405
394
|
});
|
|
406
395
|
// Get all identifiers from the rule; cci, nist, and legacy
|
|
407
396
|
if (rule.ident) {
|
|
408
|
-
rule.ident
|
|
409
|
-
var _a, _b, _c;
|
|
397
|
+
for (const identifier of rule.ident) {
|
|
410
398
|
// Get CCIs
|
|
411
399
|
if (identifier['@_system'][0].toLowerCase().includes('cci')) {
|
|
412
400
|
if (!('cci' in control.tags)) {
|
|
413
401
|
control.tags.cci = [];
|
|
414
402
|
}
|
|
415
|
-
|
|
403
|
+
control.tags.cci?.push(identifier['#text']);
|
|
416
404
|
}
|
|
417
|
-
// Get legacy identifiers
|
|
418
|
-
else if (identifier['@_system'][0].toLowerCase().includes('legacy')) {
|
|
405
|
+
else if (identifier['@_system'][0].toLowerCase().includes('legacy')) { // Get legacy identifiers
|
|
419
406
|
if (!('legacy' in control.tags)) {
|
|
420
407
|
control.tags.legacy = [];
|
|
421
408
|
}
|
|
422
|
-
|
|
409
|
+
control.tags.legacy?.push(identifier['#text']);
|
|
423
410
|
}
|
|
424
|
-
// Get NIST identifiers
|
|
425
|
-
else if (identifier['@_system'].toString().toLowerCase().includes('nist')) {
|
|
411
|
+
else if (identifier['@_system'].toString().toLowerCase().includes('nist')) { // Get NIST identifiers
|
|
426
412
|
if (!('nist' in control.tags)) {
|
|
427
413
|
control.tags.nist = [];
|
|
428
414
|
}
|
|
429
|
-
|
|
415
|
+
control.tags.nist?.push(identifier['#text']);
|
|
430
416
|
}
|
|
431
|
-
}
|
|
417
|
+
}
|
|
432
418
|
}
|
|
433
419
|
// Update control references with content from the benchmark rule object
|
|
434
|
-
(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
452
|
-
(_c = control.refs) === null || _c === void 0 ? void 0 : _c.push({
|
|
453
|
-
ref: referenceText,
|
|
454
|
-
uri: referenceURL
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
else {
|
|
459
|
-
if ('title' in reference) {
|
|
460
|
-
const title = lodash_1.default.get(reference, 'title');
|
|
461
|
-
if (Array.isArray(title)) {
|
|
462
|
-
(_d = control.refs) === null || _d === void 0 ? void 0 : _d.push(title[0]);
|
|
420
|
+
if (rule.reference)
|
|
421
|
+
for (const reference of rule.reference) {
|
|
422
|
+
if (lodash_1.default.get(reference, '@_href') === '') {
|
|
423
|
+
control.refs?.push(lodash_1.default.get(reference, '#text', 'undefined href'));
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
try {
|
|
427
|
+
const referenceText = lodash_1.default.get(reference, '#text') || '';
|
|
428
|
+
const referenceURL = lodash_1.default.get(reference, '@_href') || '';
|
|
429
|
+
if (referenceURL) {
|
|
430
|
+
const parsedURL = new URL(lodash_1.default.get(reference, '@_href', 'undefined href'));
|
|
431
|
+
if (parsedURL.protocol.toLowerCase().includes('http') || parsedURL.protocol.toLowerCase().includes('https')) {
|
|
432
|
+
control.refs?.push({
|
|
433
|
+
ref: referenceText,
|
|
434
|
+
url: referenceURL,
|
|
435
|
+
});
|
|
463
436
|
}
|
|
464
437
|
else {
|
|
465
|
-
|
|
438
|
+
control.refs?.push({
|
|
439
|
+
ref: referenceText,
|
|
440
|
+
uri: referenceURL,
|
|
441
|
+
});
|
|
466
442
|
}
|
|
467
443
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
control.tags[identifierType] = [identifier];
|
|
444
|
+
else {
|
|
445
|
+
if ('title' in reference) {
|
|
446
|
+
const title = lodash_1.default.get(reference, 'title');
|
|
447
|
+
if (Array.isArray(title)) {
|
|
448
|
+
control.refs?.push(title[0]);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
control.refs?.push(lodash_1.default.get(reference, 'title'));
|
|
452
|
+
}
|
|
478
453
|
}
|
|
479
|
-
|
|
480
|
-
|
|
454
|
+
}
|
|
455
|
+
// Add the reference to the control tags when separated by §
|
|
456
|
+
if (typeof referenceText === 'string' && referenceText.includes('§')) {
|
|
457
|
+
const referenceParts = referenceText.split('§');
|
|
458
|
+
if (referenceParts.length == 2) {
|
|
459
|
+
// eslint-disable-next-line prefer-const
|
|
460
|
+
let [identifierType, identifier] = referenceText.split('§');
|
|
461
|
+
identifierType = identifierType.toLowerCase();
|
|
462
|
+
if (!(identifierType in control.tags)) {
|
|
463
|
+
control.tags[identifierType] = [identifier];
|
|
464
|
+
}
|
|
465
|
+
else if (Array.isArray(control.tags[identifierType])) {
|
|
466
|
+
control.tags[identifierType] = lodash_1.default.union(control.tags[identifierType], [identifier]);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
logger.warn(`Attempted to push identifier to control tags when identifier already exists: ${identifierType}: ${identifier}`);
|
|
470
|
+
}
|
|
481
471
|
}
|
|
482
472
|
else {
|
|
483
|
-
logger.warn(
|
|
473
|
+
logger.warn('Reference parts of invalid length: ', referenceParts);
|
|
484
474
|
}
|
|
485
475
|
}
|
|
486
|
-
else {
|
|
487
|
-
logger.warn('Reference parts of invalid length: ', referenceParts);
|
|
488
|
-
}
|
|
489
476
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
477
|
+
catch (error) {
|
|
478
|
+
logger.warn(`Error parsing ref for control ${control.id}: `);
|
|
479
|
+
logger.warn(JSON.stringify(reference, null, 2));
|
|
480
|
+
logger.warn(error);
|
|
481
|
+
}
|
|
495
482
|
}
|
|
496
483
|
}
|
|
497
|
-
});
|
|
498
484
|
// Associate any CCIs with NIST tags
|
|
499
485
|
if (control.tags.cci) {
|
|
500
|
-
control.tags.cci
|
|
501
|
-
var _a;
|
|
486
|
+
for (const cci of control.tags.cci) {
|
|
502
487
|
if (!('nist' in control.tags)) {
|
|
503
488
|
control.tags.nist = [];
|
|
504
489
|
}
|
|
505
|
-
if (cci in
|
|
506
|
-
|
|
490
|
+
if (cci in cci_nist_mapping_data_1.data) {
|
|
491
|
+
control.tags.nist?.push(lodash_1.default.get(cci_nist_mapping_data_1.data, cci));
|
|
507
492
|
}
|
|
508
|
-
}
|
|
493
|
+
}
|
|
509
494
|
}
|
|
510
495
|
profile.controls.push(control);
|
|
511
|
-
}
|
|
496
|
+
}
|
|
512
497
|
profile.controls = lodash_1.default.sortBy(profile.controls, 'id');
|
|
513
498
|
return profile.toUnformattedObject();
|
|
514
499
|
}
|