@mitre/inspec-objects 1.0.1 → 2.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.
@@ -2,13 +2,39 @@
2
2
  // Utilities to update a profile or control with new metadata
3
3
  // The ultimate goal is to preserve all the metadata that is already there and only add what is new
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.updateProfileUsingXCCDF = exports.updateProfile = exports.updateControl = exports.findUpdatedControlByAllIdentifiers = exports.getExistingDescribeFromControl = void 0;
5
+ exports.getExistingDescribeFromControl = getExistingDescribeFromControl;
6
+ exports.findUpdatedControlByAllIdentifiers = findUpdatedControlByAllIdentifiers;
7
+ exports.updateControl = updateControl;
8
+ exports.updateControlDescribeBlock = updateControlDescribeBlock;
9
+ exports.updateProfile = updateProfile;
10
+ exports.updateProfileUsingXCCDF = updateProfileUsingXCCDF;
6
11
  const tslib_1 = require("tslib");
7
12
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
8
13
  const diff_1 = require("./diff");
14
+ const control_1 = tslib_1.__importDefault(require("../objects/control"));
9
15
  const profile_1 = tslib_1.__importDefault(require("../objects/profile"));
10
16
  const xccdf_1 = require("../parsers/xccdf");
11
17
  const diffMarkdown_1 = require("./diffMarkdown");
