@mitre/inspec-objects 0.0.29 → 0.0.31

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.
@@ -0,0 +1,33 @@
1
+ name: Push @mitre/inspec-objects to NPM
2
+ on:
3
+ release:
4
+ types: [published]
5
+
6
+ jobs:
7
+ build-deploy:
8
+ runs-on: ubuntu-18.04
9
+ steps:
10
+ - uses: actions/checkout@master
11
+
12
+ - name: setup node
13
+ uses: actions/setup-node@v1
14
+ with:
15
+ node-version: "16.x"
16
+ registry-url: 'https://registry.npmjs.org'
17
+
18
+ - name: Install project dependencies
19
+ run: npm ci
20
+
21
+ - name: Remove testing resources
22
+ run: rm -rf test
23
+
24
+ - name: Build
25
+ run: npm run build
26
+
27
+ - name: Pack all items that are published as packages
28
+ run: npm pack
29
+
30
+ - name: Publish SAF CLI to NPM
31
+ run: npm publish --access public mitre-inspec-objects-*.tgz
32
+ env:
33
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -3,4 +3,4 @@ import Profile from "../objects/profile";
3
3
  export declare function processEvaluation(evaluationInput: ContextualizedEvaluation): Profile;
4
4
  export declare function processProfileJSON(profileInput: ContextualizedProfile): Profile;
5
5
  export declare function processExecJSON(execJSON: ExecJSON.Execution): Profile;
6
- export declare function processJSON(json: string): Profile;
6
+ export declare function processInSpecProfile(json: string): Profile;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.processJSON = exports.processExecJSON = exports.processProfileJSON = exports.processEvaluation = void 0;
3
+ exports.processInSpecProfile = exports.processExecJSON = exports.processProfileJSON = exports.processEvaluation = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const inspecjs_1 = require("inspecjs");
6
6
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
@@ -72,7 +72,7 @@ function processExecJSON(execJSON) {
72
72
  return processEvaluation((0, inspecjs_1.contextualizeEvaluation)(execJSON));
73
73
  }
74
74
  exports.processExecJSON = processExecJSON;
