@mitre/inspec-objects 0.0.34 → 1.0.1

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,21 @@
1
+ name-template: "$NEXT_PATCH_VERSION"
2
+ tag-template: "$NEXT_PATCH_VERSION"
3
+ categories:
4
+ - title: "New Features"
5
+ labels:
6
+ - "feature"
7
+ - "enhancement"
8
+ - title: "Bug Fixes"
9
+ labels:
10
+ - "fix"
11
+ - "bugfix"
12
+ - "bug"
13
+ - title: "Security Enhancements"
14
+ labels:
15
+ - "security"
16
+ - title: "Dependency Updates"
17
+ labels:
18
+ - "dependencies"
19
+ change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
20
+ template: |
21
+ $CHANGES
@@ -0,0 +1,16 @@
1
+ name: Draft Release
2
+
3
+ on:
4
+ push:
5
+ # branches to consider in the event; optional, defaults to all
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ update_draft_release:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ # Drafts your next Release notes as Pull Requests are merged into "master"
14
+ - uses: toolmantim/release-drafter@v5.2.0
15
+ env:
16
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -49,5 +49,6 @@ export default class Control {
49
49
  };
50
50
  constructor(data?: Partial<Control>);
51
51
  toUnformattedObject(): Control;
52
- toRuby(): string;
52
+ toString(): string;
53
+ toRuby(verbose?: boolean): string;
53
54
  }
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
6
  const flat_1 = require("flat");
7
7
  const global_1 = require("../utilities/global");
8
+ const logging_1 = require("../utilities/logging");
8
9
  function objectifyDescriptions(descs) {
9
10
  if (Array.isArray(descs)) {
10
11
  const descriptions = {};
@@ -36,21 +37,86 @@ class Control {
36
37
  });
37
38
  return new Control((0, flat_1.unflatten)(flattened));
38
39
  }
