@mitre/inspec-objects 2.0.5 → 2.1.0

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.
@@ -2,6 +2,7 @@ name: Auto approve and Merge Dependabot PRs
2
2
  on:
3
3
  pull_request_target:
4
4
  types: [labeled]
5
+
5
6
  permissions:
6
7
  pull-requests: write
7
8
  contents: write
@@ -1,19 +1,23 @@
1
1
  name: Build and Pack TS-InSpec-Objects
2
+
2
3
  on:
3
4
  push:
4
5
  branches: [ main ]
5
6
  pull_request:
6
7
 
8
+ permissions:
9
+ contents: read
10
+
7
11
  jobs:
8
12
  build:
9
13
  name: Build and Pack TS-InSpec-Objects
10
14
  runs-on: ubuntu-24.04
11
15
 
12
16
  steps:
13
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v5
14
18
 
15
19
  - name: Setup Node.js
16
- uses: actions/setup-node@v4
20
+ uses: actions/setup-node@v6
17
21
  with:
18
22
  node-version: 22
19
23
  cache: 'npm'
@@ -2,10 +2,13 @@ name: Draft Release
2
2
 
3
3
  on:
4
4
  push:
5
- # branches to consider in the event; optional, defaults to all
6
5
  branches:
7
6
  - main
8
7
 
8
+ permissions:
9
+ contents: write
10
+ pull-requests: read
11
+
9
12
  jobs:
10
13
  update_draft_release:
11
14
  runs-on: ubuntu-24.04
@@ -1,19 +1,23 @@
1
1
  name: Run TS-InSpec-Objects E2E Tests
2
+
2
3
  on:
3
4
  push:
4
5
  branches: [ main ]
5
6
  pull_request:
6
7
 
8
+ permissions:
9
+ contents: read
10
+
7
11
  jobs:
8
12
  build:
9
13
  name: Run TS-InSpec-Objects E2E Tests
10
14
  runs-on: ubuntu-24.04
11
15
 
12
16
  steps:
13
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v5
14
18
 
15
19
  - name: Setup Node.js
16
- uses: actions/setup-node@v4
20
+ uses: actions/setup-node@v6
17
21
  with:
18
22
  node-version: 22
19
23
  cache: 'npm'
@@ -22,4 +26,4 @@ jobs:
22
26
  run: npm ci
23
27
 
24
28
  - name: Run e2e tests
25
- run: npm test
29
+ run: npm run test:ci
@@ -1,9 +1,13 @@
1
1
  name: Lint TS-InSpec-Objects
2
+
2
3
  on:
3
4
  push:
4
5
  branches: [ main ]
5
6
  pull_request:
6
7
 
8
+ permissions:
9
+ contents: read
10
+
7
11
  jobs:
8
12
  build:
9
13
  name: Lint TS-InSpec-Objects
@@ -11,10 +15,10 @@ jobs:
11
15
 
12
16
  steps:
13
17
  - name: Checkout code
14
- uses: actions/checkout@v4
18
+ uses: actions/checkout@v5
15
19
 
16
20
  - name: Setup Node.js
17
- uses: actions/setup-node@v4
21
+ uses: actions/setup-node@v6
18
22
  with:
19
23
  node-version: 22
20
24
  cache: 'npm'
@@ -1,17 +1,22 @@
1
1
  name: Build and Release NPM to GPR (GitHub Package Registry)
2
+
2
3
  on:
3
4
  release:
4
5
  types: [published]
5
6
  workflow_dispatch:
6
7
 
8
+ permissions:
9
+ contents: read
10
+ packages: write
11
+
7
12
  jobs:
8
13
  build-deploy:
9
14
  runs-on: ubuntu-24.04
10
15
  steps:
11
- - uses: actions/checkout@v4
16
+ - uses: actions/checkout@v5
12
17
 
13
18
  - name: Setup node
14
- uses: actions/setup-node@v4
19
+ uses: actions/setup-node@v6
15
20
  with:
16
21
  node-version: 22
17
22
  registry-url: 'https://npm.pkg.github.com'
@@ -1,21 +1,29 @@
1
1
  name: Push @mitre/inspec-objects to NPM
2
+
2
3
  on:
3
4
  release:
4
5
  types: [published]
5
6
  workflow_dispatch:
6
7
 
8
+ permissions:
9
+ id-token: write # required for trusted publishing's use of OIDC
10
+ contents: read
11
+
7
12
  jobs:
8
13
  build-deploy:
9
14
  runs-on: ubuntu-24.04
10
15
  steps:
11
- - uses: actions/checkout@v4
16
+ - uses: actions/checkout@v5
12
17
 
13
18
  - name: setup node
14
- uses: actions/setup-node@v4
19
+ uses: actions/setup-node@v6
15
20
  with:
16
21
  node-version: 22
17
22
  registry-url: 'https://registry.npmjs.org'
18
23
 
24
+ - name: Manually update npm to 11.5.1 or later which is the required version for trusted publishing to work # this step can be removed after the ubuntu runner has the correct minimum version of npm installed
25
+ run: npm install -g npm@latest
26
+
19
27
  - name: Install project dependencies
20
28
  run: npm ci
21
29
 
@@ -30,5 +38,3 @@ jobs:
30
38
 
31
39
  - name: Publish inspec-objects to NPM
32
40
  run: npm publish --access public mitre-inspec-objects-*.tgz
33
- env:
34
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,41 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import js from '@eslint/js';
3
+ import stylistic from '@stylistic/eslint-plugin';
4
+ import tseslint from 'typescript-eslint';
5
+ import unicorn from 'eslint-plugin-unicorn';
6
+ import n from 'eslint-plugin-n';
7
+
8
+ export default defineConfig([
9
+ {
10
+ ignores: ['node_modules/**', 'lib/**'],
11
+ },
12
+ js.configs.recommended,
13
+ ...tseslint.configs.recommended,
14
+ unicorn.configs.recommended,
15
+ stylistic.configs.customize({
16
+ braceStyle: '1tbs',
17
+ indent: [2, { SwitchCase: 1 }],
18
+ semi: true,
19
+ quoteProps: 'as-needed',
20
+ quotes: 'single',
21
+ }),
22
+ {
23
+ languageOptions: {
24
+ parser: tseslint.parser,
25
+ ecmaVersion: 'latest',
26
+ sourceType: 'module',
27
+ },
28
+ plugins: {
29
+ '@typescript-eslint': tseslint.plugin,
30
+ unicorn,
31
+ n,
32
+ },
33
+ rules: {
34
+ '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
35
+ '@typescript-eslint/no-explicit-any': 'off',
36
+ 'unicorn/no-null': 'off',
37
+ 'unicorn/prefer-node-protocol': 'off',
38
+ 'unicorn/prevent-abbreviations': 'off',
39
+ },
40
+ },
41
+ ]);
package/lib/index.d.ts CHANGED
@@ -3,7 +3,5 @@ export * from './parsers/oval';
3
3
  export * from './parsers/xccdf';
4
4
  export * from './utilities/diff';
5
5
  export * from './utilities/update';
6
- import Control from './objects/control';
7
- import Profile from './objects/profile';
8
- export { Control };
9
- export { Profile };
6
+ export { default as Control } from './objects/control';
7
+ export { default as Profile } from './objects/profile';
package/lib/index.js CHANGED
@@ -7,7 +7,7 @@ tslib_1.__exportStar(require("./parsers/oval"), exports);
7
7
  tslib_1.__exportStar(require("./parsers/xccdf"), exports);
8
8
  tslib_1.__exportStar(require("./utilities/diff"), exports);
9
9
  tslib_1.__exportStar(require("./utilities/update"), exports);
10
- const control_1 = tslib_1.__importDefault(require("./objects/control"));
11
- exports.Control = control_1.default;
12
- const profile_1 = tslib_1.__importDefault(require("./objects/profile"));
13
- exports.Profile = profile_1.default;
10
+ var control_1 = require("./objects/control");
11
+ Object.defineProperty(exports, "Control", { enumerable: true, get: function () { return tslib_1.__importDefault(control_1).default; } });
12
+ var profile_1 = require("./objects/profile");
13
+ Object.defineProperty(exports, "Profile", { enumerable: true, get: function () { return tslib_1.__importDefault(profile_1).default; } });
@@ -5099,5 +5099,5 @@ exports.data = {
5099
5099
  'CCI-005144': 'SR-12',
5100
5100
  'CCI-005145': 'SR-12',
5101
5101
  'CCI-005146': 'SR-12',
5102
- 'CCI-005147': 'AT-2 a 1'
5102
+ 'CCI-005147': 'AT-2 a 1',
5103
5103
  };