18
+ /**
19
+ * Projects values from the source object onto the destination object,
20
+ * updating the destination object in place.
21
+ *
22
+ * @param dst - The destination object to be updated.
23
+ * @param src - The source object containing new values.
24
+ * @param currentPath - The current path being processed (used for nested objects).
25
+ * @returns The updated destination object.
26
+ *
27
+ * @remarks
28
+ * - If a value in the source object is an object and the corresponding value
29
+ * in the destination object is also an object, the function will recursively
30
+ * update the nested object.
31
+ * - If a value in the source object is a string, it will be trimmed before
32
+ * being set in the destination object.
33
+ * - If a value in the source object is a number, it will be directly set in
34
+ * the destination object.
35
+ * - If a value in the source object is an array, the function will merge it with
36
+ * the corresponding array in the destination object, ensuring unique values.
37
+ */
12
38
  function projectValuesOntoExistingObj(dst, src, currentPath = '') {
13
39
  for (const updatedValue in src) {
14
40
  const existingValue = lodash_1.default.get(dst, updatedValue);
@@ -31,28 +57,32 @@ function projectValuesOntoExistingObj(dst, src, currentPath = '') {
31
57
  }
32
58
  return dst;
33
59
  }
60
+ /**
61
+ * Returns an array containing two numerical indices (i.e., start and stop
62
+ * line numbers) for each string or multi-line comment, given raw text as
63
+ * an input parameter. The raw text is a string containing the entirety of an
64
+ * InSpec control.
65
+ *
66
+ * The function utilizes a pair of stacks (i.e., `stack`, `rangeStack`) to keep
67
+ * track of string delimiters and their associated line numbers, respectively.
68
+ *
69
+ * Combinations Handled:
70
+ * - Single quotes (')
71
+ * - Double quotes (")
72
+ * - Back ticks (`)
73
+ * - Mixed quotes ("`'")
74
+ * - Percent strings (%; keys: q, Q, r, i, I, w, W, x; delimiters: (), {},
75
+ * [], <>, most non-alphanumeric characters); (e.g., "%q()")
76
+ * - Percent literals (%; delimiters: (), {}, [], <>, most non-
77
+ * alphanumeric characters); (e.g., "%()")
78
+ * - Multi-line comments (e.g., =begin\nSome comment\n=end)
79
+ * - Variable delimiters (i.e., parenthesis: (); array: []; hash: {})
80
+ *
81
+ * @param text - The raw text containing the entirety of an InSpec control.
82
+ * @returns An array of arrays, each containing two numerical indices representing
83
+ * the start and stop line numbers for each string or multi-line comment.
84
+ */
34
85
  function getRangesForLines(text) {
35
- /*
36
- Returns an array containing two numerical indices (i.e., start and stop
37
- line numbers) for each string or multi-line comment, given raw text as
38
- an input parameter. The raw text is a string containing the entirety of an
39
- InSpec control.
40
-
41
- Algorithm utilizes a pair of stacks (i.e., `stack`, `rangeStack`) to keep
42
- track of string delimiters and their associated line numbers, respectively.
43
-
44
- Combinations Handled:
45
- - Single quotes (')
46
- - Double quotes (")
47
- - Back ticks (`)
48
- - Mixed quotes ("`'")
49
- - Percent strings (%; keys: q, Q, r, i, I, w, W, x; delimiters: (), {},
50
- [], <>, most non-alphanumeric characters); (e.g., "%q()")
51
- - Percent literals (%; delimiters: (), {}, [], <>, most non-
52
- alphanumeric characters); (e.g., "%()")
53
- - Multi-line comments (e.g., =begin\nSome comment\n=end)
54
- - Variable delimiters (i.e., parenthesis: (); array: []; hash: {})
55
- */
56
86
  const stringDelimiters = { '(': ')', '{': '}', '[': ']', '<': '>' };
57
87
  const variableDelimiters = { '(': ')', '{': '}', '[': ']' };
58
88
  const quotes = '\'"`';
@@ -145,11 +175,17 @@ function getRangesForLines(text) {
145
175
  }
146
176
  return ranges;
147
177
  }
178
+ /**
179
+ * Joins lines of text from specified ranges and returns an array of strings.
180
+ * Each range is specified by a start and stop index, and the lines within
181
+ * those ranges are joined together.
182
+ *
183
+ * @param text - The raw text input to be processed.
184
+ * @param ranges - An array of ranges, where each range is a tuple containing
185
+ * the start and stop indices (inclusive) of the lines to be joined.
186
+ * @returns An array of strings, where lines within the specified ranges are joined.
187
+ */
148
188
  function joinMultiLineStringsFromRanges(text, ranges) {
149
- /*
150
- Returns an array of strings and joined strings at specified ranges, given
151
- raw text as an input parameter.
152
- */
153
189
  const originalLines = text.split('\n');
154
190
  const joinedLines = [];
155
191
  let i = 0;
@@ -170,11 +206,16 @@ function joinMultiLineStringsFromRanges(text, ranges) {
170
206
  }
171
207
  return joinedLines;
172
208
  }
209
+ /**
210
+ * Filters out ranges that span only a single line. There is,
211
+ * drops ranges with the same start and stop line numbers (i.e., strings
212
+ * that populate a single line)
213
+ *
214
+ * @param ranges - An array of ranges, where each range is represented by a
215
+ * tuple of start and stop line numbers.
216
+ * @returns An array of ranges that span multiple lines.
217
+ */
173
218
  function getMultiLineRanges(ranges) {
174
- /*
175
- Drops ranges with the same start and stop line numbers (i.e., strings
176
- that populate a single line)
177
- */
178
219
  const multiLineRanges = [];
179
220
  for (const [start, stop] of ranges) {
180
221
  if (start !== stop) {
@@ -183,10 +224,15 @@ function getMultiLineRanges(ranges) {
183
224
  }
184
225
  return multiLineRanges;
185
226
  }
186
- /*
187
- This is the most likely thing to break if you are getting code formatting issues.
188
- Extract the existing describe blocks (what is actually run by inspec for validation)
189
- */
227
+ /**
228
+ * This is the most likely thing to break if you are getting code formatting issues.
229
+ *
230
+ * Extracts the `describe` block (what is actually run by inspec for validation)
231
+ * from an InSpec control object, collapsing multi-line strings.
232
+ *
233
+ * @param control - The InSpec control object containing the code to extract the `describe` block from.
234
+ * @returns The extracted `describe` block as a string, or an empty string if the control has no code.
235
+ */
190
236
  function getExistingDescribeFromControl(control) {
191
237
  if (control.code) {
192
238
  // Join multi-line strings in InSpec control.
@@ -214,17 +260,32 @@ function getExistingDescribeFromControl(control) {
214
260
  }
215
261
  }
216
262
  // Return synthesized logic as describe block
217
- return describeBlock.slice(0, describeBlock.lastIndexOf('end')).join('\n'); // Drop trailing ['end', '\n'] from Control block.
263
+ const lastIndex = (describeBlock.lastIndexOf('end') === -1)
264
+ ? describeBlock.lastIndexOf('end\r')
265
+ : describeBlock.lastIndexOf('end');
266
+ // Drop trailing ['end', '\n'] from Control block.
267
+ return describeBlock.slice(0, lastIndex).join('\n');
218
268
  }
219
269
  else {
220
270
  return '';
221
271
  }
222
272
  }
223
- exports.getExistingDescribeFromControl = getExistingDescribeFromControl;
273
+ /**
274
+ * Finds an updated control from a list of updated controls by matching all possible identifiers.
275
+ *
276
+ * This function attempts to find a matching control in the `updatedControls` array by comparing
277
+ * the `id` of the `existingControl` with the `id` of each control in the `updatedControls` array.
278
+ * If no match is found based on `id`, it then tries to match based on legacy identifiers found
279
+ * in the `tags.legacy` property of each control in the `updatedControls` array.
280
+ *
281
+ * @param existingControl - The control to find a match for in the updated controls.
282
+ * @param updatedControls - An array of updated controls to search through.
283
+ * @returns The matching updated control if found, otherwise `undefined`.
284
+ */
224
285
  function findUpdatedControlByAllIdentifiers(existingControl, updatedControls) {
225
286
  // Try to match based on IDs
226
287
  let updatedControl = updatedControls.find((updatedControl) => {
227
- return updatedControl.id.toLowerCase() === existingControl.id.toLowerCase();
288
+ return updatedControl.id[0].toLowerCase() === existingControl.id[0].toLowerCase();
228
289
  });
229
290
  if (updatedControl) {
230
291
  return updatedControl;
@@ -242,7 +303,14 @@ function findUpdatedControlByAllIdentifiers(existingControl, updatedControls) {
242
303
  }
243
304
  return undefined;
244
305
  }
245
- exports.findUpdatedControlByAllIdentifiers = findUpdatedControlByAllIdentifiers;
306
+ /**
307
+ * Updates a given control object with the provided partial update and logs the process.
308
+ *
309
+ * @param {Control} from - The original control object to be updated.
310
+ * @param {Partial<Control>} update - An object containing the properties to update in the original control.
311
+ * @param {winston.Logger} logger - A logger instance to log debug information.
312
+ * @returns {Control} - The updated control object.
313
+ */
246
314
  function updateControl(from, update, logger) {
247
315
  const existingDescribeBlock = getExistingDescribeFromControl(from);
248
316
  logger.debug(`Existing describe block for control ${from.id}: ${JSON.stringify(existingDescribeBlock)}`);
@@ -250,7 +318,31 @@ function updateControl(from, update, logger) {
250
318
  projectedControl.describe = existingDescribeBlock;
251
319
  return projectedControl;
252
320
  }
253
- exports.updateControl = updateControl;
321
+ /**
322
+ * Updates the describe block of a control with the describe block from another control.
323
+ *
324
+ * @param from - The control from which to get the existing describe block.
325
+ * @param update - The partial control data to update.
326
+ * @param logger - The logger instance to use for logging debug information.
327
+ * @returns The updated control with the describe block from the `from` control.
328
+ */
329
+ function updateControlDescribeBlock(from, update, logger) {
330
+ const existingDescribeBlock = getExistingDescribeFromControl(from);
331
+ logger.debug(`Updating control ${update.id} with this describe block: ${JSON.stringify(existingDescribeBlock)}`);
332
+ const projectedControl = new control_1.default(update);
333
+ projectedControl.describe = existingDescribeBlock;
334
+ return projectedControl;
335
+ }
336
+ /**
337
+ * Updates a given profile with new metadata and controls from another profile.
338
+ *
339
+ * @param from - The original profile to be updated.
340
+ * @param using - The profile containing the new metadata and controls.
341
+ * @param logger - A winston logger instance for logging debug information.
342
+ * @returns An object containing the updated profile and the diff between the original and updated profiles, excluding markdown.
343
+ *
344
+ * @throws Will throw an error if a new control is added but the control data is not available.
345
+ */
254
346
  function updateProfile(from, using, logger) {
255
347
  // Update the profile with the new metadata
256
348
  const to = new profile_1.default(lodash_1.default.omit(from, 'controls'));
@@ -285,9 +377,8 @@ function updateProfile(from, using, logger) {
285
377
  diff,
286
378
  };
287
379
  }
288
- exports.updateProfile = updateProfile;
289
380
  function updateProfileUsingXCCDF(from, using, id, logger, ovalDefinitions) {
290
- logger.debug(`Updating profile ${from.name} with control IDs: ${id}`);
381
+ logger.info(`Updating profile ${from.name} with control IDs type: ${id}`);
291
382
  // Parse the XCCDF benchmark and convert it into a Profile
292
383
  logger.debug('Loading XCCDF File');
293
384
  const xccdfProfile = (0, xccdf_1.processXCCDF)(using, false, id, ovalDefinitions);
@@ -305,4 +396,3 @@ function updateProfileUsingXCCDF(from, using, id, logger, ovalDefinitions) {
305
396
  markdown: markdown
306
397
  };
307
398
  }
308
- exports.updateProfileUsingXCCDF = updateProfileUsingXCCDF;
@@ -1,7 +1,88 @@
1
1
  import { DecodedDescription } from '../types/xccdf';
2
+ /**
3
+ * Converts an encoded XML string into a JSON object.
4
+ *
5
+ * @param encodedXml - The encoded XML string to be converted.
6
+ * @returns The JSON representation of the XML string.
7
+ *
8
+ * @remarks
9
+ * This function uses the `fast-xml-parser` library to parse the XML string.
10
+ * The parser options are configured to:
11
+ * - Not ignore attributes.
12
+ * - Remove namespace prefixes.
13
+ * - Prefix attribute names with '@_'.
14
+ * - Stop parsing at 'div' and 'p' nodes.
15
+ * - Treat all nodes as arrays.
16
+ *
17
+ * For more details on the parser options, see the documentation for the v4 or v5 version of the library:
18
+ * {@link https://github.com/NaturalIntelligence/fast-xml-parser/tree/master/docs/v4}
19
+ */
2
20
  export declare function convertEncodedXmlIntoJson(encodedXml: string): any;
21
+ /**
22
+ * Converts a JSON object into an XML string.
23
+ *
24
+ * @param data - The JSON object to be converted.
25
+ * @returns The XML string representation of the JSON object.
26
+ */
3
27
  export declare function convertJsonIntoXML(data: any): string;
28
+ /**
29
+ * Removes XML special characters from a given string.
30
+ *
31
+ * This function decodes any XML special characters in the input string
32
+ * and returns the decoded result.
33
+ *
34
+ * @param str - The input string containing XML special characters.
35
+ * @returns The decoded string with XML special characters removed.
36
+ */
4
37
  export declare function removeXMLSpecialCharacters(str: string): string;
5
- export declare function severityStringToImpact(string: string, id: string): number;
38
+ /**
39
+ * Converts a severity string to a numerical impact value.
40
+ *
41
+ * The function matches the input string against various regular expressions
42
+ * to determine the corresponding impact value:
43
+ * - "none", "na", "n/a", "not applicable" (case insensitive) -> 0.0
44
+ * - "low", "category iii", "category 3" (case insensitive) -> 0.3
45
+ * - "medium", "category ii", "category 2" -> 0.5
46
+ * - "high", "category i", "category 1" -> 0.7
47
+ * - "critical", "severe" -> 1.0
48
+ *
49
+ * If no match is found, the default impact value is 0.5.
50
+ *
51
+ * @param string - The severity string to be converted.
52
+ * @returns The numerical impact value corresponding to the severity string.
53
+ */
54
+ export declare function severityStringToImpact(string: string): number;
55
+ /**
56
+ * Converts an impact number to a severity string.
57
+ *
58
+ * @param impact - A number representing the impact, which must be between 0.0 and 1.0 inclusive.
59
+ * @returns A string representing the severity level:
60
+ * - 'critical' for impact >= 0.9
61
+ * - 'high' for impact >= 0.7
62
+ * - 'medium' for impact >= 0.4
63
+ * - 'low' for impact >= 0.1
64
+ * - 'none' for impact < 0.1
65
+ * @throws {Error} If the impact is less than 0.0 or greater than 1.0.
66
+ */
6
67
  export declare function impactNumberToSeverityString(impact: number): string;
68
+ /**
69
+ * Converts an encoded HTML string into a JSON object, handling specific edge
70
+ * cases related to XSS and XML tags.
71
+ *
72
+ * This function performs the following steps:
73
+ * 1. Replaces occurrences of `"&lt;"` with a placeholder to avoid
74
+ * breaking parsing.
75
+ * 2. Parses the patched HTML to extract text chunks.
76
+ * 3. Converts the extracted text chunks into JSON.
77
+ * 4. Cleans the converted JSON by replacing placeholders with the original
78
+ * characters and handling nested objects.
79
+ *
80
+ * Note: This function specifically addresses issues found in certain
81
+ * STIGs (Security Technical Implementation Guides) where XML tags are
82
+ * embedded within text fields.
83
+ *
84
+ * @param encodedHTML - The encoded HTML string to be converted.
85
+ * If not provided, an empty object is returned.
86
+ * @returns A `DecodedDescription` object containing the converted JSON data.
87
+ */
7
88
  export declare function convertEncodedHTMLIntoJson(encodedHTML?: string): DecodedDescription;
@@ -1,51 +1,118 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.convertEncodedHTMLIntoJson = exports.impactNumberToSeverityString = exports.severityStringToImpact = exports.removeXMLSpecialCharacters = exports.convertJsonIntoXML = exports.convertEncodedXmlIntoJson = void 0;
3
+ exports.convertEncodedXmlIntoJson = convertEncodedXmlIntoJson;
4
+ exports.convertJsonIntoXML = convertJsonIntoXML;
5
+ exports.removeXMLSpecialCharacters = removeXMLSpecialCharacters;
6
+ exports.severityStringToImpact = severityStringToImpact;
7
+ exports.impactNumberToSeverityString = impactNumberToSeverityString;
8
+ exports.convertEncodedHTMLIntoJson = convertEncodedHTMLIntoJson;
4
9
  const tslib_1 = require("tslib");
5
- const fast_xml_parser_1 = tslib_1.__importDefault(require("fast-xml-parser"));
10
+ const fast_xml_parser_1 = require("fast-xml-parser");
6
11
  const jstoxml_1 = require("jstoxml");
7
12
  const htmlparser = tslib_1.__importStar(require("htmlparser2"));
8
13
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
9
14
  const he_1 = tslib_1.__importDefault(require("he"));
15
+ /**
16
+ * Converts an encoded XML string into a JSON object.
17
+ *
18
+ * @param encodedXml - The encoded XML string to be converted.
19
+ * @returns The JSON representation of the XML string.
20
+ *
21
+ * @remarks
22
+ * This function uses the `fast-xml-parser` library to parse the XML string.
23
+ * The parser options are configured to:
24
+ * - Not ignore attributes.
25
+ * - Remove namespace prefixes.
26
+ * - Prefix attribute names with '@_'.
27
+ * - Stop parsing at 'div' and 'p' nodes.
28
+ * - Treat all nodes as arrays.
29
+ *
30
+ * For more details on the parser options, see the documentation for the v4 or v5 version of the library:
31
+ * {@link https://github.com/NaturalIntelligence/fast-xml-parser/tree/master/docs/v4}
32
+ */
10
33
  function convertEncodedXmlIntoJson(encodedXml) {
11
- return fast_xml_parser_1.default.parse(encodedXml, {
34
+ const options = {
12
35
  ignoreAttributes: false,
13
- ignoreNameSpace: true,
36
+ removeNSPrefix: true,
14
37
  attributeNamePrefix: '@_',
15
38
  stopNodes: ['div', 'p'],
16
- arrayMode: true
17
- });
39
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
40
+ isArray: (_name, _jpath, _isLeafNode, _isAttribute) => true,
41
+ };
42
+ const parser = new fast_xml_parser_1.XMLParser(options);
43
+ return parser.parse(encodedXml);
18
44
  }
19
- exports.convertEncodedXmlIntoJson = convertEncodedXmlIntoJson;
45
+ /**
46
+ * Converts a JSON object into an XML string.
47
+ *
48
+ * @param data - The JSON object to be converted.
49
+ * @returns The XML string representation of the JSON object.
50
+ */
20
51
  function convertJsonIntoXML(data) {
21
52
  return (0, jstoxml_1.toXML)(data);
22
53
  }
23
- exports.convertJsonIntoXML = convertJsonIntoXML;
54
+ /**
55
+ * Removes XML special characters from a given string.
56
+ *
57
+ * This function decodes any XML special characters in the input string
58
+ * and returns the decoded result.
59
+ *
60
+ * @param str - The input string containing XML special characters.
61
+ * @returns The decoded string with XML special characters removed.
62
+ */
24
63
  function removeXMLSpecialCharacters(str) {
25
- return he_1.default.decode(str);
64
+ //console.log('Remove special characters: ', JSON.stringify(str, null, 2));
65
+ const result = he_1.default.decode(str);
66
+ //console.log('Result of he.decode: ', JSON.stringify(result));
67
+ return result;
26
68
  }
27
- exports.removeXMLSpecialCharacters = removeXMLSpecialCharacters;
28
- function severityStringToImpact(string, id) {
69
+ /**
70
+ * Converts a severity string to a numerical impact value.
71
+ *
72
+ * The function matches the input string against various regular expressions
73
+ * to determine the corresponding impact value:
74
+ * - "none", "na", "n/a", "not applicable" (case insensitive) -> 0.0
75
+ * - "low", "category iii", "category 3" (case insensitive) -> 0.3
76
+ * - "medium", "category ii", "category 2" -> 0.5
77
+ * - "high", "category i", "category 1" -> 0.7
78
+ * - "critical", "severe" -> 1.0
79
+ *
80
+ * If no match is found, the default impact value is 0.5.
81
+ *
82
+ * @param string - The severity string to be converted.
83
+ * @returns The numerical impact value corresponding to the severity string.
84
+ */
85
+ function severityStringToImpact(string) {
29
86
  var _a, _b, _c, _d, _e;
30
- if ((_a = string.match(/none|na|n\/a|not[\s()*_|]?applicable/i)) === null || _a === void 0 ? void 0 : _a.length) {
87
+ if ((_a = RegExp(/none|na|n\/a|not[\s()*_|]?applicable/i).exec(string)) === null || _a === void 0 ? void 0 : _a.length) {
31
88
  return 0.0;
32
89
  }
33
- if ((_b = string.match(/low|cat(egory)?\s*(iii|3)/i)) === null || _b === void 0 ? void 0 : _b.length) {
90
+ if ((_b = RegExp(/low|cat(egory)?\s*(iii|3)/i).exec(string)) === null || _b === void 0 ? void 0 : _b.length) {
34
91
  return 0.3;
35
92
  }
36
- if ((_c = string.match(/med(ium)?|cat(egory)?\s*(ii|2)/)) === null || _c === void 0 ? void 0 : _c.length) {
93
+ if ((_c = RegExp(/med(ium)?|cat(egory)?\s*(ii|2)/).exec(string)) === null || _c === void 0 ? void 0 : _c.length) {
37
94
  return 0.5;
38
95
  }
39
- if ((_d = string.match(/high|cat(egory)?\s*(i|1)/)) === null || _d === void 0 ? void 0 : _d.length) {
96
+ if ((_d = RegExp(/high|cat(egory)?\s*(i|1)/).exec(string)) === null || _d === void 0 ? void 0 : _d.length) {
40
97
  return 0.7;
41
98
  }
42
- if ((_e = string.match(/crit(ical)?|severe/)) === null || _e === void 0 ? void 0 : _e.length) {
99
+ if ((_e = RegExp(/crit(ical)?|severe/).exec(string)) === null || _e === void 0 ? void 0 : _e.length) {
43
100
  return 1.0;
44
101
  }
45
- console.log(`${string} is not a valid severity value. It should be one of the approved keywords. ${id} will be treated as medium severity`);
46
102
  return 0.5;
47
103
  }
48
- exports.severityStringToImpact = severityStringToImpact;
104
+ /**
105
+ * Converts an impact number to a severity string.
106
+ *
107
+ * @param impact - A number representing the impact, which must be between 0.0 and 1.0 inclusive.
108
+ * @returns A string representing the severity level:
109
+ * - 'critical' for impact >= 0.9
110
+ * - 'high' for impact >= 0.7
111
+ * - 'medium' for impact >= 0.4
112
+ * - 'low' for impact >= 0.1
113
+ * - 'none' for impact < 0.1
114
+ * @throws {Error} If the impact is less than 0.0 or greater than 1.0.
115
+ */
49
116
  function impactNumberToSeverityString(impact) {
50
117
  // Impact must be 0.0 - 1.0
51
118
  if (impact < 0.0 || impact > 1.0) {
@@ -67,7 +134,26 @@ function impactNumberToSeverityString(impact) {
67
134
  return 'none';
68
135
  }
69
136
  }
70
- exports.impactNumberToSeverityString = impactNumberToSeverityString;
137
+ /**
138
+ * Converts an encoded HTML string into a JSON object, handling specific edge
139
+ * cases related to XSS and XML tags.
140
+ *
141
+ * This function performs the following steps:
142
+ * 1. Replaces occurrences of `"&lt;"` with a placeholder to avoid
143
+ * breaking parsing.
144
+ * 2. Parses the patched HTML to extract text chunks.
145
+ * 3. Converts the extracted text chunks into JSON.
146
+ * 4. Cleans the converted JSON by replacing placeholders with the original
147
+ * characters and handling nested objects.
148
+ *
149
+ * Note: This function specifically addresses issues found in certain
150
+ * STIGs (Security Technical Implementation Guides) where XML tags are
151
+ * embedded within text fields.
152
+ *
153
+ * @param encodedHTML - The encoded HTML string to be converted.
154
+ * If not provided, an empty object is returned.
155
+ * @returns A `DecodedDescription` object containing the converted JSON data.
156
+ */
71
157
  function convertEncodedHTMLIntoJson(encodedHTML) {
72
158
  if (encodedHTML) {
73
159
  // Some STIGs regarding XSS put the < character inside of the description which breaks parsing
@@ -82,9 +168,15 @@ function convertEncodedHTMLIntoJson(encodedHTML) {
82
168
  htmlParser.end();
83
169
  const converted = convertEncodedXmlIntoJson(xmlChunks.join(''));
84
170
  let cleaned = {};
85
- if (typeof converted.VulnDiscussion === 'object') { // Some STIGs have xml tags inside of the actual text which breaks processing, e.g U_ASD_STIG_V5R1_Manual-xccdf.xml and all Oracle Database STIGs
171
+ // Some STIGs have xml tags inside of the actual text which breaks processing,
172
+ // e.g U_ASD_STIG_V5R1_Manual-xccdf.xml and all Oracle Database STIGs
173
+ if (typeof converted.VulnDiscussion === 'object') {
86
174
  let extractedVulnDescription = '';
87
- const remainingFields = lodash_1.default.omit(converted.VulnDiscussion, ['FalsePositives', 'FalseNegatives', 'Documentable', 'Mitigations', 'SeverityOverrideGuidance', 'PotentialImpacts', 'ThirdPartyTools', 'MitigationControl', 'Responsibility', 'IAControls']);
175
+ const remainingFields = lodash_1.default.omit(converted.VulnDiscussion, [
176
+ 'FalsePositives', 'FalseNegatives', 'Documentable', 'Mitigations',
177
+ 'SeverityOverrideGuidance', 'PotentialImpacts', 'ThirdPartyTools',
178
+ 'MitigationControl', 'Responsibility', 'IAControls'
179
+ ]);
88
180
  Object.entries(remainingFields).forEach(([field, value]) => {
89
181
  extractedVulnDescription += `<${field}> ${value}`;
90
182
  });
@@ -114,4 +206,3 @@ function convertEncodedHTMLIntoJson(encodedHTML) {
114
206
  }
115
207
  return {};
116
208
  }
117
- exports.convertEncodedHTMLIntoJson = convertEncodedHTMLIntoJson;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mitre/inspec-objects",
3
- "version": "1.0.1",
3
+ "version": "2.0.0",
4
4
  "description": "Typescript objects for normalizing between InSpec profiles and XCCDF benchmarks",
5
5
  "main": "lib/index.js",
6
6
  "publishConfig": {
@@ -24,36 +24,36 @@
24
24
  },
25
25
  "homepage": "https://github.com/mitre/ts-inspec-objects#readme",
26
26
  "dependencies": {
27
- "@types/flat": "^5.0.2",
27
+ "@types/flat": "5.0.2",
28
28
  "@types/he": "^1.1.2",
29
- "@types/json-diff": "^0.7.0",
29
+ "@types/json-diff": "^1.0.0",
30
30
  "@types/jstoxml": "^2.0.2",
31
31
  "@types/lodash": "^4.14.178",
32
32
  "@types/mustache": "^4.2.0",
33
33
  "@types/pretty": "^2.0.1",
34
- "fast-xml-parser": "^3.1.19",
35
- "flat": "^5.0.2",
34
+ "fast-xml-parser": "^4.5.1",
35
+ "flat": "5.0.2",
36
36
  "he": "^1.2.0",
37
- "htmlparser2": "^7.2.0",
37
+ "htmlparser2": "^10.0.0",
38
38
  "inspecjs": "^2.6.6",
39
- "jest": "^28.1.1",
40
- "json-diff": "^0.9.0",
41
- "jstoxml": "^3.2.3",
39
+ "json-diff": "^1.0.6",
40
+ "jstoxml": "^5.0.2",
42
41
  "lodash": "^4.17.21",
43
42
  "mustache": "^4.2.0",
44
43
  "pretty": "^2.0.0",
45
- "ts-jest": "^28.0.4",
46
- "typescript": "^4.5.5",
47
44
  "winston": "^3.8.1",
48
- "yaml": "^1.10.2"
45
+ "yaml": "^2.3.1"
49
46
  },
50
47
  "devDependencies": {
51
- "@types/jest": "^28.1.1",
52
- "@types/node": "^17.0.18",
53
- "@typescript-eslint/eslint-plugin": "^5.47.0",
54
- "@typescript-eslint/parser": "^5.47.0",
48
+ "@types/jest": "^29.5.12",
49
+ "@types/node": "^22.5.2",
50
+ "@typescript-eslint/eslint-plugin": "^6.4.1",
51
+ "@typescript-eslint/parser": "^6.0.0",
55
52
  "eslint": "^8.30.0",
56
- "tslib": "^2.4.0"
53
+ "jest": "^29.7.0",
54
+ "ts-jest": "^29.1.1",
55
+ "tslib": "^2.4.0",
56
+ "typescript": "^5.2.2"
57
57
  },
58
58
  "jest": {
59
59
  "rootDir": ".",