75
- function processJSON(json) {
75
+ function processInSpecProfile(json) {
76
76
  const convertedFile = (0, inspecjs_1.convertFile)(json, true);
77
77
  let profile = new profile_1.default();
78
78
  if (convertedFile["1_0_ExecJson"]) {
@@ -87,4 +87,4 @@ function processJSON(json) {
87
87
  profile.controls = lodash_1.default.sortBy(profile.controls, "id");
88
88
  return profile;
89
89
  }
90
- exports.processJSON = processJSON;
90
+ exports.processInSpecProfile = processInSpecProfile;
@@ -40,6 +40,7 @@ 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
+ // Moving the newline removal to diff library rather than processXCCDF level
43
44
  function processXCCDF(xml, removeNewlines = false, useRuleId, ovalDefinitions) {
44
45
  const parsedXML = (0, xccdf_1.convertEncodedXmlIntoJson)(xml);
45
46
  const rules = extractAllRules(parsedXML.Benchmark[0].Group);
@@ -2,8 +2,8 @@ import Profile from "../objects/profile";
2
2
  import { ProfileDiff } from "../types/diff";
3
3
  import winston from "winston";
4
4
  export declare function removeNewlines(control?: Record<string, unknown>): Record<string, unknown>;
5
- export declare function simplifyDiff(diffData: Record<string, unknown>): Record<string, unknown>;
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
- simplified: ProfileDiff;
8
- originalDiff: Record<string, unknown>;
7
+ ignoreFormattingDiff: ProfileDiff;
8
+ rawDiff: Record<string, unknown>;
9
9
  };
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.diffProfile = exports.simplifyDiff = exports.removeNewlines = void 0;
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 {};
@@ -20,14 +21,15 @@ function removeNewlines(control) {
20
21
  });
21
22
  }
22
23
  exports.removeNewlines = removeNewlines;
23
- function simplifyDiff(diffData) {
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
27
  if (lodash_1.default.has(diffValue, "__new")) {
26
28
  // Remove any trailing space
27
29
  if (typeof lodash_1.default.get(diffValue, "__new") === "string" &&
28
30
  typeof lodash_1.default.get(diffValue, "__old") === "string") {
29
- if (lodash_1.default.get(diffValue, "__new").trim() !==
30
- lodash_1.default.get(diffValue, "__old").trim()) {
31
+ if ((0, global_1.removeWhitespace)(lodash_1.default.get(diffValue, "__new")) !==
32
+ (0, global_1.removeWhitespace)(lodash_1.default.get(diffValue, "__old"))) {
31
33
  lodash_1.default.set(result, key, lodash_1.default.get(diffValue, "__new"));
32
34
  }
33
35
  }
@@ -41,7 +43,7 @@ function simplifyDiff(diffData) {
41
43
  .filter((value) => value);
42
44
  }
43
45
  else if (typeof diffValue === "object") {
44
- result[key] = simplifyDiff(diffValue);
46
+ result[key] = ignoreFormattingDiff(diffValue);
45
47
  }
46
48
  else if (key.endsWith("__deleted")) {
47
49
  return undefined;
@@ -51,19 +53,22 @@ function simplifyDiff(diffData) {
51
53
  }
52
54
  });
53
55
  }
54
- exports.simplifyDiff = simplifyDiff;
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
- renamedControlIds: {},
62
+ renamedControlIDs: {},
63
+ changedControlIDs: [],
60
64
  addedControls: {},
61
65
  changedControls: {},
62
66
  };
63
67
  const originalDiff = {
64
68
  addedControlIDs: [],
65
69
  removedControlIDs: [],
66
- renamedControlIds: {},
70
+ renamedControlIDs: {},
71
+ changedControlIDs: [],
67
72
  addedControls: {},
68
73
  changedControls: {},
69
74
  };
@@ -72,9 +77,11 @@ 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
86
  if (diffValue[0] === "-") {
80
87
  const existingControl = fromProfile.controls.find((control) => control.id === diffValue[1]);
@@ -82,13 +89,18 @@ function diffProfile(fromProfile, toProfile, logger) {
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.renamedControlIds[existingControl.id] = newControl.id;
86
- originalDiff.renamedControlIds[existingControl.id] = newControl.id;
92
+ profileDiff.renamedControlIDs[existingControl.id] = newControl.id;
93
+ originalDiff.renamedControlIDs[existingControl.id] = newControl.id;
87
94
  changedControlIds.push(newControl.id.toLowerCase());
88
95
  const controlDiff = lodash_1.default.omit((0, json_diff_1.diff)(existingControl, newControl), "code__deleted");
89
- profileDiff.changedControls[newControl.id] = simplifyDiff(controlDiff);
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
- logger.verbose(`Control ${existingControl.id} has been upgraded to ${newControl.id}`);
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] === "+" && !changedControlIds.includes(diffValue[1].toLowerCase())) {
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);
@@ -118,11 +136,13 @@ function diffProfile(fromProfile, toProfile, logger) {
118
136
  if (toControl) {
119
137
  const controlDiff = lodash_1.default.omit((0, json_diff_1.diff)(fromControl, toControl), "code__deleted");
120
138
  if (controlDiff) {
121
- profileDiff.changedControls[toControl.id] = simplifyDiff(controlDiff);
139
+ profileDiff.changedControls[toControl.id] = ignoreFormattingDiff(controlDiff);
140
+ profileDiff.changedControlIDs.push(toControl.id);
122
141
  originalDiff.changedControls[toControl.id] = controlDiff;
142
+ originalDiff.changedControlIDs.push(toControl.id);
123
143
  }
124
144
  }
125
145
  }
126
- return { simplified: profileDiff, originalDiff: originalDiff };
146
+ return { ignoreFormattingDiff: profileDiff, rawDiff: originalDiff };
127
147
  }
128
148
  exports.diffProfile = diffProfile;
@@ -1,5 +1,5 @@
1
1
  import { ProfileDiff } from "../types/diff";
2
2
  export declare function createDiffMarkdown(diff: {
3
- simplified: ProfileDiff;
4
- originalDiff: any;
3
+ ignoreFormattingDiff: ProfileDiff;
4
+ rawDiff: any;
5
5
  }): string;
@@ -11,7 +11,7 @@ function getUpdatedCheckForId(id, profile) {
11
11
  }