@@ -17,9 +17,9 @@ const logging_1 = require("../utilities/logging");
17
17
  function objectifyDescriptions(descs) {
18
18
  if (Array.isArray(descs)) {
19
19
  const descriptions = {};
20
- descs.forEach((description) => {
20
+ for (const description of descs) {
21
21
  descriptions[description.label] = description.data;
22
- });
22
+ }
23
23
  return descriptions;
24
24
  }
25
25
  return descs || {};
@@ -54,13 +54,12 @@ class Control {
54
54
  * If provided, the properties of the data object will be assigned to the instance.
55
55
  */
56
56
  constructor(data) {
57
- this.tags = {};
58
57
  this.refs = [];
59
58
  this.tags = {};
60
59
  if (data) {
61
- Object.entries(lodash_1.default.cloneDeep(data)).forEach(([key, value]) => {
60
+ for (const [key, value] of Object.entries(structuredClone(data))) {
62
61
  lodash_1.default.set(this, key, value);
63
- });
62
+ }
64
63
  }
65
64
  }
66
65
  /**
@@ -100,11 +99,11 @@ class Control {
100
99
  result += ` desc "${this.desc}"\n`;
101
100
  }
102
101
  if (this.descs) {
103
- Object.entries(this.descs).forEach(([key, subDesc]) => {
102
+ for (const [key, subDesc] of Object.entries(this.descs)) {
104
103
  if (subDesc) {
105
104
  result += ` desc '${key}', "${subDesc}"\n`;
106
105
  }
107
- });
106
+ }
108
107
  }
109
108
  if (this.impact) {
110
109
  result += ` impact ${this.impact}\n`;
@@ -113,34 +112,18 @@ class Control {
113
112
  result += ` impact ${this.impact.toFixed(1)}\n`;
114
113
  }
115
114
  if (this.refs) {
116
- this.refs.forEach((ref) => {
117
- var _a;
118
- if (typeof ref === 'string') {
119
- result += ` ref "${ref}"\n`;
120
- }
121
- else {
122
- result += ` ref ${((_a = ref.ref) === null || _a === void 0 ? void 0 : _a.toString()) || ''}, url: ${ref.url || ''}`;
123
- }
124
- });
115
+ for (const ref of this.refs) {
116
+ result += typeof ref === 'string' ? ` ref "${ref}"\n` : ` ref ${ref.ref?.toString() || ''}, url: ${ref.url || ''}`;
117
+ }
125
118
  }
126
- Object.entries(this.tags).forEach(([tag, value]) => {
119
+ for (const [tag, value] of Object.entries(this.tags)) {
127
120
  if (typeof value === 'object') {
128
- if (Array.isArray(value) && typeof value[0] === 'string') {
129
- result += ` tag ${tag}: ${JSON.stringify(value)}\n`;
130
- }
131
- else {
132
- result += ` tag '${tag}': ${(value == null ? 'nil' : value)}\n`;
133
- }
121
+ result += Array.isArray(value) && typeof value[0] === 'string' ? ` tag ${tag}: ${JSON.stringify(value)}\n` : ` tag '${tag}': ${(value == undefined ? 'nil' : value)}\n`;
134
122
  }
135
123
  else if (typeof value === 'string') {
136
- if (value.includes('"')) {
137
- result += ` tag "${tag}": "${value}"\n`;
138
- }
139
- else {
140
- result += ` tag '${tag}': '${value}'\n`;
141
- }
124
+ result += value.includes('"') ? ` tag "${tag}": "${value}"\n` : ` tag '${tag}': '${value}'\n`;
142
125
  }
143
- });
126
+ }
144
127
  if (this.describe) {
145
128
  result += '\n';
146
129
  result += this.describe;
@@ -176,57 +159,47 @@ class Control {
176
159
  if (this.title) {
177
160
  result += ` title ${(0, global_1.escapeQuotes)(this.title)}\n`;
178
161
  }
179
- else {
180
- if (verbose) {
181
- logger.error(`${this.id} does not have a title`);
182
- }
162
+ else if (verbose) {
163
+ logger.error(`${this.id} does not have a title`);
183
164
  }
184
165
  // This is the known 'default' description - on previous version this content was repeated on descriptions processed by "descs"
185
166
  if (this.desc) {
186
167
  result += ` desc ${(0, global_1.escapeQuotes)(this.desc)}\n`;
187
168
  }
188
- else {
189
- if (verbose) {
190
- logger.error(`${this.id} does not have a desc`);
191
- }
169
+ else if (verbose) {
170
+ logger.error(`${this.id} does not have a desc`);
192
171
  }
193
172
  if (this.descs) {
194
- Object.entries(this.descs).forEach(([key, subDesc]) => {
173
+ for (const [key, subDesc] of Object.entries(this.descs)) {
195
174
  if (subDesc) {
196
175
  if (key.match('default') && this.desc) {
197
- if (subDesc != this.desc) {
198
- // The "default" keyword may have the same content as the desc content for backward compatibility with different historical InSpec versions.
199
- // In that case, we can ignore writing the "default" subdescription field.
200
- // If they are different, however, someone may be trying to use the keyword "default" for a unique subdescription, which should not be done.
201
- if (verbose) {
202
- 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.`);
203
- }
176
+ // The "default" keyword may have the same content as the desc content for backward compatibility with different historical InSpec versions.
177
+ // In that case, we can ignore writing the "default" subdescription field.
178
+ // If they are different, however, someone may be trying to use the keyword "default" for a unique subdescription, which should not be done.
179
+ if (subDesc != this.desc && verbose) {
180
+ 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.`);
204
181
  }
205
182
  }
206
183
  else {
207
184
  result += ` desc '${key}', ${(0, global_1.escapeQuotes)(subDesc)}\n`;
208
185
  }
209
186
  }
210
- else {
211
- if (verbose) {
212
- logger.warn(`${this.id} does not have a desc for the value ${key}`);
213
- }
187
+ else if (verbose) {
188
+ logger.warn(`${this.id} does not have a desc for the value ${key}`);
214
189
  }
215
- });
190
+ }
216
191
  }
217
192
  if (this.impact !== undefined) {
218
193
  result += ` impact ${(this.impact <= 0 ? this.impact.toFixed(1) : this.impact)}\n`;
219
194
  }
220
- else {
221
- if (verbose) {
222
- logger.error(`${this.id} does not have an impact`);
223
- }
195
+ else if (verbose) {
196
+ logger.error(`${this.id} does not have an impact`);
224
197
  }
225
- //-------------------------------------------------------------------------
198
+ // -------------------------------------------------------------------------
226
199
  // This may not be necessary, leaving commented code for posterity. Once we
227
200
  // have implemented the process and determined that there isn't any side
228
201
  // effects we can remove the commented code
229
- //-------------------------------------------------------------------------
202
+ // -------------------------------------------------------------------------
230
203
  // if (this.refs) {
231
204
  // this.refs.forEach((ref) => {
232
205
  // if (typeof ref === 'string') {
@@ -236,25 +209,25 @@ class Control {
236
209
  // }
237
210
  // });
238
211
  // }
239
- Object.entries(this.tags).forEach(([tag, value]) => {
212
+ for (const [tag, value] of Object.entries(this.tags)) {
240
213
  if (value) {
241
214
  if (typeof value === 'object') {
242
215
  if (Array.isArray(value) && typeof value[0] === 'string') {
243
216
  // The goal is to keep the style similar to cookstyle formatting
244
217
  result += ` tag ${tag}: ${JSON.stringify(value)
245
- .replace(/"/g, "'") // replace the double quotes with single quotes, ex: ["V-72029","SV-86653"] -> ['V-72029','SV-86653']
218
+ .replaceAll('"', "'") // replace the double quotes with single quotes, ex: ["V-72029","SV-86653"] -> ['V-72029','SV-86653']
246
219
  .split("','") // split the items in the string
247
220
  .join("', '")}\n`; // join them together using single quote and a space, ex: ['V-72029','SV-86653'] -> ['V-72029', 'SV-86653']
248
221
  }
249
222
  else {
250
223
  // Convert JSON Object to Ruby Hash
251
224
  const stringifiedObject = JSON.stringify(value, null, 2)
252
- .replace(/\n/g, '\n ')
253
- .replace(/\{\n {6}/g, '{')
254
- .replace(/\[\n {8}/g, '[')
255
- .replace(/\n {6}\]/g, ']')
256
- .replace(/\n {4}\}/g, '}')
257
- .replace(/": \[/g, '" => [');
225
+ .replaceAll('\n', '\n ')
226
+ .replaceAll(/\{\n {6}/g, '{')
227
+ .replaceAll(/\[\n {8}/g, '[')
228
+ .replaceAll(/\n {6}\]/g, ']')
229
+ .replaceAll(/\n {4}\}/g, '}')
230
+ .replaceAll('": [', '" => [');
258
231
  result += ` tag ${tag}: ${stringifiedObject}\n`;
259
232
  }
260
233
  }
@@ -264,17 +237,12 @@ class Control {
264
237
  }
265
238
  else {
266
239
  const nilTagList = ['severity', 'satisfies'];
267
- if (nilTagList.includes(tag)) {
268
- result += ` tag ${tag}: nil\n`;
269
- }
270
- else {
271
- result += ` tag '${tag}'\n`;
272
- }
240
+ result += nilTagList.includes(tag) ? ` tag ${tag}: nil\n` : ` tag '${tag}'\n`;
273
241
  if (verbose) {
274
242
  logger.info(`${this.id} does not have a value for tag: ${tag}`);
275
243
  }
276
244
  }
277
- });
245
+ }
278
246
  if (this.describe) {
279
247
  result += '\n';
280
248
  result += this.describe;
@@ -53,9 +53,9 @@ class Profile {
53
53
  this.files = [];
54
54
  this.controls = [];
55
55
  if (data) {
56
- Object.entries(data).forEach(([key, value]) => {
56
+ for (const [key, value] of Object.entries(data)) {
57
57
  lodash_1.default.set(this, key, value);
58
- });
58
+ }
59
59
  }
60
60
  }
61
61
  /**
@@ -79,7 +79,7 @@ class Profile {
79
79
  version: this.version,
80
80
  supports: this.supports,
81
81
  depends: this.depends,
82
- //inspec_version: this.inspec_version,
82
+ // inspec_version: this.inspec_version,
83
83
  inspec_version: yaml_1.default.stringify(`${this.inspec_version}`, { defaultStringType: 'QUOTE_DOUBLE' }),
84
84
  });
85
85
  }
@@ -95,12 +95,12 @@ class Profile {
95
95
  */
96
96
  toUnformattedObject() {
97
97
  const unformattedProfile = new Profile(this);
98
- Object.entries(this).forEach(([key, value]) => {
98
+ for (const [key, value] of Object.entries(this)) {
99
99
  if (typeof value === 'string') {
100
100
  lodash_1.default.set(unformattedProfile, key, (0, global_1.unformatText)(value));
101
101
  }
102
- });
103
- unformattedProfile.controls = this.controls.map((control) => control.toUnformattedObject());
102
+ }
103
+ unformattedProfile.controls = this.controls.map(control => control.toUnformattedObject());
104
104
  return unformattedProfile;
105
105
  }
106
106
  }
@@ -29,7 +29,7 @@ function processEvaluation(evaluationInput) {
29
29
  description: lodash_1.default.get(topLevelProfile.data, 'description'),
30
30
  version: topLevelProfile.data.version,
31
31
  });
