@mitre/inspec-objects 2.0.0 → 2.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.
- package/lib/parsers/oval.js +1 -1
- package/lib/parsers/xccdf.d.ts +5 -1
- package/lib/parsers/xccdf.js +128 -32
- package/lib/utilities/diff.js +1 -1
- package/lib/utilities/update.js +1 -1
- package/lib/utilities/xccdf.d.ts +45 -6
- package/lib/utilities/xccdf.js +74 -14
- package/package.json +1 -1
package/lib/parsers/oval.js
CHANGED
|
@@ -99,7 +99,7 @@ function processOVAL(oval) {
|
|
|
99
99
|
if (!oval) {
|
|
100
100
|
return undefined;
|
|
101
101
|
}
|
|
102
|
-
const parsed = (0, xccdf_1.convertEncodedXmlIntoJson)(oval);
|
|
102
|
+
const parsed = (0, xccdf_1.convertEncodedXmlIntoJson)(oval, 'withArrayNoEntitiesOption');
|
|
103
103
|
const extractedDefinitions = {};
|
|
104
104
|
for (const ovalDefinitions of parsed.oval_definitions) {
|
|
105
105
|
for (const definitionList of ovalDefinitions.definitions) {
|
package/lib/parsers/xccdf.d.ts
CHANGED
|
@@ -28,7 +28,11 @@ export type InputTextLang = {
|
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
30
30
|
* Processes an XCCDF XML string and converts it into a Profile object.
|
|
31
|
-
*
|
|
31
|
+
* NOTE: We are using the fast xml parser (FXP) V4 which requires to specify
|
|
32
|
+
* which Whether a single tag should be parsed as an array or an object,
|
|
33
|
+
* it can't be decided by FXP. We process every tag as an array, this is
|
|
34
|
+
* the reason there are numerous tag test, were array index zero [0] is
|
|
35
|
+
* tested.
|
|
32
36
|
*
|
|
33
37
|
* @param xml - The XCCDF XML string to process.
|
|
34
38
|
* @param removeNewlines - A flag indicating whether to remove newlines from the processed data.
|
package/lib/parsers/xccdf.js
CHANGED
|
@@ -55,21 +55,32 @@ function extractAllComplexChecks(complexCheck) {
|
|
|
55
55
|
return complexChecks;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Ensures that the input is decoded
|
|
58
|
+
* Ensures that the input is decoded to a string value.
|
|
59
59
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
60
|
+
* This function takes an input which can be either a string or an array of `InputTextLang` objects.
|
|
61
|
+
* If the input is a string, it returns the input as is.
|
|
62
|
+
* If the input is an array, it attempts to retrieve the `#text` property from the first element of the array.
|
|
63
|
+
* If the input is neither a string nor an array, it attempts to retrieve the `#text` property from the input.
|
|
64
|
+
* If the `#text` property is not found, it returns the provided default value.
|
|
65
|
+
*
|
|
66
|
+
* @param input - The input value which can be a string or an array of `InputTextLang` objects.
|
|
67
|
+
* @param defaultValue - The default value to return if the `#text` property is not found.
|
|
68
|
+
* @returns The decoded string value or the default value.
|
|
66
69
|
*/
|
|
67
70
|
function ensureDecodedXMLStringValue(input, defaultValue) {
|
|
68
|
-
return lodash_1.default.isString(input)
|
|
71
|
+
return lodash_1.default.isString(input)
|
|
72
|
+
? input
|
|
73
|
+
: lodash_1.default.isArray(input)
|
|
74
|
+
? lodash_1.default.get(input, '[0].#text', defaultValue)
|
|
75
|
+
: lodash_1.default.get(input, '#text', defaultValue);
|
|
69
76
|
}
|
|
70
77
|
/**
|
|
71
78
|
* Processes an XCCDF XML string and converts it into a Profile object.
|
|
72
|
-
*
|
|
79
|
+
* NOTE: We are using the fast xml parser (FXP) V4 which requires to specify
|
|
80
|
+
* which Whether a single tag should be parsed as an array or an object,
|
|
81
|
+
* it can't be decided by FXP. We process every tag as an array, this is
|
|
82
|
+
* the reason there are numerous tag test, were array index zero [0] is
|
|
83
|
+
* tested.
|
|
73
84
|
*
|
|
74
85
|
* @param xml - The XCCDF XML string to process.
|
|
75
86
|
* @param removeNewlines - A flag indicating whether to remove newlines from the processed data.
|
|
@@ -84,14 +95,40 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
84
95
|
if (parsedXML.Benchmark === undefined) {
|
|
85
96
|
throw new Error('Could not process the XCCDF file, check the input to make sure this is a properly formatted XCCDF file.');
|
|
86
97
|
}
|
|
98
|
+
// Extracts all rules from the given benchmark groups.
|
|
87
99
|
const rules = extractAllRules(parsedXML.Benchmark[0].Group);
|
|
100
|
+
// Variable used to store the profile data.
|
|
101
|
+
// The name is the benchmark Id, title and summary are from benchmark.
|
|
88
102
|
const profile = new profile_1.default({
|
|
89
|
-
name: parsedXML.Benchmark[0]['@_id'],
|
|
90
|
-
title: parsedXML.Benchmark[0].title[0]['#text'],
|
|
91
|
-
summary: parsedXML.Benchmark[0].description[0]['#text']
|
|
103
|
+
//name: parsedXML.Benchmark[0]['@_id'],
|
|
104
|
+
// title: (parsedXML.Benchmark[0].title[0] as FrontMatter)['#text'],
|
|
105
|
+
// summary: (parsedXML.Benchmark[0].description[0] as RationaleElement)['#text']
|
|
106
|
+
name: Array.isArray(parsedXML.Benchmark[0]['@_id'])
|
|
107
|
+
? parsedXML.Benchmark[0]['@_id'].map(n => n['@_id']).join(' ') === ''
|
|
108
|
+
? parsedXML.Benchmark[0]['@_id'].map(n => n).join(' ')
|
|
109
|
+
: parsedXML.Benchmark[0]['@_id'].join(' ')
|
|
110
|
+
: parsedXML.Benchmark[0]['@_id'],
|
|
111
|
+
title: Array.isArray(parsedXML.Benchmark[0].title)
|
|
112
|
+
? parsedXML.Benchmark[0].title.map(t => t['#text']).join(' ') === ''
|
|
113
|
+
? parsedXML.Benchmark[0].title.map(t => t).join(' ')
|
|
114
|
+
: parsedXML.Benchmark[0].title.map(t => t['#text']).join(' ')
|
|
115
|
+
: parsedXML.Benchmark[0].title,
|
|
116
|
+
summary: Array.isArray(parsedXML.Benchmark[0].description)
|
|
117
|
+
? parsedXML.Benchmark[0].description.map(d => d['#text']).join(' ') === ''
|
|
118
|
+
? parsedXML.Benchmark[0].description.map(d => d['p'] || '').join(' ') === ''
|
|
119
|
+
? parsedXML.Benchmark[0].description.map(d => d).join(' ')
|
|
120
|
+
: parsedXML.Benchmark[0].description.map(d => d['p'] || '').join(' ')
|
|
121
|
+
: parsedXML.Benchmark[0].description.map(d => d['#text']).join(' ')
|
|
122
|
+
: parsedXML.Benchmark[0].description
|
|
92
123
|
});
|
|
124
|
+
// Process each rule, extracting the necessary
|
|
125
|
+
// data and save it to the profile variable.
|
|
93
126
|
rules.forEach(rule => {
|
|
94
127
|
var _a, _b, _c;
|
|
128
|
+
// The description tag contains the following tags:
|
|
129
|
+
// "FalsePositives", "FalseNegatives", "Documentable", "Mitigations",
|
|
130
|
+
// "SeverityOverrideGuidance", "PotentialImpacts", "ThirdPartyTools",
|
|
131
|
+
// "MitigationControl", "Responsibility", "IAControls"
|
|
95
132
|
let extractedDescription;
|
|
96
133
|
if (typeof rule.description === 'object') {
|
|
97
134
|
if (Array.isArray(rule.description) && lodash_1.default.get(rule, "description[0]['#text']")) {
|
|
@@ -105,6 +142,10 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
105
142
|
if (Array.isArray(lodash_1.default.get(rule.description, '[0].p'))) {
|
|
106
143
|
const joinedDescriptions = lodash_1.default.get(rule.description, '[0].p');
|
|
107
144
|
extractedDescription = (0, pretty_1.default)(joinedDescriptions.join('\n\n'));
|
|
145
|
+
extractedDescription = (0, xccdf_1.removeHtmlTags)(extractedDescription).replace('\n', ' ');
|
|
146
|
+
}
|
|
147
|
+
else if (Array.isArray(rule.description)) {
|
|
148
|
+
extractedDescription = (0, xccdf_1.convertEncodedHTMLIntoJson)(rule.description[0]);
|
|
108
149
|
}
|
|
109
150
|
else {
|
|
110
151
|
extractedDescription = JSON.stringify(rule.description);
|
|
@@ -115,10 +156,12 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
115
156
|
else {
|
|
116
157
|
extractedDescription = (0, xccdf_1.convertEncodedHTMLIntoJson)(rule.description);
|
|
117
158
|
}
|
|
159
|
+
// Create a new control object and populate it with the necessary data.
|
|
118
160
|
const control = new control_1.default();
|
|
161
|
+
// Update the control Id with the appropriate value based on the rule id.
|
|
119
162
|
switch (useRuleId) {
|
|
120
163
|
case 'group':
|
|
121
|
-
control.id = rule.group['@_id'];
|
|
164
|
+
control.id = rule.group['@_id'].toString();
|
|
122
165
|
break;
|
|
123
166
|
case 'rule':
|
|
124
167
|
if (rule['@_id'][0].toLowerCase().startsWith('sv')) {
|
|
@@ -129,27 +172,51 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
129
172
|
}
|
|
130
173
|
break;
|
|
131
174
|
case 'version':
|
|
132
|
-
|
|
175
|
+
if (rule.version !== undefined) {
|
|
176
|
+
(lodash_1.default.isArray(rule.version))
|
|
177
|
+
? control.id = rule.version[0]
|
|
178
|
+
: control.id = rule.version;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
throw new Error('The rule type "version" did not provide an identification (Id) value');
|
|
182
|
+
}
|
|
133
183
|
break;
|
|
134
|
-
case 'cis':
|
|
135
|
-
//
|
|
184
|
+
case 'cis': {
|
|
185
|
+
// Regex explained
|
|
186
|
+
// \d:
|
|
187
|
+
// matches a single digit (0-9), the required starting point of the match.
|
|
188
|
+
// (\d?):
|
|
189
|
+
// matches an optional digit, there are three of these in sequence
|
|
190
|
+
// (.\d(\d?)(\d?)(\d?))?:
|
|
191
|
+
// matches an optional group that starts with a period (.) followed
|
|
192
|
+
// by one digit and up to three additional optional digits
|
|
193
|
+
// The pattern is repeated four times to match between zero and four
|
|
194
|
+
// groups of a period followed by one required digit and up to three
|
|
195
|
+
// additional optional digits. The pattern matches:
|
|
196
|
+
// 1, 123, 1.2, 1.234, 1.2.3.4.5, or 1.23.456.7.89
|
|
136
197
|
const controlIdRegex = /\d(\d?)(\d?)(\d?)(.\d(\d?)(\d?)(\d?))?(.\d(\d?)(\d?)(\d?))?(.\d(\d?)(\d?)(\d?))?(.\d(\d?)(\d?)(\d?))?/g;
|
|
137
|
-
// eslint-disable-next-line no-case-declarations
|
|
138
198
|
const controlIdMatch = controlIdRegex.exec(rule['@_id']);
|
|
139
199
|
if (controlIdMatch) {
|
|
140
200
|
control.id = controlIdMatch[0];
|
|
141
201
|
}
|
|
142
202
|
else {
|
|
143
|
-
throw new Error(`Could not parse control ID from rule ID: ${rule['@_id']}. Expecting something in this example format:
|
|
203
|
+
throw new Error(`Could not parse control ID from rule ID: ${rule['@_id']}. Expecting something in this example format: xccdf_org.cisecurity.benchmarks_rule_1.1.11_Rule_title_summary`);
|
|
144
204
|
}
|
|
145
205
|
break;
|
|
206
|
+
}
|
|
146
207
|
default:
|
|
147
|
-
throw new Error('useRuleId must be one of "group", "rule", or "
|
|
208
|
+
throw new Error('useRuleId must be one of "group", "rule", "version" for STIG benchmarks, or "cis" for CIS benchmarks');
|
|
148
209
|
}
|
|
149
210
|
if (!(lodash_1.default.isArray(rule.title) && rule.title.length === 1)) {
|
|
150
211
|
throw new Error('Rule title is not an array of length 1. Investigate if the file is in the proper format.');
|
|
151
212
|
}
|
|
152
|
-
|
|
213
|
+
// Update the control title with the rule.tile content if a rule severity
|
|
214
|
+
// exists after removing any special characters, otherwise set the control
|
|
215
|
+
// title to [[[MISSING SEVERITY FROM BENCHMARK]]], undefined title.
|
|
216
|
+
control.title = (0, xccdf_1.removeXMLSpecialCharacters)(rule['@_severity'] || rule['@_weight']
|
|
217
|
+
? ensureDecodedXMLStringValue(rule.title[0], 'undefined title')
|
|
218
|
+
: `[[[MISSING SEVERITY or WEIGHT FROM BENCHMARK]]] ${ensureDecodedXMLStringValue(rule.title[0], 'undefined title')}`);
|
|
219
|
+
// Update the control description (desc) with the extracted description content
|
|
153
220
|
if (typeof extractedDescription === 'object' && !Array.isArray(extractedDescription)) {
|
|
154
221
|
control.desc = ((_a = extractedDescription.VulnDiscussion) === null || _a === void 0 ? void 0 : _a.split('Satisfies: ')[0]) || '';
|
|
155
222
|
}
|
|
@@ -162,10 +229,13 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
162
229
|
else {
|
|
163
230
|
logger.warn(`Invalid value for extracted description: ${extractedDescription}`);
|
|
164
231
|
}
|
|
232
|
+
// Update the control impact with the severity value from the rule,
|
|
233
|
+
// default to medium (0.5) if not found.
|
|
165
234
|
control.impact = (0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'medium');
|
|
166
235
|
if (!control.descs || Array.isArray(control.descs)) {
|
|
167
236
|
control.descs = {};
|
|
168
237
|
}
|
|
238
|
+
// Update the control descriptions (descs) check with the check text from the rule,
|
|
169
239
|
if (rule.check) {
|
|
170
240
|
if (rule.check.some((ruleValue) => 'check-content' in ruleValue)) {
|
|
171
241
|
control.descs.check = (0, xccdf_1.removeXMLSpecialCharacters)(rule.check ? rule.check[0]['check-content'][0] : 'Missing description');
|
|
@@ -240,20 +310,22 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
240
310
|
control.descs.check = checkTexts.join('\n');
|
|
241
311
|
}
|
|
242
312
|
}
|
|
313
|
+
// Update the control descriptions (descs) fix with content from the rule
|
|
314
|
+
// fixtest, if not found, defaults to "Missing fix text"
|
|
243
315
|
if (lodash_1.default.get(rule.fixtext, '[0]["#text"]')) {
|
|
244
316
|
control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)(rule.fixtext[0]['#text']);
|
|
245
317
|
}
|
|
246
318
|
else if (typeof rule.fixtext === 'undefined') {
|
|
247
319
|
if (rule.fix && rule.fix[0]) {
|
|
248
|
-
control.descs.fix = (0, xccdf_1.
|
|
320
|
+
control.descs.fix = (0, xccdf_1.removeHtmlTags)(rule.fix[0]['#text'] || 'Missing fix text');
|
|
249
321
|
}
|
|
250
322
|
}
|
|
251
323
|
else if (typeof rule.fixtext[0] === 'string') {
|
|
252
|
-
control.descs.fix = (0, xccdf_1.
|
|
324
|
+
control.descs.fix = (0, xccdf_1.removeHtmlTags)(rule.fixtext[0]);
|
|
253
325
|
}
|
|
254
326
|
else if (typeof rule.fixtext[0] === 'object') {
|
|
255
327
|
if (Array.isArray(rule.fixtext[0])) {
|
|
256
|
-
control.descs.fix = (0, xccdf_1.
|
|
328
|
+
control.descs.fix = (0, xccdf_1.removeHtmlTags)((0, pretty_1.default)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext[0].map((fixtext) => {
|
|
257
329
|
if (fixtext.div) {
|
|
258
330
|
return fixtext.div;
|
|
259
331
|
}
|
|
@@ -263,21 +335,25 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
263
335
|
}))));
|
|
264
336
|
}
|
|
265
337
|
else {
|
|
266
|
-
control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)((0, pretty_1.default)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext)));
|
|
338
|
+
control.descs.fix = (0, xccdf_1.removeHtmlTags)((0, xccdf_1.removeXMLSpecialCharacters)((0, pretty_1.default)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext)))).replace('\n', ' ').trim();
|
|
267
339
|
}
|
|
268
340
|
}
|
|
269
341
|
else {
|
|
270
342
|
control.descs.fix = 'Missing fix text';
|
|
271
343
|
}
|
|
344
|
+
// Update the control tags base on corresponding rule tags.
|
|
272
345
|
control.tags.severity = (0, xccdf_1.impactNumberToSeverityString)((0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'medium'));
|
|
273
346
|
control.tags.gid = rule.group['@_id'],
|
|
274
347
|
control.tags.rid = rule['@_id'];
|
|
275
348
|
control.tags.stig_id = rule['version'];
|
|
276
|
-
if (typeof rule.group.title
|
|
277
|
-
control.tags.gtitle = (0, xccdf_1.removeXMLSpecialCharacters)(rule.group.title
|
|
349
|
+
if (typeof rule.group.title === 'string') {
|
|
350
|
+
control.tags.gtitle = (0, xccdf_1.removeXMLSpecialCharacters)(rule.group.title);
|
|
278
351
|
}
|
|
279
352
|
else {
|
|
280
|
-
|
|
353
|
+
const gtitle = lodash_1.default.get(rule.group, 'title[0].#text', 'undefined title') === 'undefined title'
|
|
354
|
+
? lodash_1.default.get(rule.group, 'title[0]', 'undefined title')
|
|
355
|
+
: lodash_1.default.get(rule.group, 'title[0].#text', 'undefined title');
|
|
356
|
+
control.tags.gtitle = typeof gtitle === 'string' ? gtitle : gtitle['#text'] || 'undefined title';
|
|
281
357
|
}
|
|
282
358
|
if (rule['fix'] && rule['fix'].length > 0) {
|
|
283
359
|
control.tags.fix_id = rule['fix'][0]['@_id'];
|
|
@@ -285,11 +361,20 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
285
361
|
if (rule['rationale']) {
|
|
286
362
|
control.tags.rationale = rule['rationale'][0]['#text'];
|
|
287
363
|
}
|
|
364
|
+
// The description tag contains the following tags as well:
|
|
365
|
+
// "FalsePositives", "FalseNegatives", "Documentable", "Mitigations",
|
|
366
|
+
// "SeverityOverrideGuidance", "PotentialImpacts", "ThirdPartyTools",
|
|
367
|
+
// "MitigationControl", "Responsibility", "IAControls"
|
|
288
368
|
if (typeof extractedDescription === 'object') {
|
|
289
|
-
control.tags.satisfies =
|
|
369
|
+
control.tags.satisfies =
|
|
370
|
+
((_b = extractedDescription.VulnDiscussion) === null || _b === void 0 ? void 0 : _b.includes('Satisfies: ')) && extractedDescription.VulnDiscussion.split('Satisfies: ').length >= 1
|
|
371
|
+
? extractedDescription.VulnDiscussion.split('Satisfies: ')[1].split(',').map(satisfaction => satisfaction.trim())
|
|
372
|
+
: undefined;
|
|
290
373
|
control.tags.false_negatives = extractedDescription.FalseNegatives || undefined;
|
|
291
374
|
control.tags.false_positives = extractedDescription.FalsePositives || undefined;
|
|
292
|
-
control.tags.documentable = typeof extractedDescription.Documentable === 'boolean'
|
|
375
|
+
control.tags.documentable = typeof extractedDescription.Documentable === 'boolean'
|
|
376
|
+
? extractedDescription.Documentable
|
|
377
|
+
: undefined;
|
|
293
378
|
control.tags.mitigations = extractedDescription.Mitigations || undefined;
|
|
294
379
|
control.tags.severity_override_guidance = extractedDescription.SeverityOverrideGuidance || undefined;
|
|
295
380
|
control.tags.potential_impacts = extractedDescription.PotentialImpacts || undefined;
|
|
@@ -299,11 +384,15 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
299
384
|
control.tags.responsibility = extractedDescription.Responsibility || undefined;
|
|
300
385
|
control.tags.ia_controls = extractedDescription.IAControls || undefined;
|
|
301
386
|
}
|
|
387
|
+
// Ensure that tags inside the tags array are not an array
|
|
302
388
|
control.tags = lodash_1.default.mapValues(lodash_1.default.omitBy(control.tags, (value) => value === undefined), (value) => {
|
|
303
389
|
if (value && Array.isArray(value)) {
|
|
304
390
|
if (Array.isArray(value[0])) {
|
|
305
391
|
return (0, xccdf_1.removeXMLSpecialCharacters)(value[0][0]);
|
|
306
392
|
}
|
|
393
|
+
else if (value.length > 1) {
|
|
394
|
+
return value;
|
|
395
|
+
}
|
|
307
396
|
else {
|
|
308
397
|
return (0, xccdf_1.removeXMLSpecialCharacters)(value[0]);
|
|
309
398
|
}
|
|
@@ -315,7 +404,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
315
404
|
return value;
|
|
316
405
|
}
|
|
317
406
|
});
|
|
318
|
-
// Get all identifiers from the rule
|
|
407
|
+
// Get all identifiers from the rule; cci, nist, and legacy
|
|
319
408
|
if (rule.ident) {
|
|
320
409
|
rule.ident.forEach((identifier) => {
|
|
321
410
|
var _a, _b, _c;
|
|
@@ -342,8 +431,9 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
342
431
|
}
|
|
343
432
|
});
|
|
344
433
|
}
|
|
434
|
+
// Update control references with content from the benchmark rule object
|
|
345
435
|
(_c = rule.reference) === null || _c === void 0 ? void 0 : _c.forEach((reference) => {
|
|
346
|
-
var _a, _b, _c, _d;
|
|
436
|
+
var _a, _b, _c, _d, _e;
|
|
347
437
|
if (lodash_1.default.get(reference, '@_href') === '') {
|
|
348
438
|
(_a = control.refs) === null || _a === void 0 ? void 0 : _a.push(lodash_1.default.get(reference, '#text', 'undefined href'));
|
|
349
439
|
}
|
|
@@ -368,7 +458,13 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
|
368
458
|
}
|
|
369
459
|
else {
|
|
370
460
|
if ('title' in reference) {
|
|
371
|
-
|
|
461
|
+
const title = lodash_1.default.get(reference, 'title');
|
|
462
|
+
if (Array.isArray(title)) {
|
|
463
|
+
(_d = control.refs) === null || _d === void 0 ? void 0 : _d.push(title[0]);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
(_e = control.refs) === null || _e === void 0 ? void 0 : _e.push(lodash_1.default.get(reference, 'title'));
|
|
467
|
+
}
|
|
372
468
|
}
|
|
373
469
|
}
|
|
374
470
|
// Add the reference to the control tags when separated by §
|
package/lib/utilities/diff.js
CHANGED
|
@@ -151,7 +151,7 @@ function diffProfile(fromProfile, toProfile, logger) {
|
|
|
151
151
|
logger.error(`Unable to find existing control ${diffValue[1]}`);
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
else if (diffValue[0] === '+' && !changedControlIds.includes(diffValue[1].
|
|
154
|
+
else if (diffValue[0] === '+' && !changedControlIds.includes(diffValue[1].toLowerCase()) && diffValue[1]) {
|
|
155
155
|
logger.info(JSON.stringify(diffValue));
|
|
156
156
|
logger.info(JSON.stringify(changedControlIds));
|
|
157
157
|
profileDiff.addedControlIDs.push(diffValue[1]);
|
package/lib/utilities/update.js
CHANGED
|
@@ -285,7 +285,7 @@ function getExistingDescribeFromControl(control) {
|
|
|
285
285
|
function findUpdatedControlByAllIdentifiers(existingControl, updatedControls) {
|
|
286
286
|
// Try to match based on IDs
|
|
287
287
|
let updatedControl = updatedControls.find((updatedControl) => {
|
|
288
|
-
return updatedControl.id
|
|
288
|
+
return updatedControl.id.toLowerCase() === existingControl.id.toLowerCase();
|
|
289
289
|
});
|
|
290
290
|
if (updatedControl) {
|
|
291
291
|
return updatedControl;
|
package/lib/utilities/xccdf.d.ts
CHANGED
|
@@ -1,23 +1,55 @@
|
|
|
1
1
|
import { DecodedDescription } from '../types/xccdf';
|
|
2
2
|
/**
|
|
3
|
-
* Converts an encoded XML string into a JSON object
|
|
3
|
+
* Converts an encoded XML string into a JSON object using specified
|
|
4
|
+
* parsing options.
|
|
4
5
|
*
|
|
5
|
-
* @param encodedXml
|
|
6
|
+
* @param encodedXml - The encoded XML string to be converted.
|
|
7
|
+
* @param xmlParserOption - The parsing option to be used. Defaults to
|
|
8
|
+
* 'withArrayOption'.
|
|
9
|
+
* Possible values are:
|
|
10
|
+
* - 'withArrayOption': Parses XML with array option enabled.
|
|
11
|
+
* - 'withArrayNoEntitiesOption': Parses XML with array option
|
|
12
|
+
* enabled and processes entities.
|
|
13
|
+
* - Any other value: Parses XML without array option.
|
|
6
14
|
* @returns The JSON representation of the XML string.
|
|
7
15
|
*
|
|
8
16
|
* @remarks
|
|
9
17
|
* This function uses the `fast-xml-parser` library to parse the XML string.
|
|
10
18
|
* The parser options are configured to:
|
|
11
|
-
* -
|
|
19
|
+
* - Prevent the parser from converting XML entities (converting < into <)
|
|
20
|
+
* - Ignore attributes, allow or disallows attributes to be parsed
|
|
12
21
|
* - Remove namespace prefixes.
|
|
13
22
|
* - Prefix attribute names with '@_'.
|
|
14
|
-
* - Stop parsing
|
|
15
|
-
* - Treat all nodes as arrays
|
|
23
|
+
* - Stop parsing 'div' and 'p' tags.
|
|
24
|
+
* - Treat all nodes as arrays or not
|
|
25
|
+
*
|
|
26
|
+
* Options being used for the XML parser (V4) are:
|
|
27
|
+
* - processEntities: true or false (based on xmlParserOption)
|
|
28
|
+
* - ignoreAttributes: false (allow attributes to be parsed)
|
|
29
|
+
* - removeNSPrefix: true (remove namespace prefixes)
|
|
30
|
+
* - attributeNamePrefix: '@_' (prefix all attribute names with @_)
|
|
31
|
+
* - stopNodes: ["*.pre", "*.p"]
|
|
32
|
+
* - isArray(): true or false (based on xmlParserOption)
|
|
33
|
+
*
|
|
34
|
+
* NOTE: The isArray can specify what tags to always convert into an array, we
|
|
35
|
+
* do not specify specific fields as it could break parsing if future
|
|
36
|
+
* fields are added, we parse all fields as an array.
|
|
16
37
|
*
|
|
17
38
|
* For more details on the parser options, see the documentation for the v4 or v5 version of the library:
|
|
18
39
|
* {@link https://github.com/NaturalIntelligence/fast-xml-parser/tree/master/docs/v4}
|
|
19
40
|
*/
|
|
20
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Converts an encoded XML string into a JSON object using specified parsing options.
|
|
43
|
+
*
|
|
44
|
+
* @param encodedXml - The encoded XML string to be converted.
|
|
45
|
+
* @param xmlParserOption - The parsing option to be used. Defaults to 'withArrayOption'.
|
|
46
|
+
* Possible values are:
|
|
47
|
+
* - 'withArrayOption': Parses XML with array option enabled.
|
|
48
|
+
* - 'withArrayNoEntitiesOption': Parses XML with array option enabled and processes entities.
|
|
49
|
+
* - Any other value: Parses XML without array option.
|
|
50
|
+
* @returns The JSON object resulting from the XML parsing.
|
|
51
|
+
*/
|
|
52
|
+
export declare function convertEncodedXmlIntoJson(encodedXml: string, xmlParserOption?: string): any;
|
|
21
53
|
/**
|
|
22
54
|
* Converts a JSON object into an XML string.
|
|
23
55
|
*
|
|
@@ -35,6 +67,13 @@ export declare function convertJsonIntoXML(data: any): string;
|
|
|
35
67
|
* @returns The decoded string with XML special characters removed.
|
|
36
68
|
*/
|
|
37
69
|
export declare function removeXMLSpecialCharacters(str: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* Removes HTML tags from the given input string.
|
|
72
|
+
*
|
|
73
|
+
* @param input - The string from which HTML tags should be removed.
|
|
74
|
+
* @returns A new string with all HTML tags removed.
|
|
75
|
+
*/
|
|
76
|
+
export declare function removeHtmlTags(input: string): string;
|
|
38
77
|
/**
|
|
39
78
|
* Converts a severity string to a numerical impact value.
|
|
40
79
|
*
|
package/lib/utilities/xccdf.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.convertEncodedXmlIntoJson = convertEncodedXmlIntoJson;
|
|
4
4
|
exports.convertJsonIntoXML = convertJsonIntoXML;
|
|
5
5
|
exports.removeXMLSpecialCharacters = removeXMLSpecialCharacters;
|
|
6
|
+
exports.removeHtmlTags = removeHtmlTags;
|
|
6
7
|
exports.severityStringToImpact = severityStringToImpact;
|
|
7
8
|
exports.impactNumberToSeverityString = impactNumberToSeverityString;
|
|
8
9
|
exports.convertEncodedHTMLIntoJson = convertEncodedHTMLIntoJson;
|
|
@@ -13,33 +14,85 @@ const htmlparser = tslib_1.__importStar(require("htmlparser2"));
|
|
|
13
14
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
14
15
|
const he_1 = tslib_1.__importDefault(require("he"));
|
|
15
16
|
/**
|
|
16
|
-
* Converts an encoded XML string into a JSON object
|
|
17
|
+
* Converts an encoded XML string into a JSON object using specified
|
|
18
|
+
* parsing options.
|
|
17
19
|
*
|
|
18
|
-
* @param encodedXml
|
|
20
|
+
* @param encodedXml - The encoded XML string to be converted.
|
|
21
|
+
* @param xmlParserOption - The parsing option to be used. Defaults to
|
|
22
|
+
* 'withArrayOption'.
|
|
23
|
+
* Possible values are:
|
|
24
|
+
* - 'withArrayOption': Parses XML with array option enabled.
|
|
25
|
+
* - 'withArrayNoEntitiesOption': Parses XML with array option
|
|
26
|
+
* enabled and processes entities.
|
|
27
|
+
* - Any other value: Parses XML without array option.
|
|
19
28
|
* @returns The JSON representation of the XML string.
|
|
20
29
|
*
|
|
21
30
|
* @remarks
|
|
22
31
|
* This function uses the `fast-xml-parser` library to parse the XML string.
|
|
23
32
|
* The parser options are configured to:
|
|
24
|
-
* -
|
|
33
|
+
* - Prevent the parser from converting XML entities (converting < into <)
|
|
34
|
+
* - Ignore attributes, allow or disallows attributes to be parsed
|
|
25
35
|
* - Remove namespace prefixes.
|
|
26
36
|
* - Prefix attribute names with '@_'.
|
|
27
|
-
* - Stop parsing
|
|
28
|
-
* - Treat all nodes as arrays
|
|
37
|
+
* - Stop parsing 'div' and 'p' tags.
|
|
38
|
+
* - Treat all nodes as arrays or not
|
|
39
|
+
*
|
|
40
|
+
* Options being used for the XML parser (V4) are:
|
|
41
|
+
* - processEntities: true or false (based on xmlParserOption)
|
|
42
|
+
* - ignoreAttributes: false (allow attributes to be parsed)
|
|
43
|
+
* - removeNSPrefix: true (remove namespace prefixes)
|
|
44
|
+
* - attributeNamePrefix: '@_' (prefix all attribute names with @_)
|
|
45
|
+
* - stopNodes: ["*.pre", "*.p"]
|
|
46
|
+
* - isArray(): true or false (based on xmlParserOption)
|
|
47
|
+
*
|
|
48
|
+
* NOTE: The isArray can specify what tags to always convert into an array, we
|
|
49
|
+
* do not specify specific fields as it could break parsing if future
|
|
50
|
+
* fields are added, we parse all fields as an array.
|
|
29
51
|
*
|
|
30
52
|
* For more details on the parser options, see the documentation for the v4 or v5 version of the library:
|
|
31
53
|
* {@link https://github.com/NaturalIntelligence/fast-xml-parser/tree/master/docs/v4}
|
|
32
54
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Converts an encoded XML string into a JSON object using specified parsing options.
|
|
57
|
+
*
|
|
58
|
+
* @param encodedXml - The encoded XML string to be converted.
|
|
59
|
+
* @param xmlParserOption - The parsing option to be used. Defaults to 'withArrayOption'.
|
|
60
|
+
* Possible values are:
|
|
61
|
+
* - 'withArrayOption': Parses XML with array option enabled.
|
|
62
|
+
* - 'withArrayNoEntitiesOption': Parses XML with array option enabled and processes entities.
|
|
63
|
+
* - Any other value: Parses XML without array option.
|
|
64
|
+
* @returns The JSON object resulting from the XML parsing.
|
|
65
|
+
*/
|
|
66
|
+
function convertEncodedXmlIntoJson(encodedXml, xmlParserOption = 'withArrayOption') {
|
|
67
|
+
const withArrayOption = {
|
|
68
|
+
processEntities: false,
|
|
35
69
|
ignoreAttributes: false,
|
|
36
70
|
removeNSPrefix: true,
|
|
37
71
|
attributeNamePrefix: '@_',
|
|
38
|
-
stopNodes: ['div', 'p'],
|
|
39
|
-
|
|
40
|
-
isArray: (_name, _jpath, _isLeafNode, _isAttribute) => true,
|
|
72
|
+
stopNodes: ['*.div', '*.p'],
|
|
73
|
+
isArray: () => true,
|
|
41
74
|
};
|
|
42
|
-
const
|
|
75
|
+
const withArrayNoEntitiesOption = {
|
|
76
|
+
processEntities: true,
|
|
77
|
+
ignoreAttributes: false,
|
|
78
|
+
removeNSPrefix: true,
|
|
79
|
+
attributeNamePrefix: '@_',
|
|
80
|
+
stopNodes: ['*.div', '*.p'],
|
|
81
|
+
isArray: () => true,
|
|
82
|
+
};
|
|
83
|
+
const noArrayOption = {
|
|
84
|
+
processEntities: false,
|
|
85
|
+
ignoreAttributes: false,
|
|
86
|
+
removeNSPrefix: true,
|
|
87
|
+
attributeNamePrefix: '@_',
|
|
88
|
+
stopNodes: ['*.div', '*.p'],
|
|
89
|
+
isArray: () => false,
|
|
90
|
+
};
|
|
91
|
+
const parser = new fast_xml_parser_1.XMLParser(xmlParserOption === 'withArrayOption'
|
|
92
|
+
? withArrayOption
|
|
93
|
+
: xmlParserOption === 'withArrayNoEntitiesOption'
|
|
94
|
+
? withArrayNoEntitiesOption
|
|
95
|
+
: noArrayOption);
|
|
43
96
|
return parser.parse(encodedXml);
|
|
44
97
|
}
|
|
45
98
|
/**
|
|
@@ -61,11 +114,18 @@ function convertJsonIntoXML(data) {
|
|
|
61
114
|
* @returns The decoded string with XML special characters removed.
|
|
62
115
|
*/
|
|
63
116
|
function removeXMLSpecialCharacters(str) {
|
|
64
|
-
//console.log('Remove special characters: ', JSON.stringify(str, null, 2));
|
|
65
117
|
const result = he_1.default.decode(str);
|
|
66
|
-
//console.log('Result of he.decode: ', JSON.stringify(result));
|
|
67
118
|
return result;
|
|
68
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Removes HTML tags from the given input string.
|
|
122
|
+
*
|
|
123
|
+
* @param input - The string from which HTML tags should be removed.
|
|
124
|
+
* @returns A new string with all HTML tags removed.
|
|
125
|
+
*/
|
|
126
|
+
function removeHtmlTags(input) {
|
|
127
|
+
return input.replace(/<\/?[^>]+(>|$)/g, '');
|
|
128
|
+
}
|
|
69
129
|
/**
|
|
70
130
|
* Converts a severity string to a numerical impact value.
|
|
71
131
|
*
|
|
@@ -166,7 +226,7 @@ function convertEncodedHTMLIntoJson(encodedHTML) {
|
|
|
166
226
|
});
|
|
167
227
|
htmlParser.write(patchedHTML);
|
|
168
228
|
htmlParser.end();
|
|
169
|
-
const converted = convertEncodedXmlIntoJson(xmlChunks.join(''));
|
|
229
|
+
const converted = convertEncodedXmlIntoJson(xmlChunks.join(''), 'noArrayOption');
|
|
170
230
|
let cleaned = {};
|
|
171
231
|
// Some STIGs have xml tags inside of the actual text which breaks processing,
|
|
172
232
|
// e.g U_ASD_STIG_V5R1_Manual-xccdf.xml and all Oracle Database STIGs
|