@mitre/inspec-objects 0.0.13 → 0.0.14
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/objects/control.d.ts
CHANGED
package/lib/objects/control.js
CHANGED
|
@@ -31,7 +31,7 @@ class Control {
|
|
|
31
31
|
const flattened = (0, flat_1.flatten)(this);
|
|
32
32
|
Object.entries(flattened).forEach(([key, value]) => {
|
|
33
33
|
if (typeof value === 'string') {
|
|
34
|
-
lodash_1.default.set(flattened, key,
|
|
34
|
+
lodash_1.default.set(flattened, key, value);
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
return new Control((0, flat_1.unflatten)(flattened));
|
|
@@ -40,13 +40,13 @@ class Control {
|
|
|
40
40
|
let result = "# encoding: UTF-8\n\n";
|
|
41
41
|
result += `control "${this.id}" do\n`;
|
|
42
42
|
if (this.title) {
|
|
43
|
-
result += ` title "${(0, global_1.
|
|
43
|
+
result += ` title "${(0, global_1.escapeDoubleQuotes)((0, global_1.removeNewlinePlaceholders)(this.title))}"\n`;
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
46
|
console.error(`${this.id} does not have a title`);
|
|
47
47
|
}
|
|
48
48
|
if (this.desc) {
|
|
49
|
-
result += ` desc "${(0, global_1.
|
|
49
|
+
result += ` desc "${(0, global_1.escapeDoubleQuotes)((0, global_1.removeNewlinePlaceholders)(this.desc))}"\n`;
|
|
50
50
|
}
|
|
51
51
|
else {
|
|
52
52
|
console.error(`${this.id} does not have a desc`);
|
|
@@ -54,7 +54,7 @@ class Control {
|
|
|
54
54
|
if (this.descs) {
|
|
55
55
|
Object.entries(this.descs).forEach(([key, desc]) => {
|
|
56
56
|
if (desc) {
|
|
57
|
-
result += ` desc "${key}", "${(0, global_1.
|
|
57
|
+
result += ` desc "${key}", "${(0, global_1.escapeDoubleQuotes)((0, global_1.removeNewlinePlaceholders)(desc))}"\n`;
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
60
|
console.error(`${this.id} does not have a desc for the value ${key}`);
|
|
@@ -96,10 +96,14 @@ class Control {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
else if (typeof value === "string") {
|
|
99
|
-
result += ` tag ${tag}: "${(0, global_1.
|
|
99
|
+
result += ` tag ${tag}: "${(0, global_1.escapeDoubleQuotes)((0, global_1.removeNewlinePlaceholders)(value))}"\n`;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
+
if (this.describe) {
|
|
104
|
+
result += '\n';
|
|
105
|
+
result += this.describe;
|
|
106
|
+
}
|
|
103
107
|
result += "end";
|
|
104
108
|
return result;
|
|
105
109
|
}
|
package/lib/parsers/xccdf.js
CHANGED
|
@@ -37,7 +37,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
37
37
|
summary: parsedXML.Benchmark[0].description[0]['#text']
|
|
38
38
|
});
|
|
39
39
|
rules.forEach(rule => {
|
|
40
|
-
var _a, _b, _c
|
|
40
|
+
var _a, _b, _c;
|
|
41
41
|
let extractedDescription;
|
|
42
42
|
if (Array.isArray(rule.description)) {
|
|
43
43
|
extractedDescription = rule.description[0]['#text'];
|
|
@@ -68,29 +68,15 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
68
68
|
default:
|
|
69
69
|
throw new Error('useRuleId must be one of "group", "rule", or "version"');
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const desc = (0, xccdf_1.removeXMLSpecialCharacters)(typeof extractedDescription === 'string' ? extractedDescription : ((_a = extractedDescription.VulnDiscussion) === null || _a === void 0 ? void 0 : _a.split('Satisfies: ')[0]) || 'Missing Description');
|
|
75
|
-
control.desc = desc === null || desc === void 0 ? void 0 : desc.trim().replace(/\n/g, '{{{{newlineHERE}}}}');
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
control.title = (0, xccdf_1.removeXMLSpecialCharacters)(rule['@_severity'] ? ensureDecodedXMLStringValue(rule.title) : `[[[MISSING SEVERITY FROM STIG]]] ${ensureDecodedXMLStringValue(rule.title)}`);
|
|
79
|
-
control.desc = (0, xccdf_1.removeXMLSpecialCharacters)(typeof extractedDescription === 'string' ? extractedDescription : ((_b = extractedDescription.VulnDiscussion) === null || _b === void 0 ? void 0 : _b.split('Satisfies: ')[0]) || 'Missing Description');
|
|
80
|
-
}
|
|
81
|
-
control.impact = (0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'critical', rule.group['@_id']);
|
|
71
|
+
control.title = (0, xccdf_1.removeXMLSpecialCharacters)(rule['@_severity'] ? ensureDecodedXMLStringValue(rule.title) : `[[[MISSING SEVERITY FROM STIG]]] ${ensureDecodedXMLStringValue(rule.title)}`);
|
|
72
|
+
control.desc = (0, xccdf_1.removeXMLSpecialCharacters)(typeof extractedDescription === 'string' ? extractedDescription : ((_a = extractedDescription.VulnDiscussion) === null || _a === void 0 ? void 0 : _a.split('Satisfies: ')[0]) || 'Missing Description');
|
|
73
|
+
control.impact = (0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'medium', rule.group['@_id']);
|
|
82
74
|
if (!control.descs || Array.isArray(control.descs)) {
|
|
83
75
|
control.descs = {};
|
|
84
76
|
}
|
|
85
77
|
if (rule.check) {
|
|
86
78
|
if (rule.check.some((ruleValue) => 'check-content' in ruleValue)) {
|
|
87
|
-
|
|
88
|
-
const check = (0, xccdf_1.removeXMLSpecialCharacters)(rule.check ? rule.check[0]['check-content'] : 'Missing description');
|
|
89
|
-
control.descs.check = check.replace(/\n/g, '{{{{newlineHERE}}}}');
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
control.descs.check = (0, xccdf_1.removeXMLSpecialCharacters)(rule.check ? rule.check[0]['check-content'] : 'Missing description');
|
|
93
|
-
}
|
|
79
|
+
control.descs.check = (0, xccdf_1.removeXMLSpecialCharacters)(rule.check ? rule.check[0]['check-content'] : 'Missing description');
|
|
94
80
|
}
|
|
95
81
|
else if (rule.check.some((ruleValue) => 'check-content-ref' in ruleValue) && ovalDefinitions) {
|
|
96
82
|
let referenceID = null;
|
|
@@ -104,26 +90,14 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
104
90
|
}
|
|
105
91
|
}
|
|
106
92
|
if (referenceID && referenceID in ovalDefinitions) {
|
|
107
|
-
|
|
108
|
-
const check = (0, xccdf_1.removeXMLSpecialCharacters)(ovalDefinitions[referenceID].metadata[0].title);
|
|
109
|
-
control.descs.check = check.replace(/\n/g, '{{{{newlineHERE}}}}');
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
control.descs.check = (0, xccdf_1.removeXMLSpecialCharacters)(ovalDefinitions[referenceID].metadata[0].title);
|
|
113
|
-
}
|
|
93
|
+
control.descs.check = (0, xccdf_1.removeXMLSpecialCharacters)(ovalDefinitions[referenceID].metadata[0].title);
|
|
114
94
|
}
|
|
115
95
|
else if (referenceID) {
|
|
116
96
|
console.warn(`Could not find OVAL definition for ${referenceID}`);
|
|
117
97
|
}
|
|
118
98
|
}
|
|
119
99
|
}
|
|
120
|
-
|
|
121
|
-
const fix = (0, xccdf_1.removeXMLSpecialCharacters)(rule.fixtext ? rule.fixtext[0]['#text'] : (rule.fix ? rule.fix[0]['#text'] || 'Missing fix text' : 'Missing fix text'));
|
|
122
|
-
control.descs.fix = fix.replace(/\n/g, '{{{{newlineHERE}}}}');
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)(rule.fixtext ? rule.fixtext[0]['#text'] : (rule.fix ? rule.fix[0]['#text'] || 'Missing fix text' : 'Missing fix text'));
|
|
126
|
-
}
|
|
100
|
+
control.descs.fix = (0, xccdf_1.removeXMLSpecialCharacters)(rule.fixtext ? rule.fixtext[0]['#text'] : (rule.fix ? rule.fix[0]['#text'] || 'Missing fix text' : 'Missing fix text'));
|
|
127
101
|
control.tags.severity = (0, xccdf_1.impactNumberToSeverityString)((0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'critical', control.id || 'Unknown'));
|
|
128
102
|
control.tags.gid = rule.group['@_id'],
|
|
129
103
|
control.tags.rid = rule['@_id'];
|
|
@@ -141,7 +115,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
141
115
|
control.tags.rationale = rule['rationale'][0]['#text'];
|
|
142
116
|
}
|
|
143
117
|
if (typeof extractedDescription === 'object') {
|
|
144
|
-
control.tags.satisfies = ((
|
|
118
|
+
control.tags.satisfies = ((_b = extractedDescription.VulnDiscussion) === null || _b === void 0 ? void 0 : _b.includes('Satisfies: ')) && extractedDescription.VulnDiscussion.split('Satisfies: ').length >= 1 ? extractedDescription.VulnDiscussion.split('Satisfies: ')[1].split(',').map(satisfaction => satisfaction.trim()) : undefined;
|
|
145
119
|
control.tags.false_negatives = extractedDescription.FalseNegatives || undefined;
|
|
146
120
|
control.tags.false_positives = extractedDescription.FalsePositives || undefined;
|
|
147
121
|
control.tags.documentable = typeof extractedDescription.Documentable === 'boolean' ? extractedDescription.Documentable : undefined;
|
|
@@ -194,7 +168,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
194
168
|
}
|
|
195
169
|
});
|
|
196
170
|
}
|
|
197
|
-
(
|
|
171
|
+
(_c = rule.reference) === null || _c === void 0 ? void 0 : _c.forEach((reference) => {
|
|
198
172
|
var _a, _b, _c, _d;
|
|
199
173
|
if (lodash_1.default.get(reference, '@_href') === '') {
|
|
200
174
|
(_a = control.refs) === null || _a === void 0 ? void 0 : _a.push(lodash_1.default.get(reference, '#text'));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import winston from "winston";
|
|
2
|
-
export declare function createWinstonLogger(
|
|
2
|
+
export declare function createWinstonLogger(level?: string): winston.Logger;
|
package/lib/utilities/logging.js
CHANGED
|
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createWinstonLogger = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const winston_1 = tslib_1.__importDefault(require("winston"));
|
|
6
|
-
function createWinstonLogger(
|
|
6
|
+
function createWinstonLogger(level = 'debug') {
|
|
7
7
|
return winston_1.default.createLogger({
|
|
8
8
|
transports: [new winston_1.default.transports.Console()],
|
|
9
9
|
level: level,
|
|
10
10
|
format: winston_1.default.format.combine(winston_1.default.format.timestamp({
|
|
11
11
|
format: 'MMM-DD-YYYY HH:mm:ss Z',
|
|
12
|
-
}), winston_1.default.format.printf(info => `[${[info.timestamp]}] ${
|
|
12
|
+
}), winston_1.default.format.printf(info => `[${[info.timestamp]}] ${info.message}`)),
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
exports.createWinstonLogger = createWinstonLogger;
|
package/lib/utilities/update.js
CHANGED
|
@@ -9,6 +9,7 @@ const profile_1 = tslib_1.__importDefault(require("../objects/profile"));
|
|
|
9
9
|
const xccdf_1 = require("../parsers/xccdf");
|
|
10
10
|
const diff_1 = require("./diff");
|
|
11
11
|
const diffMarkdown_1 = require("./diffMarkdown");
|
|
12
|
+
const knownInSpecKeywords = ['title', 'desc', 'impact', 'ref', 'tag', "\""];
|
|
12
13
|
function projectValuesOntoExistingObj(dst, src, currentPath = '') {
|
|
13
14
|
for (const updatedValue in src) {
|
|
14
15
|
const existingValue = lodash_1.default.get(dst, updatedValue);
|
|
@@ -31,8 +32,44 @@ function projectValuesOntoExistingObj(dst, src, currentPath = '') {
|
|
|
31
32
|
}
|
|
32
33
|
return dst;
|
|
33
34
|
}
|
|
35
|
+
// This is the most likely thing to break if you are getting code formatting issues.
|
|
36
|
+
// Extract the existing describe blocks (what is actually run by inspec for validation)
|
|
37
|
+
function getExistingDescribeFromControl(control) {
|
|
38
|
+
if (control.code) {
|
|
39
|
+
let existingDescribeBlock = '';
|
|
40
|
+
let inQuoteBlock = false;
|
|
41
|
+
control.code.split('\n').forEach((line) => {
|
|
42
|
+
const wordSplit = line.trim().split(' ');
|
|
43
|
+
wordSplit.forEach((word, index) => {
|
|
44
|
+
const charSplit = word.split('');
|
|
45
|
+
charSplit.forEach((char, index) => {
|
|
46
|
+
if (char === '"' && charSplit[index - 1] !== '\\') {
|
|
47
|
+
inQuoteBlock = !inQuoteBlock;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
if (!inQuoteBlock) {
|
|
52
|
+
// Get the number of spaces at the beggining of the current line
|
|
53
|
+
const spaces = line.substring(0, line.indexOf(wordSplit[0])).length;
|
|
54
|
+
if (spaces >= 2) {
|
|
55
|
+
const firstWord = wordSplit[0];
|
|
56
|
+
if (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) === -1 || (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) !== -1 && spaces > 2)) {
|
|
57
|
+
existingDescribeBlock += line + '\n';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return existingDescribeBlock;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
34
68
|
function updateControl(from, update) {
|
|
35
|
-
|
|
69
|
+
const existingDescribeBlock = getExistingDescribeFromControl(from);
|
|
70
|
+
const projectedControl = projectValuesOntoExistingObj(from, update);
|
|
71
|
+
projectedControl.describe = existingDescribeBlock;
|
|
72
|
+
return projectedControl;
|
|
36
73
|
}
|
|
37
74
|
exports.updateControl = updateControl;
|
|
38
75
|
function updateProfile(from, using) {
|
|
@@ -73,7 +110,6 @@ function updateProfile(from, using) {
|
|
|
73
110
|
}
|
|
74
111
|
exports.updateProfile = updateProfile;
|
|
75
112
|
function updateProfileUsingXCCDF(from, using, id, logger, ovalDefinitions) {
|
|
76
|
-
console.log(from.controls[0].desc);
|
|
77
113
|
// Parse the XCCDF benchmark and convert it into a Profile
|
|
78
114
|
logger.debug('Loading XCCDF File');
|
|
79
115
|
const xccdfProfile = (0, xccdf_1.processXCCDF)(using, false, id);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mitre/inspec-objects",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@mitre/inspec-objects",
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.14",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@types/flat": "^5.0.2",
|