32
- topLevelProfile.contains.forEach((control) => {
32
+ for (const control of topLevelProfile.contains) {
33
33
  profile.controls.push(new control_1.default({
34
34
  id: control.data.id,
35
35
  title: control.data.title,
@@ -38,7 +38,7 @@ function processEvaluation(evaluationInput) {
38
38
  descs: (0, control_1.objectifyDescriptions)(control.hdf.wraps.descriptions),
39
39
  tags: control.hdf.wraps.tags,
40
40
  }));
41
- });
41
+ }
42
42
  return profile;
43
43
  }
44
44
  /**
@@ -59,7 +59,7 @@ function processProfileJSON(profileInput) {
59
59
  description: lodash_1.default.get(profileInput.data, 'description'),
60
60
  version: profileInput.data.version,
61
61
  });
62
- profileInput.data.controls.forEach((control) => {
62
+ for (const control of profileInput.data.controls) {
63
63
  const newControl = new control_1.default({
64
64
  id: control.id,
65
65
  title: control.title,
@@ -72,17 +72,15 @@ function processProfileJSON(profileInput) {
72
72
  newControl.describe = (0, update_1.getExistingDescribeFromControl)(newControl);
73
73
  // Migrate check and fix text from tags to descriptions
74
74
  if (newControl.tags.check && !newControl.descs.check) {
75
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
76
75
  lodash_1.default.set(newControl.descs, 'check', control.tags.check);
77
76
  lodash_1.default.set(newControl.tags, 'check', undefined);
78
77
  }
79
78
  if (newControl.tags.fix && !newControl.descs.fix) {
80
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
81
79
  lodash_1.default.set(newControl.descs, 'fix', control.tags.fix);
82
80
  lodash_1.default.set(newControl.tags, 'fix', undefined);
83
81
  }
84
82
  profile.controls.push(newControl);
85
- });
83
+ }
86
84
  return profile;
87
85
  }
88
86
  /**
@@ -110,7 +108,7 @@ function processExecJSON(execJSON) {
110
108
  */
111
109
  function processInSpecProfile(json) {
112
110
  const convertedFile = (0, inspecjs_1.convertFile)(json, true);
113
- let profile = new profile_1.default();
111
+ let profile;
114
112
  if (convertedFile['1_0_ExecJson']) {
115
113
  profile = processEvaluation((0, inspecjs_1.contextualizeEvaluation)(convertedFile['1_0_ExecJson'])).toUnformattedObject();
116
114
  }