39
- toRuby() {
40
+ // WIP - provides the ability to get the control in its raw form
41
+ toString() {
42
+ let result = '';
43
+ result += `control '${this.id}' do\n`;
44
+ if (this.title) {
45
+ result += ` title "${this.title}"\n`;
46
+ }
47
+ // This is the known 'default' description - on previous version this content was repeated on descriptions processed by "descs"
48
+ if (this.desc) {
49
+ result += ` desc "${this.desc}"\n`;
50
+ }
51
+ if (this.descs) {
52
+ Object.entries(this.descs).forEach(([key, subDesc]) => {
53
+ if (subDesc) {
54
+ result += ` desc '${key}', "${subDesc}"\n`;
55
+ }
56
+ });
57
+ }
58
+ if (this.impact) {
59
+ result += ` impact ${this.impact}\n`;
60
+ }
61
+ if (this.refs) {
62
+ this.refs.forEach((ref) => {
63
+ var _a;
64
+ if (typeof ref === 'string') {
65
+ result += ` ref "${ref}"\n`;
66
+ }
67
+ else {
68
+ result += ` ref ${((_a = ref.ref) === null || _a === void 0 ? void 0 : _a.toString()) || ''}, url: ${ref.url || ''}`;
69
+ }
70
+ });
71
+ }
72
+ Object.entries(this.tags).forEach(([tag, value]) => {
73
+ if (typeof value === 'object') {
74
+ if (Array.isArray(value) && typeof value[0] === 'string') {
75
+ result += ` tag ${tag}: ${JSON.stringify(value)}\n`;
76
+ }
77
+ else {
78
+ result += ` tag '${tag}': ${(value == null ? 'nil' : value)}\n`;
79
+ }
80
+ }
81
+ else if (typeof value === 'string') {
82
+ if (value.includes('"')) {
83
+ result += ` tag "${tag}": "${value}"\n`;
84
+ }
85
+ else {
86
+ result += ` tag '${tag}': '${value}'\n`;
87
+ }
88
+ }
89
+ });
90
+ if (this.describe) {
91
+ result += '\n';
92
+ result += this.describe;
93
+ }
94
+ if (!result.slice(-1).match('\n')) {
95
+ result += '\n';
96
+ }
97
+ result += 'end\n';
98
+ return result;
99
+ }
100
+ toRuby(verbose = true) {
101
+ const logger = (0, logging_1.createWinstonLogger)();
40
102
  let result = '';
41
103
  result += `control '${this.id}' do\n`;
42
104
  if (this.title) {
43
105
  result += ` title ${(0, global_1.escapeQuotes)(this.title)}\n`;
44
106
  }
45
107
  else {
46
- console.error(`${this.id} does not have a title`);
108
+ if (verbose) {
109
+ logger.error(`${this.id} does not have a title`);
110
+ }
47
111
  }
48
112
  // This is the known 'default' description - on previous version this content was repeated on descriptions processed by "descs"
49
113
  if (this.desc) {
50
114
  result += ` desc ${(0, global_1.escapeQuotes)(this.desc)}\n`;
51
115
  }
52
116
  else {
53
- console.error(`${this.id} does not have a desc`);
117
+ if (verbose) {
118
+ logger.error(`${this.id} does not have a desc`);
119
+ }
54
120
  }
55
121
  if (this.descs) {
56
122
  Object.entries(this.descs).forEach(([key, subDesc]) => {
@@ -60,7 +126,9 @@ class Control {
60
126
  // The "default" keyword may have the same content as the desc content for backward compatibility with different historical InSpec versions.
61
127
  // In that case, we can ignore writing the "default" subdescription field.
62
128
  // If they are different, however, someone may be trying to use the keyword "default" for a unique subdescription, which should not be done.
63
- console.error(`${this.id} has a subdescription called "default" with contents that do not match the main description. "Default" should not be used as a keyword for unique sub-descriptions.`);
129
+ if (verbose) {
130
+ logger.error(`${this.id} has a subdescription called "default" with contents that do not match the main description. "Default" should not be used as a keyword for unique sub-descriptions.`);
131
+ }
64
132
  }
65
133
  }
66
134
  else {
@@ -68,15 +136,19 @@ class Control {
68
136
  }
69
137
  }
70
138
  else {
71
- console.error(`${this.id} does not have a desc for the value ${key}`);
139
+ if (verbose) {
140
+ logger.error(`${this.id} does not have a desc for the value ${key}`);
141
+ }
72
142
  }
73
143
  });
74
144
  }
75
- if (this.impact) {
76
- result += ` impact ${this.impact}\n`;
145
+ if (this.impact !== undefined) {
146
+ result += ` impact ${(this.impact <= 0 ? this.impact.toFixed(1) : this.impact)}\n`;
77
147
  }
78
148
  else {
79
- console.error(`${this.id} does not have an impact`);
149
+ if (verbose) {
150
+ logger.error(`${this.id} does not have an impact`);
151
+ }
80
152
  }
81
153
  if (this.refs) {
82
154
  this.refs.forEach((ref) => {
@@ -115,6 +187,18 @@ class Control {
115
187
  result += ` tag ${tag}: ${(0, global_1.escapeQuotes)(value)}\n`;
116
188
  }
117
189
  }
190
+ else {
191
+ const nilTagList = ['severity', 'satisfies'];
192
+ if (nilTagList.includes(tag)) {
193
+ result += ` tag ${tag}: nil\n`;
194
+ }
195
+ else {
196
+ result += ` tag '${tag}'\n`;
197
+ }
198
+ if (verbose) {
199
+ logger.info(`${this.id} does not have a value for tag: ${tag}`);
200
+ }
201
+ }
118
202
  });
119
203
  if (this.describe) {
120
204
  result += '\n';
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processOVAL = exports.extractAllCriteriaRefs = void 0;
4
4
  const xccdf_1 = require("../utilities/xccdf");
5
+ const logging_1 = require("../utilities/logging");
5
6
  // https://stackoverflow.com/questions/9133500/how-to-find-a-node-in-a-tree-with-javascript
6
7
  function searchTree(aTree, fCompair, bGreedy) {
7
8
  let oNode; // always the current node
@@ -53,6 +54,7 @@ function extractAllCriteriaRefs(initialCriteria) {
53
54
  exports.extractAllCriteriaRefs = extractAllCriteriaRefs;
54
55
  function processOVAL(oval) {
55
56
  var _a;
57
+ const logger = (0, logging_1.createWinstonLogger)();
56
58
  if (!oval) {
57
59
  return undefined;
58
60
  }
@@ -73,7 +75,7 @@ function processOVAL(oval) {
73
75
  if (foundCriteriaRefererence.object) {
74
76
  foundCriteriaRefererence.object.forEach((object) => {
75
77
  if (!object['@_object_ref']) {
76
- console.warn(`Found object without object_ref in test ${criteriaRef}`);
78
+ logger.warn(`Found object without object_ref in test ${criteriaRef}`);
77
79
  }
78
80
  else {
79
81
  const objectRef = object['@_object_ref'];
@@ -82,7 +84,7 @@ function processOVAL(oval) {
82
84
  foundObjects.push(foundObjectReference);
83
85
  }
84
86
  else {
85
- console.warn(`Could not find object ${objectRef} for test ${criteriaRef}`);
87
+ logger.warn(`Could not find object ${objectRef} for test ${criteriaRef}`);
86
88
  }
87
89
  }
88
90
  });
@@ -90,7 +92,7 @@ function processOVAL(oval) {
90
92
  if (foundCriteriaRefererence.state) {
91
93
  foundCriteriaRefererence.state.forEach((state) => {
92
94
  if (!state['@_state_ref']) {
93
- console.warn(`Found state without state_ref in test ${criteriaRef}`);
95
+ logger.warn(`Found state without state_ref in test ${criteriaRef}`);
94
96
  }
95
97
  else {
96
98
  const stateRef = state['@_state_ref'];
@@ -99,7 +101,7 @@ function processOVAL(oval) {
99
101
  foundStates.push(foundStateReference);
100
102
  }
101
103
  else {
102
- console.warn(`Could not find state ${stateRef} for test ${criteriaRef}`);
104
+ logger.warn(`Could not find state ${stateRef} for test ${criteriaRef}`);
103
105
  }
104
106
  }
105
107
  });
@@ -8,6 +8,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
10
  const pretty_1 = tslib_1.__importDefault(require("pretty"));
11
+ const logging_1 = require("../utilities/logging");
11
12
  function extractAllRules(groups) {
12
13
  const rules = [];
13
14
  groups.forEach((group) => {
@@ -42,7 +43,11 @@ function ensureDecodedXMLStringValue(input) {
42
43
  }
43
44
  // Moving the newline removal to diff library rather than processXCCDF level
44
45
  function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
46
+ const logger = (0, logging_1.createWinstonLogger)();
45
47
  const parsedXML = (0, xccdf_1.convertEncodedXmlIntoJson)(xml);
48
+ if (parsedXML.Benchmark === undefined) {
49
+ throw new Error('Could not process the XCCDF file, check the input to make sure this is a properly formatted XCCDF file.');
50
+ }
46
51
  const rules = extractAllRules(parsedXML.Benchmark[0].Group);
47
52
  const profile = new profile_1.default({
48
53
  name: parsedXML.Benchmark[0]['@_id'],
@@ -116,7 +121,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
116
121
  control.desc = extractedDescription || '';
117
122
  }
118
123
  else {
119
- console.warn(`Invalid value for extracted description: ${extractedDescription}`);
124
+ logger.warn(`Invalid value for extracted description: ${extractedDescription}`);
120
125
  }
121
126
  control.impact = (0, xccdf_1.severityStringToImpact)(rule['@_severity'] || 'medium', rule.group['@_id']);
122
127
  if (!control.descs || Array.isArray(control.descs)) {
@@ -131,7 +136,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
131
136
  let referenceID = null;
132
137
  for (const checkContent of rule.check) {
133
138
  if ('check-content-ref' in checkContent && checkContent['@_system'].includes('oval')) {
134
- console.log(`Found OVAL reference: ${checkContent['@_system']}`);
139
+ logger.info(`Found OVAL reference: ${checkContent['@_system']}`);
135
140
  for (const checkContentRef of checkContent['check-content-ref']) {
136
141
  if (checkContentRef['@_name']) {
137
142
  referenceID = checkContentRef['@_name'];
@@ -143,7 +148,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
143
148
  control.descs.check = (0, xccdf_1.removeXMLSpecialCharacters)(ovalDefinitions[referenceID].metadata[0].title);
144
149
  }
145
150
  else if (referenceID) {
146
- console.warn(`Could not find OVAL definition for ${referenceID}`);
151
+ logger.warn(`Could not find OVAL definition for ${referenceID}`);
147
152
  }
148
153
  }
149
154
  }
@@ -153,7 +158,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
153
158
  for (const complexChecks of rule['complex-check']) {
154
159
  const allComplexChecks = extractAllComplexChecks(complexChecks);
155
160
  if (control.id === '1.1.1.5') {
156
- console.log(allComplexChecks);
161
+ logger.info(allComplexChecks);
157
162
  }
158
163
  allComplexChecks.forEach((complexCheck) => {
159
164
  if (complexCheck.check) {
@@ -162,7 +167,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
162
167
  if ((_a = check['@_system']) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes('oval')) {
163
168
  const ovalReference = check['check-content-ref'][0]['@_name'];
164
169
  if (!ovalDefinitions) {
165
- console.warn(`Missing OVAL definitions! Unable to process OVAL reference: ${ovalReference}`);
170
+ logger.warn(`Missing OVAL definitions! Unable to process OVAL reference: ${ovalReference}`);
166
171
  }
167
172
  else if (ovalReference && ovalReference in ovalDefinitions) {
168
173
  ovalDefinitions[ovalReference].resolvedValues.forEach((resolvedValue) => {
@@ -185,7 +190,7 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
185
190
  }
186
191
  }
187
192
  else {
188
- console.warn(`Found external reference to unknown system: ${check['@_system']}, only OVAL is supported`);
193
+ logger.warn(`Found external reference to unknown system: ${check['@_system']}, only OVAL is supported`);
189
194
  }
190
195
  });
191
196
  }
@@ -332,19 +337,19 @@ function processXCCDF(xml, removeNewlines, useRuleId, ovalDefinitions) {
332
337
  control.tags[identifierType] = lodash_1.default.union(control.tags[identifierType], [identifier]);
333
338
  }
334
339
  else {
335
- console.warn(`Attempted to push identifier to control tags when identifier already exists: ${identifierType}: ${identifier}`);
340
+ logger.warn(`Attempted to push identifier to control tags when identifier already exists: ${identifierType}: ${identifier}`);
336
341
  }
337
342
  }
338
343
  else {
339
- console.warn('Reference parts of invalid length:');
340
- console.log(referenceParts);
344
+ logger.warn('Reference parts of invalid length:');
345
+ logger.info(referenceParts);
341
346
  }
342
347
  }
343
348
  }
344
349
  catch (e) {
345
- console.warn(`Error parsing ref for control ${control.id}: `);
346
- console.warn(JSON.stringify(reference, null, 2));
347
- console.warn(e);
350
+ logger.warn(`Error parsing ref for control ${control.id}: `);
351
+ logger.warn(JSON.stringify(reference, null, 2));
352
+ logger.warn(e);
348
353
  }
349
354
  }
350
355
  });
@@ -44,6 +44,9 @@ function removeWhitespace(input) {
44
44
  return input.replace(/\s/gi, '');
45
45
  }
46
46
  exports.removeWhitespace = removeWhitespace;
47
+ const escapeSpecialCaseBackslashes = (s) => {
48
+ return s.replace(/\\\)/g, '\\\\)'); // Escape backslashes if preceding close parentheses
49
+ };
47
50
  const escapeSingleQuotes = (s) => {
48
51
  return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); // Escape backslashes and quotes
49
52
  };
@@ -52,7 +55,7 @@ const escapeDoubleQuotes = (s) => {
52
55
  };
53
56
  function escapeQuotes(s) {
54
57
  if (s.includes("'") && s.includes('"')) {
55
- return `%q(${removeNewlinePlaceholders(s)})`;
58
+ return `%q(${escapeSpecialCaseBackslashes(removeNewlinePlaceholders(s))})`;
56
59
  }
57
60
  else if (s.includes("'")) {
58
61
  return `"${escapeDoubleQuotes(removeNewlinePlaceholders(s))}"`;
@@ -5,9 +5,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
5
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
+ const diff_1 = require("./diff");
8
9
  const profile_1 = tslib_1.__importDefault(require("../objects/profile"));
9
10
  const xccdf_1 = require("../parsers/xccdf");
10
- const diff_1 = require("./diff");
11
11
  const diffMarkdown_1 = require("./diffMarkdown");
12
12
  function projectValuesOntoExistingObj(dst, src, currentPath = '') {
13
13
  for (const updatedValue in src) {
@@ -51,7 +51,7 @@ function getRangesForLines(text) {
51
51
  - Percent literals (%; delimiters: (), {}, [], <>, most non-
52
52
  alphanumeric characters); (e.g., "%()")
53
53
  - Multi-line comments (e.g., =begin\nSome comment\n=end)
54
- - Variable delimiters (i.e., paranthesis: (); array: []; hash: {})
54
+ - Variable delimiters (i.e., parenthesis: (); array: []; hash: {})
55
55
  */
56
56
  const stringDelimiters = { '(': ')', '{': '}', '[': ']', '<': '>' };
57
57
  const variableDelimiters = { '(': ')', '{': '}', '[': ']' };
@@ -62,6 +62,7 @@ function getRangesForLines(text) {
62
62
  skipCharLength[skipCharLength["string"] = '('.length] = "string";
63
63
  skipCharLength[skipCharLength["percentString"] = 'q('.length] = "percentString";
64
64
  skipCharLength[skipCharLength["commentBegin"] = '=begin'.length] = "commentBegin";
65
+ skipCharLength[skipCharLength["inlineInterpolationBegin"] = '{'.length] = "inlineInterpolationBegin";
65
66
  })(skipCharLength || (skipCharLength = {}));
66
67
  const stack = [];
67
68
  const rangeStack = [];
@@ -80,6 +81,8 @@ function getRangesForLines(text) {
80
81
  const isVariableDelimiterChar = Object.keys(variableDelimiters).includes(char);
81
82
  const isStringDelimiterChar = ((j < line.length - 1) && (/^[^A-Za-z0-9]$/.test(line[j + 1])));
82
83
  const isCommentBeginChar = ((j == 0) && (line.length >= 6) && (line.slice(0, 6) == '=begin'));
84
+ const isCommentChar = /^\s*#/.test(line);
85
+ const isInlineInterpolation = (char == '#' && ((j < line.length - 1) && line[j + 1] == '{'));
83
86
  const isPercentStringKeyChar = ((j < line.length - 1) && (strings.includes(line[j + 1])));
84
87
  const isPercentStringDelimiterChar = ((j < line.length - 2) && (/^[^A-Za-z0-9]$/.test(line[j + 2])));
85
88
  const isPercentString = (isPercentStringKeyChar && isPercentStringDelimiterChar);
@@ -89,6 +92,11 @@ function getRangesForLines(text) {
89
92
  const stringPushCondition = (baseCondition && isPercentChar && isStringDelimiterChar);
90
93
  const percentStringPushCondition = (baseCondition && isPercentChar && isPercentString);
91
94
  const commentBeginCondition = (baseCondition && isCommentBeginChar);
95
+ const commentCondition = (baseCondition && isCommentChar);
96
+ const inlineInterpolationCondition = (isNotEmptyStack && isInlineInterpolation);
97
+ if (commentCondition) {
98
+ break;
99
+ }
92
100
  if (stringPushCondition) {
93
101
  j += skipCharLength.string; // j += 1
94
102
  }
@@ -98,6 +106,9 @@ function getRangesForLines(text) {
98
106
  else if (commentBeginCondition) {
99
107
  j += skipCharLength.commentBegin; // j += 6
100
108
  }
109
+ else if (inlineInterpolationCondition) {
110
+ j += skipCharLength.inlineInterpolationBegin; // j += 1
111
+ }
101
112
  char = line[j];
102
113
  baseCondition = (isNotEmptyStack && isNotEscapeChar);
103
114
  const delimiterCondition = (baseCondition && Object.keys(stringDelimiters).includes(stack[stack.length - 1]));
@@ -108,7 +119,7 @@ function getRangesForLines(text) {
108
119
  const commentEndCondition = (baseCondition && isCommentEndChar && (stack[stack.length - 1] == '=begin'));
109
120
  const popCondition = (basePopCondition || delimiterPopCondition || commentEndCondition);
110
121
  const pushCondition = (quotePushCondition || variablePushCondition || stringPushCondition ||
111
- percentStringPushCondition || delimiterPushCondition || commentBeginCondition);
122
+ percentStringPushCondition || delimiterPushCondition || commentBeginCondition || inlineInterpolationCondition);
112
123
  if (popCondition) {
113
124
  stack.pop();
114
125
  rangeStack[rangeStack.length - 1].push(i);
@@ -121,6 +132,9 @@ function getRangesForLines(text) {
121
132
  if (commentBeginCondition) {
122
133
  stack.push('=begin');
123
134
  }
135
+ else if (inlineInterpolationCondition) {
136
+ stack.push('{');
137
+ }
124
138
  else {
125
139
  stack.push(char);
126
140
  }
@@ -177,9 +191,11 @@ function getExistingDescribeFromControl(control) {
177
191
  if (control.code) {
178
192
  // Join multi-line strings in InSpec control.
179
193
  const ranges = getRangesForLines(control.code);
194
+ // Get the entries that have delimiters that span multi-lines
180
195
  const multiLineRanges = getMultiLineRanges(ranges);
181
- const lines = joinMultiLineStringsFromRanges(control.code, multiLineRanges); // Array of lines representing the full InSpec control, with multi-line strings collapsed
182
- // Define RegExp for lines to skip when searching for describe block.
196
+ // Array of lines representing the full InSpec control, with multi-line strings collapsed
197
+ const lines = joinMultiLineStringsFromRanges(control.code, multiLineRanges);
198
+ // Define RegExp for lines to skip.
183
199
  const skip = ['control\\W', ' title\\W', ' desc\\W', ' impact\\W', ' tag\\W', ' ref\\W'];
184
200
  const skipRegExp = RegExp(skip.map(x => `(^${x})`).join('|'));
185
201
  // Extract describe block from InSpec control with collapsed multiline strings.
@@ -197,7 +213,8 @@ function getExistingDescribeFromControl(control) {
197
213
  ignoreNewLine = true;
198
214
  }
199
215
  }
200
- return describeBlock.slice(0, describeBlock.length - 2).join('\n'); // Drop trailing ['end', '\n'] from Control block.
216
+ // Return synthesized logic as describe block
217
+ return describeBlock.slice(0, describeBlock.lastIndexOf('end')).join('\n'); // Drop trailing ['end', '\n'] from Control block.
201
218
  }
202
219
  else {
203
220
  return '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mitre/inspec-objects",
3
- "version": "0.0.34",
3
+ "version": "1.0.1",
4
4
  "description": "Typescript objects for normalizing between InSpec profiles and XCCDF benchmarks",
5
5
  "main": "lib/index.js",
6
6
  "publishConfig": {