@mitre/inspec-objects 0.0.30 → 0.0.32
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/.eslintignore +2 -0
- package/.eslintrc +41 -0
- package/.github/workflows/e2e-test.yml +6 -19
- package/.github/workflows/linter.yml +27 -0
- package/.github/workflows/push-to-gpr.yml +36 -0
- package/.github/workflows/push-to-npm.yml +34 -0
- package/README.md +48 -0
- package/images/Delta_Process.jpg +0 -0
- package/images/ts-inspec-objects.jpg +0 -0
- package/lib/index.d.ts +7 -7
- package/lib/objects/control.d.ts +2 -2
- package/lib/objects/control.js +40 -21
- package/lib/objects/profile.d.ts +6 -6
- package/lib/objects/profile.js +1 -1
- package/lib/parsers/json.d.ts +3 -3
- package/lib/parsers/json.js +18 -14
- package/lib/parsers/oval.js +20 -19
- package/lib/parsers/xccdf.d.ts +1 -1
- package/lib/parsers/xccdf.js +9 -5
- package/lib/utilities/diff.d.ts +6 -6
- package/lib/utilities/diff.js +52 -30
- package/lib/utilities/diffMarkdown.d.ts +3 -3
- package/lib/utilities/diffMarkdown.js +17 -21
- package/lib/utilities/global.d.ts +2 -4
- package/lib/utilities/global.js +29 -13
- package/lib/utilities/logging.d.ts +1 -1
- package/lib/utilities/update.d.ts +4 -3
- package/lib/utilities/update.js +57 -54
- package/lib/utilities/xccdf.js +0 -1
- package/package.json +7 -4
- package/tsconfig.json +20 -21
- package/lib/utilities/CciNistMappingData.d.ts +0 -5100
- package/lib/utilities/CciNistMappingData.js +0 -5103
- package/package-lock.json +0 -8881
package/lib/parsers/xccdf.js
CHANGED
|
@@ -40,7 +40,8 @@ exports.extractAllComplexChecks = extractAllComplexChecks;
|
|
|
40
40
|
function ensureDecodedXMLStringValue(input) {
|
|
41
41
|
return lodash_1.default.get(input, '[0].#text') ? lodash_1.default.get(input, '[0].#text') : input;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
// Moving the newline removal to diff library rather than processXCCDF level
|
|
44
|
+
function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
|
|
44
45
|
const parsedXML = (0, xccdf_1.convertEncodedXmlIntoJson)(xml);
|
|
45
46
|
const rules = extractAllRules(parsedXML.Benchmark[0].Group);
|
|
46
47
|
const profile = new profile_1.default({
|
|
@@ -90,13 +91,15 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
90
91
|
control.id = rule.version;
|
|
91
92
|
break;
|
|
92
93
|
case 'cis':
|
|
94
|
+
// eslint-disable-next-line no-case-declarations
|
|
93
95
|
const controlIdRegex = /\d(\d?)(\d?)(\d?)(.\d(\d?)(\d?)(\d?))?(.\d(\d?)(\d?)(\d?))?(.\d(\d?)(\d?)(\d?))?(.\d(\d?)(\d?)(\d?))?/g;
|
|
96
|
+
// eslint-disable-next-line no-case-declarations
|
|
94
97
|
const controlIdMatch = controlIdRegex.exec(rule['@_id']);
|
|
95
98
|
if (controlIdMatch) {
|
|
96
99
|
control.id = controlIdMatch[0];
|
|
97
100
|
}
|
|
98
101
|
else {
|
|
99
|
-
throw new Error(`Could not parse control ID from rule ID: ${rule['@_id']}. Expecting format: 'xccdf_org.cisecurity.benchmarks_rule_1.1.11_Rule_title_summary`);
|
|
102
|
+
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`);
|
|
100
103
|
}
|
|
101
104
|
break;
|
|
102
105
|
default:
|
|
@@ -146,7 +149,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
146
149
|
}
|
|
147
150
|
// Very CIS specific
|
|
148
151
|
else if (rule['complex-check']) {
|
|
149
|
-
|
|
152
|
+
const checkTexts = [];
|
|
150
153
|
for (const complexChecks of rule['complex-check']) {
|
|
151
154
|
const allComplexChecks = extractAllComplexChecks(complexChecks);
|
|
152
155
|
if (control.id === '1.1.1.5') {
|
|
@@ -225,7 +228,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
225
228
|
control.tags.gid = rule.group['@_id'],
|
|
226
229
|
control.tags.rid = rule['@_id'];
|
|
227
230
|
control.tags.stig_id = rule['version'];
|
|
228
|
-
if (typeof rule.group.title ===
|
|
231
|
+
if (typeof rule.group.title === 'string') {
|
|
229
232
|
control.tags.gtitle = (0, xccdf_1.removeXMLSpecialCharacters)(rule.group.title);
|
|
230
233
|
}
|
|
231
234
|
else {
|
|
@@ -319,6 +322,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
319
322
|
if (typeof referenceText === 'string' && referenceText.indexOf('§') !== -1) {
|
|
320
323
|
const referenceParts = referenceText.split('§');
|
|
321
324
|
if (referenceParts.length == 2) {
|
|
325
|
+
// eslint-disable-next-line prefer-const
|
|
322
326
|
let [identifierType, identifier] = referenceText.split('§');
|
|
323
327
|
identifierType = identifierType.toLowerCase();
|
|
324
328
|
if (!(identifierType in control.tags)) {
|
|
@@ -332,7 +336,7 @@ function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
|
|
|
332
336
|
}
|
|
333
337
|
}
|
|
334
338
|
else {
|
|
335
|
-
console.warn(
|
|
339
|
+
console.warn('Reference parts of invalid length:');
|
|
336
340
|
console.log(referenceParts);
|
|
337
341
|
}
|
|
338
342
|
}
|
package/lib/utilities/diff.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import Profile from
|
|
2
|
-
import { ProfileDiff } from
|
|
3
|
-
import winston from
|
|
1
|
+
import Profile from '../objects/profile';
|
|
2
|
+
import { ProfileDiff } from '../types/diff';
|
|
3
|
+
import winston from 'winston';
|
|
4
4
|
export declare function removeNewlines(control?: Record<string, unknown>): Record<string, unknown>;
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function ignoreFormattingDiff(diffData: Record<string, unknown>): Record<string, unknown>;
|
|
6
6
|
export declare function diffProfile(fromProfile: Profile, toProfile: Profile, logger: winston.Logger): {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
ignoreFormattingDiff: ProfileDiff;
|
|
8
|
+
rawDiff: Record<string, unknown>;
|
|
9
9
|
};
|
package/lib/utilities/diff.js
CHANGED
|
@@ -1,49 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.diffProfile = exports.
|
|
3
|
+
exports.diffProfile = exports.ignoreFormattingDiff = exports.removeNewlines = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const json_diff_1 = require("json-diff");
|
|
6
6
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
7
7
|
const update_1 = require("./update");
|
|
8
|
+
const global_1 = require("./global");
|
|
8
9
|
function removeNewlines(control) {
|
|
9
10
|
if (!control) {
|
|
10
11
|
return {};
|
|
11
12
|
}
|
|
12
13
|
return lodash_1.default.mapValues(control, (value) => {
|
|
13
|
-
if (typeof value ===
|
|
14
|
-
return value.replace(/\n/g,
|
|
14
|
+
if (typeof value === 'string') {
|
|
15
|
+
return value.replace(/\n/g, '{{{{newlineHERE}}}}').trim();
|
|
15
16
|
}
|
|
16
|
-
else if (typeof value ===
|
|
17
|
+
else if (typeof value === 'object' && value !== null) {
|
|
17
18
|
return removeNewlines(value);
|
|
18
19
|
}
|
|
19
20
|
return value;
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
exports.removeNewlines = removeNewlines;
|
|
23
|
-
|
|
24
|
+
// Goal is to use a linter for the formatting and compare characters without whitespaces here
|
|
25
|
+
function ignoreFormattingDiff(diffData) {
|
|
24
26
|
return lodash_1.default.transform(diffData, (result, diffValue, key) => {
|
|
25
|
-
if (lodash_1.default.has(diffValue,
|
|
27
|
+
if (lodash_1.default.has(diffValue, '__new')) {
|
|
26
28
|
// Remove any trailing space
|
|
27
|
-
if (typeof lodash_1.default.get(diffValue,
|
|
28
|
-
typeof lodash_1.default.get(diffValue,
|
|
29
|
-
if (lodash_1.default.get(diffValue,
|
|
30
|
-
lodash_1.default.get(diffValue,
|
|
31
|
-
lodash_1.default.set(result, key, lodash_1.default.get(diffValue,
|
|
29
|
+
if (typeof lodash_1.default.get(diffValue, '__new') === 'string' &&
|
|
30
|
+
typeof lodash_1.default.get(diffValue, '__old') === 'string') {
|
|
31
|
+
if ((0, global_1.removeWhitespace)(lodash_1.default.get(diffValue, '__new')) !==
|
|
32
|
+
(0, global_1.removeWhitespace)(lodash_1.default.get(diffValue, '__old'))) {
|
|
33
|
+
lodash_1.default.set(result, key, lodash_1.default.get(diffValue, '__new'));
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
else {
|
|
35
|
-
result[key] = lodash_1.default.get(diffValue,
|
|
37
|
+
result[key] = lodash_1.default.get(diffValue, '__new');
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
else if (Array.isArray(diffValue)) {
|
|
39
41
|
result[key] = diffValue
|
|
40
|
-
.map((value) => value[0] ===
|
|
42
|
+
.map((value) => value[0] === '+' && value[1])
|
|
41
43
|
.filter((value) => value);
|
|
42
44
|
}
|
|
43
|
-
else if (typeof diffValue ===
|
|
44
|
-
result[key] =
|
|
45
|
+
else if (typeof diffValue === 'object') {
|
|
46
|
+
result[key] = ignoreFormattingDiff(diffValue);
|
|
45
47
|
}
|
|
46
|
-
else if (key.endsWith(
|
|
48
|
+
else if (key.endsWith('__deleted')) {
|
|
47
49
|
return undefined;
|
|
48
50
|
}
|
|
49
51
|
else {
|
|
@@ -51,19 +53,22 @@ function simplifyDiff(diffData) {
|
|
|
51
53
|
}
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
|
-
exports.
|
|
56
|
+
exports.ignoreFormattingDiff = ignoreFormattingDiff;
|
|
55
57
|
function diffProfile(fromProfile, toProfile, logger) {
|
|
58
|
+
var _a;
|
|
56
59
|
const profileDiff = {
|
|
57
60
|
addedControlIDs: [],
|
|
58
61
|
removedControlIDs: [],
|
|
59
|
-
|
|
62
|
+
renamedControlIDs: {},
|
|
63
|
+
changedControlIDs: [],
|
|
60
64
|
addedControls: {},
|
|
61
65
|
changedControls: {},
|
|
62
66
|
};
|
|
63
67
|
const originalDiff = {
|
|
64
68
|
addedControlIDs: [],
|
|
65
69
|
removedControlIDs: [],
|
|
66
|
-
|
|
70
|
+
renamedControlIDs: {},
|
|
71
|
+
changedControlIDs: [],
|
|
67
72
|
addedControls: {},
|
|
68
73
|
changedControls: {},
|
|
69
74
|
};
|
|
@@ -72,23 +77,30 @@ function diffProfile(fromProfile, toProfile, logger) {
|
|
|
72
77
|
.sort();
|
|
73
78
|
const toControlIDs = toProfile.controls.map((control) => control.id).sort();
|
|
74
79
|
// Find new controls
|
|
75
|
-
const controlIDDiff = (0, json_diff_1.diff)(fromControlIDs, toControlIDs);
|
|
80
|
+
const controlIDDiff = (_a = (0, json_diff_1.diff)(fromControlIDs, toControlIDs)) === null || _a === void 0 ? void 0 : _a.filter((item) => !(item.length === 1 && item[0] === ' '));
|
|
76
81
|
// Contains the new IDs
|
|
77
82
|
const changedControlIds = [];
|
|
83
|
+
// a diffValue has an entry for both what was subtracted ("-")
|
|
84
|
+
// and what was added ("+") -- need to handle both
|
|
78
85
|
controlIDDiff === null || controlIDDiff === void 0 ? void 0 : controlIDDiff.forEach((diffValue) => {
|
|
79
|
-
if (diffValue[0] ===
|
|
86
|
+
if (diffValue[0] === '-') {
|
|
80
87
|
const existingControl = fromProfile.controls.find((control) => control.id === diffValue[1]);
|
|
81
88
|
// Check if the control has been given a new ID
|
|
82
89
|
if (existingControl) {
|
|
83
90
|
const newControl = (0, update_1.findUpdatedControlByAllIdentifiers)(existingControl, toProfile.controls);
|
|
84
91
|
if (newControl && newControl.id !== existingControl.id) {
|
|
85
|
-
profileDiff.
|
|
86
|
-
originalDiff.
|
|
92
|
+
profileDiff.renamedControlIDs[existingControl.id] = newControl.id;
|
|
93
|
+
originalDiff.renamedControlIDs[existingControl.id] = newControl.id;
|
|
87
94
|
changedControlIds.push(newControl.id.toLowerCase());
|
|
88
|
-
const controlDiff = lodash_1.default.omit((0, json_diff_1.diff)(existingControl, newControl),
|
|
89
|
-
|
|
95
|
+
const controlDiff = lodash_1.default.omit((0, json_diff_1.diff)(existingControl, newControl), 'code__deleted');
|
|
96
|
+
// logger.info("CONTROL DIFF:" + JSON.stringify(controlDiff, null, 2))
|
|
97
|
+
const renamedControlIgnoredFormatting = ignoreFormattingDiff(controlDiff);
|
|
98
|
+
logger.info(JSON.stringify(renamedControlIgnoredFormatting));
|
|
99
|
+
profileDiff.changedControls[newControl.id] = renamedControlIgnoredFormatting;
|
|
100
|
+
profileDiff.changedControlIDs.push(newControl.id);
|
|
90
101
|
originalDiff.changedControls[newControl.id] = controlDiff;
|
|
91
|
-
|
|
102
|
+
originalDiff.changedControlIDs.push(newControl.id);
|
|
103
|
+
logger.verbose(`Control ${existingControl.id} has been updated to ${newControl.id}`);
|
|
92
104
|
}
|
|
93
105
|
else {
|
|
94
106
|
profileDiff.removedControlIDs.push(diffValue[1]);
|
|
@@ -99,11 +111,17 @@ function diffProfile(fromProfile, toProfile, logger) {
|
|
|
99
111
|
logger.error(`Unable to find existing control ${diffValue[1]}`);
|
|
100
112
|
}
|
|
101
113
|
}
|
|
102
|
-
else if (diffValue[0] ===
|
|
114
|
+
else if (diffValue[0] === '+' && !changedControlIds.includes(diffValue[1].toLowerCase()) && diffValue[1]) {
|
|
115
|
+
logger.info(JSON.stringify(diffValue));
|
|
116
|
+
logger.info(JSON.stringify(changedControlIds));
|
|
103
117
|
profileDiff.addedControlIDs.push(diffValue[1]);
|
|
104
118
|
originalDiff.addedControlIDs.push(diffValue[1]);
|
|
105
119
|
}
|
|
106
120
|
});
|
|
121
|
+
// take the list of renamed controls out of the list of added controls
|
|
122
|
+
// (a control is not "new" if it was renamed)
|
|
123
|
+
profileDiff.addedControlIDs = profileDiff.addedControlIDs.filter((item) => !Object.values(profileDiff.renamedControlIDs).includes(item));
|
|
124
|
+
originalDiff.addedControlIDs = originalDiff.addedControlIDs.filter((item) => !Object.values(originalDiff.renamedControlIDs).includes(item));
|
|
107
125
|
// Add new controls to addedControls
|
|
108
126
|
profileDiff.addedControlIDs.forEach((addedControl) => {
|
|
109
127
|
const newControl = toProfile.controls.find((control) => addedControl === control.id);
|
|
@@ -116,13 +134,17 @@ function diffProfile(fromProfile, toProfile, logger) {
|
|
|
116
134
|
for (const fromControl of fromProfile.controls) {
|
|
117
135
|
const toControl = toProfile.controls.find((control) => control.id === fromControl.id);
|
|
118
136
|
if (toControl) {
|
|
119
|
-
const controlDiff = lodash_1.default.omit((0, json_diff_1.diff)(fromControl, toControl),
|
|
137
|
+
const controlDiff = lodash_1.default.omit((0, json_diff_1.diff)(fromControl, toControl), 'code__deleted');
|
|
120
138
|
if (controlDiff) {
|
|
121
|
-
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
140
|
+
profileDiff.changedControls[toControl.id] = ignoreFormattingDiff(controlDiff);
|
|
141
|
+
profileDiff.changedControlIDs.push(toControl.id);
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
122
143
|
originalDiff.changedControls[toControl.id] = controlDiff;
|
|
144
|
+
originalDiff.changedControlIDs.push(toControl.id);
|
|
123
145
|
}
|
|
124
146
|
}
|
|
125
147
|
}
|
|
126
|
-
return {
|
|
148
|
+
return { ignoreFormattingDiff: profileDiff, rawDiff: originalDiff };
|
|
127
149
|
}
|
|
128
150
|
exports.diffProfile = diffProfile;
|
|
@@ -5,13 +5,9 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const mustache_1 = tslib_1.__importDefault(require("mustache"));
|
|
6
6
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
7
7
|
const automatticUpdateTemplate_json_1 = tslib_1.__importDefault(require("../resources/automatticUpdateTemplate.json"));
|
|
8
|
-
function getUpdatedCheckForId(id, profile) {
|
|
9
|
-
const foundControl = profile.controls.find((control) => control.id === id);
|
|
10
|
-
return lodash_1.default.get(foundControl === null || foundControl === void 0 ? void 0 : foundControl.descs, "check") || "Missing check";
|
|
11
|
-
}
|
|
12
8
|
function createDiffMarkdown(diff) {
|
|
13
9
|
const renderableDiffData = {
|
|
14
|
-
addedControls: Object.values(diff.
|
|
10
|
+
addedControls: Object.values(diff.ignoreFormattingDiff.addedControls),
|
|
15
11
|
hasRenamedControls: false,
|
|
16
12
|
renamedControls: [],
|
|
17
13
|
updatedChecks: [],
|
|
@@ -20,20 +16,20 @@ function createDiffMarkdown(diff) {
|
|
|
20
16
|
updatedTitles: [],
|
|
21
17
|
updatedDescriptions: [],
|
|
22
18
|
};
|
|
23
|
-
Object.entries(diff.
|
|
19
|
+
Object.entries(diff.ignoreFormattingDiff.renamedControlIDs).forEach(([oldId, newId]) => {
|
|
24
20
|
renderableDiffData.hasRenamedControls = true;
|
|
25
21
|
renderableDiffData.renamedControls.push({
|
|
26
22
|
oldId: oldId,
|
|
27
23
|
newId: newId,
|
|
28
24
|
});
|
|
29
25
|
});
|
|
30
|
-
Object.entries(diff.
|
|
26
|
+
Object.entries(diff.rawDiff.changedControls).forEach(([id, controlDiff]) => {
|
|
31
27
|
var _a, _b;
|
|
32
28
|
if ((_a = controlDiff.descs) === null || _a === void 0 ? void 0 : _a.check) {
|
|
33
|
-
const oldCheck = lodash_1.default.get(controlDiff.descs.check,
|
|
34
|
-
const newCheck = lodash_1.default.get(controlDiff.descs.check,
|
|
35
|
-
if (oldCheck.replace(/\n/g,
|
|
36
|
-
newCheck.replace(/\n/g,
|
|
29
|
+
const oldCheck = lodash_1.default.get(controlDiff.descs.check, '__old');
|
|
30
|
+
const newCheck = lodash_1.default.get(controlDiff.descs.check, '__new');
|
|
31
|
+
if (oldCheck.replace(/\n/g, '').replace(/\W/g, '') !==
|
|
32
|
+
newCheck.replace(/\n/g, '').replace(/\W/g, '')) {
|
|
37
33
|
renderableDiffData.updatedChecks.push({
|
|
38
34
|
id: id,
|
|
39
35
|
old: oldCheck,
|
|
@@ -42,10 +38,10 @@ function createDiffMarkdown(diff) {
|
|
|
42
38
|
}
|
|
43
39
|
}
|
|
44
40
|
if ((_b = controlDiff.descs) === null || _b === void 0 ? void 0 : _b.fix) {
|
|
45
|
-
const oldFix = lodash_1.default.get(controlDiff.descs.fix,
|
|
46
|
-
const newFix = lodash_1.default.get(controlDiff.descs.fix,
|
|
47
|
-
if (oldFix.replace(/\n/g,
|
|
48
|
-
newFix.replace(/\n/g,
|
|
41
|
+
const oldFix = lodash_1.default.get(controlDiff.descs.fix, '__old');
|
|
42
|
+
const newFix = lodash_1.default.get(controlDiff.descs.fix, '__new');
|
|
43
|
+
if (oldFix.replace(/\n/g, '').replace(/\W/g, '') !==
|
|
44
|
+
newFix.replace(/\n/g, '').replace(/\W/g, '')) {
|
|
49
45
|
renderableDiffData.updatedFixes.push({
|
|
50
46
|
id: id,
|
|
51
47
|
old: oldFix,
|
|
@@ -54,8 +50,8 @@ function createDiffMarkdown(diff) {
|
|
|
54
50
|
}
|
|
55
51
|
}
|
|
56
52
|
if (controlDiff.impact) {
|
|
57
|
-
const oldImpact = lodash_1.default.get(controlDiff.impact,
|
|
58
|
-
const newImpact = lodash_1.default.get(controlDiff.impact,
|
|
53
|
+
const oldImpact = lodash_1.default.get(controlDiff.impact, '__old');
|
|
54
|
+
const newImpact = lodash_1.default.get(controlDiff.impact, '__new');
|
|
59
55
|
if (oldImpact !== newImpact) {
|
|
60
56
|
renderableDiffData.updatedImpacts.push({
|
|
61
57
|
id: id,
|
|
@@ -65,8 +61,8 @@ function createDiffMarkdown(diff) {
|
|
|
65
61
|
}
|
|
66
62
|
}
|
|
67
63
|
if (controlDiff.title) {
|
|
68
|
-
const oldTitle = lodash_1.default.get(controlDiff.title,
|
|
69
|
-
const newTitle = lodash_1.default.get(controlDiff.title,
|
|
64
|
+
const oldTitle = lodash_1.default.get(controlDiff.title, '__old');
|
|
65
|
+
const newTitle = lodash_1.default.get(controlDiff.title, '__new');
|
|
70
66
|
if (oldTitle !== newTitle) {
|
|
71
67
|
renderableDiffData.updatedTitles.push({
|
|
72
68
|
id: id,
|
|
@@ -76,8 +72,8 @@ function createDiffMarkdown(diff) {
|
|
|
76
72
|
}
|
|
77
73
|
}
|
|
78
74
|
if (controlDiff.desc) {
|
|
79
|
-
const oldDesc = lodash_1.default.get(controlDiff.desc,
|
|
80
|
-
const newDesc = lodash_1.default.get(controlDiff.desc,
|
|
75
|
+
const oldDesc = lodash_1.default.get(controlDiff.desc, '__old');
|
|
76
|
+
const newDesc = lodash_1.default.get(controlDiff.desc, '__new');
|
|
81
77
|
if (oldDesc !== newDesc) {
|
|
82
78
|
renderableDiffData.updatedDescriptions.push({
|
|
83
79
|
id: id,
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export declare function wrap(s: string, lineLength?: number): string;
|
|
2
2
|
export declare function unformatText(s: string): string;
|
|
3
|
-
declare
|
|
4
|
-
declare
|
|
5
|
-
declare const wrapAndEscapeQuotes: (s: string, lineLength?: number) => string;
|
|
6
|
-
export { escapeQuotes, escapeDoubleQuotes, wrapAndEscapeQuotes };
|
|
3
|
+
export declare function removeWhitespace(input: string): string;
|
|
4
|
+
export declare function escapeQuotes(s: string): string;
|
|
7
5
|
export declare function removeNewlinePlaceholders(s: string): string;
|
|
8
6
|
export declare function getFirstPath(object: Record<string, unknown>, paths: string[]): string;
|
|
9
7
|
export declare function hasPath(file: Record<string, unknown>, path: string | string[]): boolean;
|
package/lib/utilities/global.js
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hasPath = exports.getFirstPath = exports.removeNewlinePlaceholders = exports.
|
|
3
|
+
exports.hasPath = exports.getFirstPath = exports.removeNewlinePlaceholders = exports.escapeQuotes = exports.removeWhitespace = exports.unformatText = exports.wrap = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
6
6
|
// Breaks lines down to lineLength number of characters
|
|
7
7
|
function wrap(s, lineLength = 80) {
|
|
8
|
-
let newString =
|
|
9
|
-
let currentLine = "";
|
|
8
|
+
let newString = '';
|
|
10
9
|
let currentLength = 0;
|
|
11
10
|
let shouldBreakLine = false;
|
|
12
|
-
for (
|
|
11
|
+
for (let i = 0; i < s.length; i++) {
|
|
13
12
|
if (shouldBreakLine) {
|
|
14
|
-
newString +=
|
|
13
|
+
newString += '\n';
|
|
15
14
|
currentLength = 0;
|
|
16
15
|
shouldBreakLine = false;
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (nextChar ===
|
|
17
|
+
const currentChar = s.charAt(i);
|
|
18
|
+
const nextChar = s.charAt(i + 1);
|
|
19
|
+
if (nextChar === ' ') {
|
|
21
20
|
if (currentLength >= lineLength) {
|
|
22
21
|
shouldBreakLine = true;
|
|
23
22
|
newString += currentChar;
|
|
@@ -36,16 +35,33 @@ function wrap(s, lineLength = 80) {
|
|
|
36
35
|
return newString;
|
|
37
36
|
}
|
|
38
37
|
exports.wrap = wrap;
|
|
38
|
+
// Remove new lines and tabs
|
|
39
39
|
function unformatText(s) {
|
|
40
40
|
return s.replace(/\n/g, ' ').replace(/\\n/g, ' ').replace(/( +|\t)/g, ' ');
|
|
41
41
|
}
|
|
42
42
|
exports.unformatText = unformatText;
|
|
43
|
-
|
|
43
|
+
function removeWhitespace(input) {
|
|
44
|
+
return input.replace(/\s/gi, '');
|
|
45
|
+
}
|
|
46
|
+
exports.removeWhitespace = removeWhitespace;
|
|
47
|
+
const escapeSingleQuotes = (s) => {
|
|
48
|
+
return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); // Escape backslashes and quotes
|
|
49
|
+
};
|
|
50
|
+
const escapeDoubleQuotes = (s) => {
|
|
51
|
+
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); // Escape backslashes and double quotes
|
|
52
|
+
};
|
|
53
|
+
function escapeQuotes(s) {
|
|
54
|
+
if (s.includes("'") && s.includes('"')) {
|
|
55
|
+
return `%q(${removeNewlinePlaceholders(s)})`;
|
|
56
|
+
}
|
|
57
|
+
else if (s.includes("'")) {
|
|
58
|
+
return `"${escapeDoubleQuotes(removeNewlinePlaceholders(s))}"`;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return `'${escapeSingleQuotes(removeNewlinePlaceholders(s))}'`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
44
64
|
exports.escapeQuotes = escapeQuotes;
|
|
45
|
-
const escapeDoubleQuotes = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); // Escape backslashes and double quotes
|
|
46
|
-
exports.escapeDoubleQuotes = escapeDoubleQuotes;
|
|
47
|
-
const wrapAndEscapeQuotes = (s, lineLength) => escapeDoubleQuotes(wrap(s, lineLength)); // Escape backslashes and quotes, and wrap long lines
|
|
48
|
-
exports.wrapAndEscapeQuotes = wrapAndEscapeQuotes;
|
|
49
65
|
function removeNewlinePlaceholders(s) {
|
|
50
66
|
return s.replace(/\{\{\{\{newlineHERE\}\}\}\}/g, '\n');
|
|
51
67
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import winston from
|
|
1
|
+
import winston from 'winston';
|
|
2
2
|
export declare function createWinstonLogger(level?: string): winston.Logger;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import winston from
|
|
1
|
+
import winston from 'winston';
|
|
2
2
|
import Control from '../objects/control';
|
|
3
3
|
import Profile from '../objects/profile';
|
|
4
4
|
import { ProfileDiff } from '../types/diff';
|
|
@@ -6,11 +6,12 @@ import { OvalDefinitionValue } from '../types/oval';
|
|
|
6
6
|
export declare type UpdatedProfileReturn = {
|
|
7
7
|
profile: Profile;
|
|
8
8
|
diff: {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
ignoreFormattingDiff: ProfileDiff;
|
|
10
|
+
rawDiff: Record<string, unknown>;
|
|
11
11
|
};
|
|
12
12
|
markdown: string;
|
|
13
13
|
};
|
|
14
|
+
export declare function getExistingDescribeFromControl(control: Control): string;
|
|
14
15
|
export declare function findUpdatedControlByAllIdentifiers(existingControl: Control, updatedControls: Control[]): Control | undefined;
|
|
15
16
|
export declare function updateControl(from: Control, update: Partial<Control>, logger: winston.Logger): Control;
|
|
16
17
|
export declare function updateProfile(from: Profile, using: Profile, logger: winston.Logger): Omit<UpdatedProfileReturn, 'markdown'>;
|
package/lib/utilities/update.js
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
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 = void 0;
|
|
5
|
+
exports.updateProfileUsingXCCDF = exports.updateProfile = exports.updateControl = exports.findUpdatedControlByAllIdentifiers = exports.getExistingDescribeFromControl = void 0;
|
|
6
6
|
const tslib_1 = require("tslib");
|
|
7
7
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
8
8
|
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', "\""];
|
|
13
12
|
function projectValuesOntoExistingObj(dst, src, currentPath = '') {
|
|
14
13
|
for (const updatedValue in src) {
|
|
15
14
|
const existingValue = lodash_1.default.get(dst, updatedValue);
|
|
@@ -32,65 +31,69 @@ function projectValuesOntoExistingObj(dst, src, currentPath = '') {
|
|
|
32
31
|
}
|
|
33
32
|
return dst;
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
/*
|
|
35
|
+
Return first index found from given array that is not an empty entry (cell)
|
|
36
|
+
*/
|
|
37
|
+
function getIndexOfFirstLine(auditArray, index, action) {
|
|
38
|
+
let indexVal = index;
|
|
39
|
+
while (auditArray[indexVal] === '') {
|
|
40
|
+
switch (action) {
|
|
41
|
+
case '-':
|
|
42
|
+
indexVal--;
|
|
43
|
+
break;
|
|
44
|
+
case '+':
|
|
45
|
+
indexVal++;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return indexVal;
|
|
50
|
+
}
|
|
51
|
+
/*
|
|
52
|
+
This is the most likely thing to break if you are getting code formatting issues.
|
|
53
|
+
Extract the existing describe blocks (what is actually run by inspec for validation)
|
|
54
|
+
*/
|
|
37
55
|
function getExistingDescribeFromControl(control) {
|
|
56
|
+
// Algorithm:
|
|
57
|
+
// Locate the start and end of the control string
|
|
58
|
+
// Update the end of the control that contains information (if empty lines are at the end of the control)
|
|
59
|
+
// loop: until the start index is changed (loop is done from the bottom up)
|
|
60
|
+
// Clean testing array entry line (removes any non-print characters)
|
|
61
|
+
// if: line starts with meta-information 'tag' or 'ref'
|
|
62
|
+
// set start index to found location
|
|
63
|
+
// break out of the loop
|
|
64
|
+
// end
|
|
65
|
+
// end
|
|
66
|
+
// Remove any empty lines after the start index (in any)
|
|
67
|
+
// Extract the describe block from the audit control given the start and end indices
|
|
68
|
+
// Assumptions:
|
|
69
|
+
// 1 - The meta-information 'tag' or 'ref' precedes the describe block
|
|
70
|
+
// Pros: Solves the potential issue with option 1, as the lookup for the meta-information
|
|
71
|
+
// 'tag' or 'ref' is expected to the at the beginning of the line.
|
|
38
72
|
if (control.code) {
|
|
39
73
|
let existingDescribeBlock = '';
|
|
40
|
-
let
|
|
41
|
-
let
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
mostSpacesSeen = spaces;
|
|
54
|
-
indentedMetadataOverride = false;
|
|
55
|
-
}
|
|
56
|
-
if ((!inQuoteBlock && !inMetadataValueOverride && !indentedMetadataOverride) || inDescribeBlock) {
|
|
57
|
-
// Get the number of spaces at the beggining of the current line
|
|
58
|
-
if (spaces >= 2) {
|
|
59
|
-
const firstWord = wordSplit[0];
|
|
60
|
-
if (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) === -1 || (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) !== -1 && spaces > 2) || inDescribeBlock) {
|
|
61
|
-
inDescribeBlock = true;
|
|
62
|
-
existingDescribeBlock += line + '\n';
|
|
63
|
-
}
|
|
64
|
-
}
|
|
74
|
+
let indexStart = control.code.toLowerCase().indexOf('control');
|
|
75
|
+
let indexEnd = control.code.toLowerCase().trimEnd().lastIndexOf('end');
|
|
76
|
+
const auditControl = control.code.substring(indexStart, indexEnd).split('\n');
|
|
77
|
+
indexStart = 0;
|
|
78
|
+
indexEnd = auditControl.length - 1;
|
|
79
|
+
indexEnd = getIndexOfFirstLine(auditControl, indexEnd, '-');
|
|
80
|
+
let index = indexEnd;
|
|
81
|
+
while (indexStart === 0) {
|
|
82
|
+
const line = auditControl[index].toLowerCase().trim();
|
|
83
|
+
if (line.indexOf('ref') === 0 || line.indexOf('tag') === 0) {
|
|
84
|
+
indexStart = index + 1;
|
|
65
85
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!currentQuoteEscape || !inQuoteBlock) {
|
|
71
|
-
currentQuoteEscape = '"';
|
|
72
|
-
}
|
|
73
|
-
if (currentQuoteEscape === '"') {
|
|
74
|
-
inQuoteBlock = !inQuoteBlock;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
else if (char === "'" && charSplit[index - 1] !== '\\') {
|
|
78
|
-
if (!currentQuoteEscape || !inQuoteBlock) {
|
|
79
|
-
currentQuoteEscape = "'";
|
|
80
|
-
}
|
|
81
|
-
if (currentQuoteEscape === "'") {
|
|
82
|
-
inQuoteBlock = !inQuoteBlock;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|
|
86
|
+
index--;
|
|
87
|
+
}
|
|
88
|
+
indexStart = getIndexOfFirstLine(auditControl, indexStart, '+');
|
|
89
|
+
existingDescribeBlock = auditControl.slice(indexStart, indexEnd + 1).join('\n').toString();
|
|
88
90
|
return existingDescribeBlock;
|
|
89
91
|
}
|
|
90
92
|
else {
|
|
91
93
|
return '';
|
|
92
94
|
}
|
|
93
95
|
}
|
|
96
|
+
exports.getExistingDescribeFromControl = getExistingDescribeFromControl;
|
|
94
97
|
function findUpdatedControlByAllIdentifiers(existingControl, updatedControls) {
|
|
95
98
|
// Try to match based on IDs
|
|
96
99
|
let updatedControl = updatedControls.find((updatedControl) => {
|
|
@@ -127,8 +130,8 @@ function updateProfile(from, using, logger) {
|
|
|
127
130
|
// Find the diff
|
|
128
131
|
const diff = (0, diff_1.diffProfile)(from, using, logger);
|
|
129
132
|
// Add the new controls
|
|
130
|
-
diff.
|
|
131
|
-
const addedControl = diff.
|
|
133
|
+
diff.ignoreFormattingDiff.addedControlIDs.forEach(id => {
|
|
134
|
+
const addedControl = diff.ignoreFormattingDiff.addedControls[id];
|
|
132
135
|
if (addedControl) {
|
|
133
136
|
logger.debug(`New Control: ${addedControl.id} - ${addedControl.title}`);
|
|
134
137
|
to.controls.push(addedControl);
|
|
@@ -141,7 +144,7 @@ function updateProfile(from, using, logger) {
|
|
|
141
144
|
for (const existingControl of from.controls) {
|
|
142
145
|
const updatedControl = findUpdatedControlByAllIdentifiers(existingControl, using.controls);
|
|
143
146
|
if (updatedControl) {
|
|
144
|
-
const controlDiff = diff.
|
|
147
|
+
const controlDiff = diff.ignoreFormattingDiff.changedControls[updatedControl.id];
|
|
145
148
|
if (controlDiff) {
|
|
146
149
|
to.controls.push(updateControl(existingControl, controlDiff, logger));
|
|
147
150
|
}
|
package/lib/utilities/xccdf.js
CHANGED
|
@@ -25,7 +25,6 @@ function removeXMLSpecialCharacters(str) {
|
|
|
25
25
|
return he_1.default.decode(str);
|
|
26
26
|
}
|
|
27
27
|
exports.removeXMLSpecialCharacters = removeXMLSpecialCharacters;
|
|
28
|
-
;
|
|
29
28
|
function severityStringToImpact(string, id) {
|
|
30
29
|
var _a, _b, _c, _d, _e;
|
|
31
30
|
if ((_a = string.match(/none|na|n\/a|not[\s()*_|]?applicable/i)) === null || _a === void 0 ? void 0 : _a.length) {
|