@mitre/inspec-objects 0.0.25 → 0.0.28

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.
@@ -1,2 +1,3 @@
1
- import { OvalDefinitionValue } from '../types/oval';
1
+ import { OvalDefinitionValue, DefinitionCriterion } from '../types/oval';
2
+ export declare function extractAllCriteriaRefs(initialCriteria: DefinitionCriterion[]): string[];
2
3
  export declare function processOVAL(oval?: string): Record<string, OvalDefinitionValue> | undefined;
@@ -1,8 +1,58 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.processOVAL = void 0;
3
+ exports.processOVAL = exports.extractAllCriteriaRefs = void 0;
4
4
  const xccdf_1 = require("../utilities/xccdf");
5
+ // https://stackoverflow.com/questions/9133500/how-to-find-a-node-in-a-tree-with-javascript
6
+ function searchTree(aTree, fCompair, bGreedy) {
7
+ var aInnerTree = []; // will contain the inner children
8
+ var oNode; // always the current node
9
+ var aReturnNodes = []; // the nodes array which will returned
10
+ // 1. loop through all root nodes so we don't touch the tree structure
11
+ for (const keysTree in aTree) {
12
+ aInnerTree.push(aTree[keysTree]);
13
+ }
14
+ while (aInnerTree.length > 0) {
15
+ oNode = aInnerTree.pop();
16
+ // check current node
17
+ if (fCompair(oNode)) {
18
+ aReturnNodes.push(oNode);
19
+ if (!bGreedy) {
20
+ return aReturnNodes;
21
+ }
22
+ }
23
+ else { // if (node.children && node.children.length) {
24
+ // find other objects, 1. check all properties of the node if they are arrays
25
+ for (const keysNode in oNode) {
26
+ // true if the property is an array
27
+ if (oNode[keysNode] instanceof Array) {
28
+ // 2. push all array object to aInnerTree to search in those later
29
+ for (var i = 0; i < oNode[keysNode].length; i++) {
30
+ aInnerTree.push(oNode[keysNode][i]);
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ return aReturnNodes;
37
+ }
38
+ function extractAllCriteriaRefs(initialCriteria) {
39
+ const criteriaRefs = [];
40
+ initialCriteria.forEach(criteria => {
41
+ var _a;
42
+ (_a = criteria.criterion) === null || _a === void 0 ? void 0 : _a.forEach((criterion) => {
43
+ if (criterion["@_test_ref"]) {
44
+ criteriaRefs.push(criterion["@_test_ref"]);
45
+ }
46
+ });
47
+ if (criteria.criteria) {
48
+ criteriaRefs.push(...extractAllCriteriaRefs(criteria.criteria));
49
+ }
50
+ });
51
+ return criteriaRefs;
52
+ }
53
+ exports.extractAllCriteriaRefs = extractAllCriteriaRefs;
5
54
  function processOVAL(oval) {
55
+ var _a;
6
56
  if (!oval) {
7
57
  return undefined;
8
58
  }
@@ -12,6 +62,50 @@ function processOVAL(oval) {
12
62
  for (const definitionList of ovalDefinitions.definitions) {
13
63
  for (const definition of definitionList.definition) {
14
64
  extractedDefinitions[definition["@_id"]] = definition;
65
+ extractedDefinitions[definition["@_id"]].criteriaRefs = extractAllCriteriaRefs(definition.criteria);
66
+ extractedDefinitions[definition["@_id"]].resolvedValues = (_a = extractedDefinitions[definition["@_id"]].criteriaRefs) === null || _a === void 0 ? void 0 : _a.map((criteriaRef) => {
67
+ // Extract the original criteria from the oval file
68
+ const foundCriteriaRefererence = searchTree(parsed.oval_definitions[0].tests, (oNode) => oNode["@_id"] === criteriaRef, false)[0];
69
+ let foundObjects = [];
70
+ let foundStates = [];
71
+ if (foundCriteriaRefererence) {
72
+ if (foundCriteriaRefererence.object) {
73
+ foundCriteriaRefererence.object.forEach((object) => {
74
+ if (!object["@_object_ref"]) {
75
+ console.warn(`Found object without object_ref in test ${criteriaRef}`);
76
+ }
77
+ else {
78
+ const objectRef = object["@_object_ref"];
79
+ const foundObjectReference = searchTree(parsed.oval_definitions[0].objects, (oNode) => oNode["@_id"] === objectRef, false)[0];
80
+ if (foundObjectReference) {
81
+ foundObjects.push(foundObjectReference);
82
+ }
83
+ else {
84
+ console.warn(`Could not find object ${objectRef} for test ${criteriaRef}`);
85
+ }
86
+ }
87
+ });
88
+ }
89
+ if (foundCriteriaRefererence.state) {
90
+ foundCriteriaRefererence.state.forEach((state) => {
91
+ if (!state["@_state_ref"]) {
92
+ console.warn(`Found state without state_ref in test ${criteriaRef}`);
93
+ }
94
+ else {
95
+ const stateRef = state["@_state_ref"];
96
+ const foundStateReference = searchTree(parsed.oval_definitions[0].states, (oNode) => oNode["@_id"] === stateRef, false)[0];
97
+ if (foundStateReference) {
98
+ foundStates.push(foundStateReference);
99
+ }
100
+ else {
101
+ console.warn(`Could not find state ${stateRef} for test ${criteriaRef}`);
102
+ }
103
+ }
104
+ });
105
+ }
106
+ }
107
+ return { ...foundCriteriaRefererence, resolvedObjects: foundObjects, resolvedStates: foundStates };
108
+ }).filter((value) => value);
15
109
  }
16
110
  }
17
111
  }
@@ -1,8 +1,12 @@
1
1
  import Profile from '../objects/profile';
2
- import { BenchmarkGroup, BenchmarkRule } from '../types/xccdf';
2
+ import { BenchmarkGroup, BenchmarkRule, RuleComplexCheck } from '../types/xccdf';
3
3
  import { OvalDefinitionValue } from '../types/oval';
4
4
  export declare type GroupContextualizedRule = BenchmarkRule & {
5
5
  group: Omit<BenchmarkGroup, 'Rule' | 'Group'>;
6
6
  };
7
7
  export declare function extractAllRules(groups: BenchmarkGroup[]): GroupContextualizedRule[];
8
- export declare function processXCCDF(xml: string, removeNewlines: boolean | undefined, useRuleId: 'group' | 'rule' | 'version' | 'cis', ovalDefinitions?: Record<string, OvalDefinitionValue>): Profile;
8
+ export declare function extractAllComplexChecks(complexCheck: RuleComplexCheck): Omit<RuleComplexCheck, 'complex-check'>[];
9
+ export declare function processXCCDF(xml: string, removeNewlines: boolean | undefined, useRuleId: 'group' | 'rule' | 'version' | 'cis', ovalDefinitions?: Record<string, OvalDefinitionValue & {
10
+ criteriaRefs?: string[];
11
+ resolvedValues?: any;
12
+ }>): Profile;
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.processXCCDF = exports.extractAllRules = void 0;
3
+ exports.processXCCDF = exports.extractAllComplexChecks = exports.extractAllRules = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const profile_1 = tslib_1.__importDefault(require("../objects/profile"));
6
6
  const xccdf_1 = require("../utilities/xccdf");
7
7
  const control_1 = tslib_1.__importDefault(require("../objects/control"));
8
8
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
9
9
  const CciNistMappingData_1 = require("../mappings/CciNistMappingData");
10
+ const pretty_1 = tslib_1.__importDefault(require("pretty"));
10
11
  function extractAllRules(groups) {
11
12
  const rules = [];
12
13
  groups.forEach((group) => {
@@ -25,6 +26,17 @@ function extractAllRules(groups) {
25
26
  return rules;
26
27
  }
27
28
  exports.extractAllRules = extractAllRules;
29
+ function extractAllComplexChecks(complexCheck) {
30
+ const complexChecks = [lodash_1.default.omit(complexCheck, 'complex-check')];
31
+ if (complexCheck['complex-check']) {
32
+ complexChecks.push(...complexCheck['complex-check'].map((subComplexCheck) => lodash_1.default.omit(subComplexCheck, 'complex-check')));
33
+ complexCheck['complex-check'].forEach((subComplexCheck) => {
34
+ complexChecks.push(...extractAllComplexChecks(subComplexCheck));
35
+ });
36
+ }
37
+ return complexChecks;
38
+ }
39
+ exports.extractAllComplexChecks = extractAllComplexChecks;
28
40
  function ensureDecodedXMLStringValue(input) {
29
41
  return lodash_1.default.get(input, '[0].#text') ? lodash_1.default.get(input, '[0].#text') : input;
30
42
  }
@@ -39,8 +51,18 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
39
51
  rules.forEach(rule => {
40
52
  var _a, _b, _c;
41
53
  let extractedDescription;
42
- if (Array.isArray(rule.description)) {
43
- extractedDescription = rule.description[0]['#text'];
54
+ if (typeof rule.description === 'object') {
55
+ if (Array.isArray(rule.description) && lodash_1.default.get(rule, "description[0]['#text']")) {
56
+ extractedDescription = rule.description[0]['#text'];
57
+ }
58
+ else {
59
+ if (typeof lodash_1.default.get(rule.description, '[0].p') === 'string') {
60
+ extractedDescription = (0, pretty_1.default)(lodash_1.default.get(rule.description, '[0].p'));
61
+ }
62
+ else {
63
+ extractedDescription = JSON.stringify(rule.description);
64
+ }
65
+ }
44
66
  }
45
67
  else {
46
68
  extractedDescription = (0, xccdf_1.convertEncodedHTMLIntoJson)(rule.description);
@@ -74,9 +96,19 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
74
96
  default:
75
97
  throw new Error('useRuleId must be one of "group", "rule", or "version"');
76
98
  }
77
- control.title = (0, xccdf_1.removeXMLSpecialCharacters)(rule['@_severity'] ? ensureDecodedXMLStringValue(rule.title) : `[[[MISSING SEVERITY FROM STIG]]] ${ensureDecodedXMLStringValue(rule.title)}`);
78
- const descriptionText = (typeof extractedDescription === 'object' && !Array.isArray(extractedDescription)) ? ((_a = extractedDescription.VulnDiscussion) === null || _a === void 0 ? void 0 : _a.split('Satisfies: ')[0]) || 'Missing Description' : '';
79
- control.desc = (0, xccdf_1.removeXMLSpecialCharacters)(descriptionText);
99
+ control.title = (0, xccdf_1.removeXMLSpecialCharacters)(rule['@_severity'] ? ensureDecodedXMLStringValue(rule.title) : `[[[MISSING SEVERITY FROM BENCHMARK]]] ${ensureDecodedXMLStringValue(rule.title)}`);
100
+ if (typeof extractedDescription === 'object' && !Array.isArray(extractedDescription)) {
101
+ control.desc = ((_a = extractedDescription.VulnDiscussion) === null || _a === void 0 ? void 0 : _a.split('Satisfies: ')[0]) || '';
102
+ }
103
+ else if (typeof extractedDescription === 'object') {
104
+ control.desc = JSON.stringify(extractedDescription);
105
+ }
106
+ else if (typeof extractedDescription === 'string') {
107
+ control.desc = extractedDescription || '';
108
+ }
109
+ else {
110
+ console.warn(`Invalid value for extracted description: ${extractedDescription}`);
111
+ }
80
112
  control.impact = (0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'medium', rule.group['@_id']);
81
113
  if (!control.descs || Array.isArray(control.descs)) {
82
114
  control.descs = {};
@@ -90,6 +122,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
90
122
  let referenceID = null;
91
123
  for (const checkContent of rule.check) {
92
124
  if ('check-content-ref' in checkContent && checkContent['@_system'].includes('oval')) {
125
+ console.log(`Found OVAL reference: ${checkContent['@_system']}`);
93
126
  for (const checkContentRef of checkContent['check-content-ref']) {
94
127
  if (checkContentRef['@_name']) {
95
128
  referenceID = checkContentRef['@_name'];
@@ -105,6 +138,54 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
105
138
  }
106
139
  }
107
140
  }
141
+ // Very CIS specific
142
+ else if (rule['complex-check']) {
143
+ let checkTexts = [];
144
+ for (const complexChecks of rule['complex-check']) {
145
+ const allComplexChecks = extractAllComplexChecks(complexChecks);
146
+ if (control.id === '1.1.1.5') {
147
+ console.log(allComplexChecks);
148
+ }
149
+ allComplexChecks.forEach((complexCheck) => {
150
+ if (complexCheck.check) {
151
+ complexCheck.check.forEach((check) => {
152
+ var _a;
153
+ if ((_a = check['@_system']) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes('oval')) {
154
+ const ovalReference = check['check-content-ref'][0]['@_name'];
155
+ if (!ovalDefinitions) {
156
+ console.warn(`Missing OVAL definitions! Unable to process OVAL reference: ${ovalReference}`);
157
+ }
158
+ else if (ovalReference && ovalReference in ovalDefinitions) {
159
+ ovalDefinitions[ovalReference].resolvedValues.forEach((resolvedValue) => {
160
+ const comment = resolvedValue['@_comment'];
161
+ if (comment) {
162
+ checkTexts.push(comment + '\n');
163
+ }
164
+ resolvedValue.resolvedObjects.forEach((resolvedObject) => {
165
+ // Try to find the associated state for a resolved object
166
+ const resolvedId = resolvedObject['@_id'].split(':')[resolvedValue['@_id'].split(':').length - 1];
167
+ if (resolvedId) {
168
+ const relatedResolvedState = resolvedValue.resolvedStates.find((resolvedState) => resolvedState['@_id'].toLowerCase().includes(resolvedId.toLowerCase()));
169
+ if (relatedResolvedState) {
170
+ lodash_1.default.set(resolvedObject, 'expectedState', lodash_1.default.pickBy(relatedResolvedState, (value, key) => !key.startsWith('@_')));
171
+ }
172
+ }
173
+ checkTexts.push(JSON.stringify(lodash_1.default.pickBy(resolvedObject, (value, key) => !key.startsWith('@_')), null, 2));
174
+ });
175
+ });
176
+ }
177
+ }
178
+ else {
179
+ console.warn(`Found external reference to unknown system: ${check['@_system']}, only OVAL is supported`);
180
+ }
181
+ });
182
+ }
183
+ });
184
+ }
185
+ if (checkTexts.length >= 1) {
186
+ control.descs.check = checkTexts.join('\n');
187
+ }
188
+ }
108
189
  if (lodash_1.default.get(rule.fixtext, '[0]["#text"]')) {
109
190
  control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)(rule.fixtext[0]['#text']);
110
191
  }
@@ -113,7 +194,17 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
113
194
  }
114
195
  else if (typeof rule.fixtext === 'object') {
115
196
  if (Array.isArray(rule.fixtext)) {
116
- control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)(JSON.stringify(rule.fixtext));
197
+ control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)((0, pretty_1.default)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext.map((fixtext) => {
198
+ if (fixtext.div) {
199
+ return fixtext.div;
200
+ }
201
+ else {
202
+ return fixtext;
203
+ }
204
+ }))));
205
+ }
206
+ else {
207
+ control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)((0, pretty_1.default)((0, xccdf_1.convertJsonIntoXML)(rule.fixtext)));
117
208
  }
118
209
  }
119
210
  else if (typeof rule.fixtext === 'undefined') {
@@ -124,7 +215,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
124
215
  else {
125
216
  control.descs.fix = 'Missing fix text';
126
217
  }
127
- control.tags.severity = (0, xccdf_1.impactNumberToSeverityString)((0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'critical', control.id || 'Unknown'));
218
+ control.tags.severity = (0, xccdf_1.impactNumberToSeverityString)((0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'medium', control.id || 'Unknown'));
128
219
  control.tags.gid = rule.group['@_id'],
129
220
  control.tags.rid = rule['@_id'];
130
221
  control.tags.stig_id = rule['version'];
@@ -41,6 +41,7 @@ function getExistingDescribeFromControl(control) {
41
41
  let inQuoteBlock = false;
42
42
  let inMetadataValueOverride = false;
43
43
  let indentedMetadataOverride = false;
44
+ let inDescribeBlock = false;
44
45
  let mostSpacesSeen = 0;
45
46
  control.code.split('\n').forEach((line) => {
46
47
  const wordSplit = line.trim().split(' ');
@@ -52,11 +53,16 @@ function getExistingDescribeFromControl(control) {
52
53
  mostSpacesSeen = spaces;
53
54
  indentedMetadataOverride = false;
54
55
  }
55
- if (!inQuoteBlock && !inMetadataValueOverride && !indentedMetadataOverride) {
56
+ if (control.id === 'SV-204392') {
57
+ console.log(line);
58
+ console.log(inDescribeBlock);
59
+ }
60
+ if ((!inQuoteBlock && !inMetadataValueOverride && !indentedMetadataOverride) || inDescribeBlock) {
56
61
  // Get the number of spaces at the beggining of the current line
57
62
  if (spaces >= 2) {
58
63
  const firstWord = wordSplit[0];
59
- if (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) === -1 || (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) !== -1 && spaces > 2)) {
64
+ if (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) === -1 || (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) !== -1 && spaces > 2) || inDescribeBlock) {
65
+ inDescribeBlock = true;
60
66
  existingDescribeBlock += line + '\n';
61
67
  }
62
68
  }
@@ -65,7 +71,7 @@ function getExistingDescribeFromControl(control) {
65
71
  const charSplit = word.split('');
66
72
  charSplit.forEach((char, index) => {
67
73
  if (char === '"' && charSplit[index - 1] !== '\\') {
68
- if (!currentQuoteEscape) {
74
+ if (!currentQuoteEscape || !inQuoteBlock) {
69
75
  currentQuoteEscape = '"';
70
76
  }
71
77
  if (currentQuoteEscape === '"') {
@@ -73,7 +79,7 @@ function getExistingDescribeFromControl(control) {
73
79
  }
74
80
  }
75
81
  else if (char === "'" && charSplit[index - 1] !== '\\') {
76
- if (!currentQuoteEscape) {
82
+ if (!currentQuoteEscape || !inQuoteBlock) {
77
83
  currentQuoteEscape = "'";
78
84
  }
79
85
  if (currentQuoteEscape === "'") {
@@ -1,5 +1,6 @@
1
1
  import { DecodedDescription } from '../types/xccdf';
2
2
  export declare function convertEncodedXmlIntoJson(encodedXml: string): any;
3
+ export declare function convertJsonIntoXML(data: any): string;
3
4
  export declare function removeXMLSpecialCharacters(str: string): string;
4
5
  export declare function severityStringToImpact(string: string, id: string): number;
5
6
  export declare function impactNumberToSeverityString(impact: number): string;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.convertEncodedHTMLIntoJson = exports.impactNumberToSeverityString = exports.severityStringToImpact = exports.removeXMLSpecialCharacters = exports.convertEncodedXmlIntoJson = void 0;
3
+ exports.convertEncodedHTMLIntoJson = exports.impactNumberToSeverityString = exports.severityStringToImpact = exports.removeXMLSpecialCharacters = exports.convertJsonIntoXML = exports.convertEncodedXmlIntoJson = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const fast_xml_parser_1 = tslib_1.__importDefault(require("fast-xml-parser"));
6
+ const jstoxml_1 = require("jstoxml");
6
7
  const htmlparser = tslib_1.__importStar(require("htmlparser2"));
7
8
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
8
9
  const he_1 = tslib_1.__importDefault(require("he"));
@@ -11,10 +12,15 @@ function convertEncodedXmlIntoJson(encodedXml) {
11
12
  ignoreAttributes: false,
12
13
  ignoreNameSpace: true,
13
14
  attributeNamePrefix: '@_',
15
+ stopNodes: ['div', 'p'],
14
16
  arrayMode: true
15
17
  });
16
18
  }
17
19
  exports.convertEncodedXmlIntoJson = convertEncodedXmlIntoJson;
20
+ function convertJsonIntoXML(data) {
21
+ return (0, jstoxml_1.toXML)(data);
22
+ }
23
+ exports.convertJsonIntoXML = convertJsonIntoXML;
18
24
  function removeXMLSpecialCharacters(str) {
19
25
  return he_1.default.decode(str);
20
26
  }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mitre/inspec-objects",
3
- "version": "0.0.25",
3
+ "version": "0.0.28",
4
4
  "description": "Typescript objects for normalizing between InSpec profiles and XCCDF benchmarks",
5
5
  "main": "lib/index.js",
6
6
  "publishConfig": {
@@ -25,8 +25,10 @@
25
25
  "@types/flat": "^5.0.2",
26
26
  "@types/he": "^1.1.2",
27
27
  "@types/json-diff": "^0.7.0",
28
+ "@types/jstoxml": "^2.0.2",
28
29
  "@types/lodash": "^4.14.178",
29
30
  "@types/mustache": "^4.2.0",
31
+ "@types/pretty": "^2.0.1",
30
32
  "fast-xml-parser": "^3.1.19",
31
33
  "flat": "^5.0.2",
32
34
  "he": "^1.2.0",
@@ -34,8 +36,10 @@
34
36
  "inspecjs": "^2.6.6",
35
37
  "jest": "^28.1.1",
36
38
  "json-diff": "^0.9.0",
39
+ "jstoxml": "^3.2.3",
37
40
  "lodash": "^4.17.21",
38
41
  "mustache": "^4.2.0",
42
+ "pretty": "^2.0.0",
39
43
  "ts-jest": "^28.0.4",
40
44
  "typescript": "^4.5.5",
41
45
  "winston": "^3.8.1",