12
12
  function createDiffMarkdown(diff) {
13
13
  const renderableDiffData = {
14
- addedControls: Object.values(diff.simplified.addedControls),
14
+ addedControls: Object.values(diff.ignoreFormattingDiff.addedControls),
15
15
  hasRenamedControls: false,
16
16
  renamedControls: [],
17
17
  updatedChecks: [],
@@ -20,14 +20,14 @@ function createDiffMarkdown(diff) {
20
20
  updatedTitles: [],
21
21
  updatedDescriptions: [],
22
22
  };
23
- Object.entries(diff.simplified.renamedControlIds).forEach(([oldId, newId]) => {
23
+ Object.entries(diff.ignoreFormattingDiff.renamedControlIDs).forEach(([oldId, newId]) => {
24
24
  renderableDiffData.hasRenamedControls = true;
25
25
  renderableDiffData.renamedControls.push({
26
26
  oldId: oldId,
27
27
  newId: newId,
28
28
  });
29
29
  });
30
- Object.entries(diff.originalDiff.changedControls).forEach(([id, controlDiff]) => {
30
+ Object.entries(diff.rawDiff.changedControls).forEach(([id, controlDiff]) => {
31
31
  var _a, _b;
32
32
  if ((_a = controlDiff.descs) === null || _a === void 0 ? void 0 : _a.check) {
33
33
  const oldCheck = lodash_1.default.get(controlDiff.descs.check, "__old");
@@ -1,5 +1,6 @@
1
1
  export declare function wrap(s: string, lineLength?: number): string;
2
2
  export declare function unformatText(s: string): string;
3
+ export declare function removeWhitespace(input: string): string;
3
4
  declare const escapeQuotes: (s: string) => string;
4
5
  declare const escapeDoubleQuotes: (s: string) => string;
5
6
  declare const wrapAndEscapeQuotes: (s: string, lineLength?: number) => string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hasPath = exports.getFirstPath = exports.removeNewlinePlaceholders = exports.wrapAndEscapeQuotes = exports.escapeDoubleQuotes = exports.escapeQuotes = exports.unformatText = exports.wrap = void 0;
3
+ exports.hasPath = exports.getFirstPath = exports.removeNewlinePlaceholders = exports.wrapAndEscapeQuotes = exports.escapeDoubleQuotes = 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
@@ -40,6 +40,10 @@ function unformatText(s) {
40
40
  return s.replace(/\n/g, ' ').replace(/\\n/g, ' ').replace(/( +|\t)/g, ' ');
41
41
  }
42
42
  exports.unformatText = unformatText;
43
+ function removeWhitespace(input) {
44
+ return input.replace(/\s/gi, '');
45
+ }
46
+ exports.removeWhitespace = removeWhitespace;
43
47
  const escapeQuotes = (s) => s.replace(/\\/g, "\\\\").replace(/'/g, "\\'"); // Escape backslashes and quotes
44
48
  exports.escapeQuotes = escapeQuotes;
45
49
  const escapeDoubleQuotes = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); // Escape backslashes and double quotes
@@ -6,8 +6,8 @@ import { OvalDefinitionValue } from '../types/oval';
6
6
  export declare type UpdatedProfileReturn = {
7
7
  profile: Profile;
8
8
  diff: {
9
- simplified: ProfileDiff;
10
- originalDiff: Record<string, unknown>;
9
+ ignoreFormattingDiff: ProfileDiff;
10
+ rawDiff: Record<string, unknown>;
11
11
  };
12
12
  markdown: string;
13
13
  };
@@ -38,14 +38,18 @@ function getExistingDescribeFromControl(control) {
38
38
  if (control.code) {
39
39
  let existingDescribeBlock = '';
40
40
  let currentQuoteEscape = '';
41
+ let percentBlockRegexp = /%[qQriIwWxs]?(?<lDelimiter>[\(\[\{\<])/;
42
+ let inPercentBlock = false;
41
43
  let inQuoteBlock = false;
42
44
  let inMetadataValueOverride = false;
43
45
  let indentedMetadataOverride = false;
44
46
  let inDescribeBlock = false;
45
47
  let mostSpacesSeen = 0;
48
+ let lDelimiter = '(';
49
+ let rDelimiter = ')';
46
50
  control.code.split('\n').forEach((line) => {
47
- const wordSplit = line.trim().split(' ');
48
- const spaces = line.substring(0, line.indexOf(wordSplit[0])).length;
51
+ const wordArray = line.trim().split(' ');
52
+ const spaces = line.substring(0, line.indexOf(wordArray[0])).length;
49
53
  if (spaces - mostSpacesSeen > 10) {
50
54
  indentedMetadataOverride = true;
51
55
  }
@@ -53,24 +57,51 @@ function getExistingDescribeFromControl(control) {
53
57
  mostSpacesSeen = spaces;
54
58
  indentedMetadataOverride = false;
55
59
  }
56
- if (control.id === 'SV-204392') {
57
- console.log(line);
58
- console.log(inDescribeBlock);
59
- }
60
- if ((!inQuoteBlock && !inMetadataValueOverride && !indentedMetadataOverride) || inDescribeBlock) {
61
- // Get the number of spaces at the beggining of the current line
62
- if (spaces >= 2) {
63
- const firstWord = wordSplit[0];
60
+ if ((!inPercentBlock && !inQuoteBlock && !inMetadataValueOverride && !indentedMetadataOverride) || inDescribeBlock) {
61
+ if (inDescribeBlock && wordArray.length === 1 && wordArray.includes('')) {
62
+ existingDescribeBlock += '\n';
63
+ }
64
+ // Get the number of spaces at the beginning of the current line
65
+ else if (spaces >= 2) {
66
+ const firstWord = wordArray[0];
64
67
  if (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) === -1 || (knownInSpecKeywords.indexOf(firstWord.toLowerCase()) !== -1 && spaces > 2) || inDescribeBlock) {
65
68
  inDescribeBlock = true;
66
69
  existingDescribeBlock += line + '\n';
67
70
  }
68
71
  }
69
72
  }
70
- wordSplit.forEach((word, index) => {
71
- const charSplit = word.split('');
72
- charSplit.forEach((char, index) => {
73
- if (char === '"' && charSplit[index - 1] !== '\\') {
73
+ wordArray.forEach((word, index) => {
74
+ //console.log(`LDELIMITER: \"${lDelimiter}\" RDELIMITER: \"${rDelimiter}\"`)
75
+ let percentBlockMatch = percentBlockRegexp.exec(word);
76
+ if (percentBlockMatch && inPercentBlock === false) {
77
+ inPercentBlock = true;
78
+ lDelimiter = percentBlockMatch.groups.lDelimiter || '(';
79
+ switch (lDelimiter) {
80
+ case '{': {
81
+ rDelimiter = '}';
82
+ break;
83
+ }
84
+ case '[': {
85
+ rDelimiter = ']';
86
+ break;
87
+ }
88
+ case '<': {
89
+ rDelimiter = '>';
90
+ break;
91
+ }
92
+ default: {
93
+ break;
94
+ }
95
+ }
96
+ }
97
+ const charArray = word.split('');
98
+ charArray.forEach((char, index) => {
99
+ if (inPercentBlock) {
100
+ if (char === rDelimiter && charArray[index - 1] !== '\\' && !inQuoteBlock) {
101
+ inPercentBlock = false;
102
+ }
103
+ }
104
+ if (char === '"' && charArray[index - 1] !== '\\') {
74
105
  if (!currentQuoteEscape || !inQuoteBlock) {
75
106
  currentQuoteEscape = '"';
76
107
  }
@@ -78,7 +109,7 @@ function getExistingDescribeFromControl(control) {
78
109
  inQuoteBlock = !inQuoteBlock;
79
110
  }
80
111
  }
81
- else if (char === "'" && charSplit[index - 1] !== '\\') {
112
+ else if (char === "'" && charArray[index - 1] !== '\\') {
82
113
  if (!currentQuoteEscape || !inQuoteBlock) {
83
114
  currentQuoteEscape = "'";
84
115
  }
@@ -89,7 +120,8 @@ function getExistingDescribeFromControl(control) {
89
120
  });
90
121
  });
91
122
  });
92
- return existingDescribeBlock;
123
+ // Take off the extra newline at the end
124
+ return existingDescribeBlock.slice(0, -1);
93
125
  }
94
126
  else {
95
127
  return '';
@@ -131,8 +163,8 @@ function updateProfile(from, using, logger) {
131
163
  // Find the diff
132
164
  const diff = (0, diff_1.diffProfile)(from, using, logger);
133
165
  // Add the new controls
134
- diff.simplified.addedControlIDs.forEach(id => {
135
- const addedControl = diff.simplified.addedControls[id];
166
+ diff.ignoreFormattingDiff.addedControlIDs.forEach(id => {
167
+ const addedControl = diff.ignoreFormattingDiff.addedControls[id];
136
168
  if (addedControl) {
137
169
  logger.debug(`New Control: ${addedControl.id} - ${addedControl.title}`);
138
170
  to.controls.push(addedControl);
@@ -145,7 +177,7 @@ function updateProfile(from, using, logger) {
145
177
  for (const existingControl of from.controls) {
146
178
  const updatedControl = findUpdatedControlByAllIdentifiers(existingControl, using.controls);
147
179
  if (updatedControl) {
148
- const controlDiff = diff.simplified.changedControls[updatedControl.id];
180
+ const controlDiff = diff.ignoreFormattingDiff.changedControls[updatedControl.id];
149
181
  if (controlDiff) {
150
182
  to.controls.push(updateControl(existingControl, controlDiff, logger));
151
183
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mitre/inspec-objects",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Typescript objects for normalizing between InSpec profiles and XCCDF benchmarks",
5
5
  "main": "lib/index.js",
6
6
  "publishConfig": {