@mitre/inspec-objects 0.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.
Files changed (36) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +20 -0
  3. package/lib/index.d.ts +5 -0
  4. package/lib/index.js +8 -0
  5. package/lib/objects/control.d.ts +42 -0
  6. package/lib/objects/control.js +88 -0
  7. package/lib/objects/profile.d.ts +50 -0
  8. package/lib/objects/profile.js +48 -0
  9. package/lib/parsers/json.d.ts +6 -0
  10. package/lib/parsers/json.js +80 -0
  11. package/lib/parsers/xccdf.d.ts +2 -0
  12. package/lib/parsers/xccdf.js +73 -0
  13. package/lib/utilities/diff.d.ts +3 -0
  14. package/lib/utilities/diff.js +53 -0
  15. package/lib/utilities/global.d.ts +6 -0
  16. package/lib/utilities/global.js +18 -0
  17. package/lib/utilities/xccdf.d.ts +5 -0
  18. package/lib/utilities/xccdf.js +103 -0
  19. package/mitre-inspec-objects-v0.0.1.tgz +0 -0
  20. package/package-lock.json +11247 -0
  21. package/package.json +53 -0
  22. package/src/index.ts +5 -0
  23. package/src/objects/control.ts +137 -0
  24. package/src/objects/profile.ts +93 -0
  25. package/src/parsers/json.ts +92 -0
  26. package/src/parsers/xccdf.ts +74 -0
  27. package/src/types/diff.d.ts +9 -0
  28. package/src/types/xccdf.d.ts +126 -0
  29. package/src/utilities/diff.ts +54 -0
  30. package/src/utilities/global.ts +23 -0
  31. package/src/utilities/xccdf.ts +110 -0
  32. package/test/sample_data/inspec/profiles/redhat-enterprise-linux-7-stig-baseline/spec/fixtures/kitchen/manifests/site.pp +29 -0
  33. package/test/sample_data/inspec/profiles/redhat-enterprise-linux-7-stig-baseline/spec/fixtures/kitchen/modules/garbage/.gitignore +0 -0
  34. package/test/sample_data/inspec/profiles/redhat-enterprise-linux-7-stig-baseline/spec/results/.gitkeep +0 -0
  35. package/tsconfig.build.json +5 -0
  36. package/tsconfig.json +21 -0
package/LICENSE.md ADDED
@@ -0,0 +1,9 @@
1
+ Licensed under the Apache-2.0 license.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ - Redistributions of source code must retain the above copyright/ digital rights legend, this list of conditions and the following Notice.
6
+
7
+ - Redistributions in binary form must reproduce the above copyright copyright/ digital rights legend, this list of conditions and the following Notice in the documentation and/or other materials provided with the distribution.
8
+
9
+ - Neither the name of The MITRE Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # ts-inspec-objects
2
+ Typescript objects for InSpec profiles
3
+
4
+ ### NOTICE
5
+
6
+ © 2018-2022 The MITRE Corporation.
7
+
8
+ Approved for Public Release; Distribution Unlimited. Case Number 18-3678.
9
+
10
+ ### NOTICE
11
+
12
+ MITRE hereby grants express written permission to use, reproduce, distribute, modify, and otherwise leverage this software to the extent permitted by the licensed terms provided in the LICENSE.md file included with this project.
13
+
14
+ ### NOTICE
15
+
16
+ This software was produced for the U. S. Government under Contract Number HHSM-500-2012-00008I, and is subject to Federal Acquisition Regulation Clause 52.227-14, Rights in Data-General.
17
+
18
+ No other use other than that granted to the U. S. Government, or to those acting on behalf of the U. S. Government under that Clause is authorized without the express written permission of The MITRE Corporation.
19
+
20
+ For further information, please contact The MITRE Corporation, Contracts Management Office, 7515 Colshire Drive, McLean, VA 22102-7539, (703) 983-6000.
package/lib/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from './objects/control';
2
+ export * from './objects/profile';
3
+ export * from './parsers/json';
4
+ export * from './parsers/xccdf';
5
+ export * from './utilities/diff';
package/lib/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./objects/control"), exports);
5
+ tslib_1.__exportStar(require("./objects/profile"), exports);
6
+ tslib_1.__exportStar(require("./parsers/json"), exports);
7
+ tslib_1.__exportStar(require("./parsers/xccdf"), exports);
8
+ tslib_1.__exportStar(require("./utilities/diff"), exports);
@@ -0,0 +1,42 @@
1
+ import { ExecJSON } from "inspecjs";
2
+ export default class Control {
3
+ id?: string | null;
4
+ title?: string | null;
5
+ code?: string | null;
6
+ desc?: string | null;
7
+ descs?: ExecJSON.ControlDescription[] | {
8
+ [key: string]: string;
9
+ } | null;
10
+ impact?: number;
11
+ ref?: string;
12
+ refs?: string[];
13
+ tags: {
14
+ check?: string;
15
+ fix?: string;
16
+ severity?: string;
17
+ gtitle?: string;
18
+ gid?: string;
19
+ satisfies?: string[];
20
+ rid?: string;
21
+ stig_id?: string;
22
+ fix_id?: string;
23
+ cci?: string[];
24
+ cis_controls?: Record<string, string[]>[];
25
+ nist?: string[];
26
+ legacy?: string[];
27
+ false_negatives?: string;
28
+ false_positives?: string;
29
+ documentable?: boolean;
30
+ mitigations?: string;
31
+ severity_override_guidance?: string;
32
+ potential_impacts?: string;
33
+ third_party_tools?: string;
34
+ mitigation_controls?: string;
35
+ responsibility?: string;
36
+ ia_controls?: string;
37
+ [key: string]: string | string[] | Record<string, string[]>[] | boolean | undefined | null;
38
+ };
39
+ constructor(data?: Partial<Control>);
40
+ toUnformattedObject(): Control;
41
+ toRuby(lineLength?: number): string;
42
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
5
+ const flat_1 = require("flat");
6
+ const global_1 = require("../utilities/global");
7
+ class Control {
8
+ constructor(data) {
9
+ this.tags = {};
10
+ if (data) {
11
+ Object.entries(data).forEach(([key, value]) => {
12
+ lodash_1.default.set(this, key, value);
13
+ });
14
+ }
15
+ }
16
+ toUnformattedObject() {
17
+ const flattened = (0, flat_1.flatten)(this);
18
+ Object.entries(flattened).forEach(([key, value]) => {
19
+ if (typeof value === 'string') {
20
+ lodash_1.default.set(flattened, key, (0, global_1.unformatText)(value));
21
+ }
22
+ });
23
+ return new Control((0, flat_1.unflatten)(flattened));
24
+ }
25
+ toRuby(lineLength = 80) {
26
+ let result = "# encoding: UTF-8\n\n";
27
+ result += `control "${this.id}" do\n`;
28
+ if (this.title) {
29
+ result += ` title "${(0, global_1.wrapAndEscapeQuotes)(this.title, lineLength)}"\n`;
30
+ }
31
+ else {
32
+ console.error(`${this.id} does not have a title`);
33
+ }
34
+ if (this.desc) {
35
+ result += ` desc "${(0, global_1.wrapAndEscapeQuotes)(this.desc, lineLength)}"\n`;
36
+ }
37
+ else {
38
+ console.error(`${this.id} does not have a desc`);
39
+ }
40
+ if (this.descs) {
41
+ Object.entries(this.descs).forEach(([key, desc]) => {
42
+ if (desc) {
43
+ result += ` desc "${key}", "${(0, global_1.wrapAndEscapeQuotes)(desc, lineLength)}"\n`;
44
+ }
45
+ else {
46
+ console.error(`${this.id} does not have a desc for the value ${key}`);
47
+ }
48
+ });
49
+ }
50
+ if (this.impact) {
51
+ result += ` impact ${this.impact}\n`;
52
+ }
53
+ else {
54
+ console.error(`${this.id} does not have an impact`);
55
+ }
56
+ if (this.refs) {
57
+ this.refs.forEach((ref) => {
58
+ result += ` ref '${(0, global_1.escapeQuotes)(ref)}'\n`;
59
+ });
60
+ }
61
+ Object.entries(this.tags).forEach(([tag, value]) => {
62
+ if (value) {
63
+ if (typeof value === "object") {
64
+ if (Array.isArray(value) && typeof value[0] === "string") {
65
+ result += ` tag ${tag}: ${JSON.stringify(value)}\n`;
66
+ }
67
+ else {
68
+ // Convert JSON Object to Ruby Hash
69
+ const stringifiedObject = JSON.stringify(value, null, 2)
70
+ .replace(/\n/g, "\n ")
71
+ .replace(/\{\n {6}/g, "{")
72
+ .replace(/\[\n {8}/g, "[")
73
+ .replace(/\n {6}\]/g, "]")
74
+ .replace(/\n {4}\}/g, "}")
75
+ .replace(/": \[/g, '" => [');
76
+ result += ` tag ${tag}: ${stringifiedObject}\n`;
77
+ }
78
+ }
79
+ else if (typeof value === "string") {
80
+ result += ` tag ${tag}: "${(0, global_1.wrapAndEscapeQuotes)(value, lineLength)}"\n`;
81
+ }
82
+ }
83
+ });
84
+ result += "end";
85
+ return result;
86
+ }
87
+ }
88
+ exports.default = Control;
@@ -0,0 +1,50 @@
1
+ import Control from "./control";
2
+ export default class Profile {
3
+ name?: string | null;
4
+ title?: string | null;
5
+ maintainer?: string | null;
6
+ copyright?: string | null;
7
+ copyright_email?: string | null;
8
+ license?: string | null;
9
+ summary?: string | null;
10
+ description?: string | null;
11
+ version?: string | null;
12
+ inspec_version?: string | null;
13
+ supports: {
14
+ "platform-family"?: string;
15
+ "platform-name"?: string;
16
+ "os-name"?: string;
17
+ "os-family"?: string;
18
+ release?: string;
19
+ platform?: string;
20
+ }[];
21
+ depends: {
22
+ name: string;
23
+ path?: string;
24
+ url?: string;
25
+ username?: string;
26
+ password?: string;
27
+ git?: string;
28
+ branch?: string;
29
+ tag?: string;
30
+ commit?: string;
31
+ version?: string;
32
+ relative_path?: string;
33
+ supermarket?: string;
34
+ compliance?: string;
35
+ }[];
36
+ inputs: {
37
+ [key: string]: string;
38
+ }[];
39
+ gem_dependencies?: {
40
+ name: string;
41
+ version: string;
42
+ }[];
43
+ libraries: string[];
44
+ readme?: string | null;
45
+ files: string[];
46
+ controls: Control[];
47
+ constructor(data?: Omit<Partial<Profile>, "controls">);
48
+ createInspecYaml(): string;
49
+ toUnformattedObject(): Profile;
50
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const yaml_1 = tslib_1.__importDefault(require("yaml"));
5
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
+ const global_1 = require("../utilities/global");
7
+ class Profile {
8
+ constructor(data) {
9
+ this.supports = [];
10
+ this.depends = [];
11
+ this.inputs = [];
12
+ this.libraries = [];
13
+ this.files = [];
14
+ this.controls = [];
15
+ if (data) {
16
+ Object.entries(data).forEach(([key, value]) => {
17
+ lodash_1.default.set(this, key, value);
18
+ });
19
+ }
20
+ }
21
+ createInspecYaml() {
22
+ return yaml_1.default.stringify({
23
+ name: this.name,
24
+ title: this.title,
25
+ maintainer: this.maintainer,
26
+ copyright: this.copyright,
27
+ copyright_email: this.copyright_email,
28
+ license: this.license,
29
+ summary: this.summary,
30
+ description: this.description,
31
+ version: this.version,
32
+ supports: this.supports,
33
+ depends: this.depends,
34
+ inspec_version: this.inspec_version,
35
+ });
36
+ }
37
+ toUnformattedObject() {
38
+ const unformattedProfile = new Profile(this);
39
+ Object.entries(this).forEach(([key, value]) => {
40
+ if (typeof value === "string") {
41
+ lodash_1.default.set(unformattedProfile, key, (0, global_1.unformatText)(value));
42
+ }
43
+ });
44
+ unformattedProfile.controls = this.controls.map((control) => control.toUnformattedObject());
45
+ return unformattedProfile;
46
+ }
47
+ }
48
+ exports.default = Profile;
@@ -0,0 +1,6 @@
1
+ import { ContextualizedEvaluation, ContextualizedProfile, ExecJSON } from "inspecjs";
2
+ import Profile from "../objects/profile";
3
+ export declare function processEvaluation(evaluationInput: ContextualizedEvaluation): Profile;
4
+ export declare function processProfileJSON(profileInput: ContextualizedProfile): Profile;
5
+ export declare function processExecJSON(execJSON: ExecJSON.Execution): Profile;
6
+ export declare function processJSON(json: string): Profile;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processJSON = exports.processExecJSON = exports.processProfileJSON = exports.processEvaluation = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const inspecjs_1 = require("inspecjs");
6
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
+ const control_1 = tslib_1.__importDefault(require("../objects/control"));
8
+ const profile_1 = tslib_1.__importDefault(require("../objects/profile"));
9
+ function processEvaluation(evaluationInput) {
10
+ const topLevelProfile = evaluationInput.contains[0];
11
+ const profile = new profile_1.default({
12
+ name: topLevelProfile.data.name,
13
+ title: topLevelProfile.data.title,
14
+ maintainer: topLevelProfile.data.maintainer,
15
+ copyright: topLevelProfile.data.copyright,
16
+ copyright_email: topLevelProfile.data.copyright_email,
17
+ license: lodash_1.default.get(topLevelProfile.data, "license"),
18
+ summary: lodash_1.default.get(topLevelProfile.data, "summary"),
19
+ description: lodash_1.default.get(topLevelProfile.data, "description"),
20
+ version: topLevelProfile.data.version,
21
+ });
22
+ topLevelProfile.contains.forEach((control) => {
23
+ profile.controls.push(new control_1.default({
24
+ id: control.data.id,
25
+ title: control.data.title,
26
+ impact: control.data.impact,
27
+ desc: control.data.desc,
28
+ descs: control.hdf.wraps.descriptions,
29
+ tags: control.hdf.wraps.tags,
30
+ }));
31
+ });
32
+ return profile;
33
+ }
34
+ exports.processEvaluation = processEvaluation;
35
+ function processProfileJSON(profileInput) {
36
+ const profile = new profile_1.default({
37
+ name: profileInput.data.name,
38
+ title: profileInput.data.title,
39
+ maintainer: profileInput.data.maintainer,
40
+ copyright: profileInput.data.copyright,
41
+ copyright_email: profileInput.data.copyright_email,
42
+ license: lodash_1.default.get(profileInput.data, "license"),
43
+ summary: lodash_1.default.get(profileInput.data, "summary"),
44
+ description: lodash_1.default.get(profileInput.data, "description"),
45
+ version: profileInput.data.version,
46
+ });
47
+ profileInput.data.controls.forEach((control) => {
48
+ profile.controls.push(new control_1.default({
49
+ id: control.id,
50
+ title: control.title,
51
+ desc: control.desc,
52
+ impact: control.impact,
53
+ code: control.code,
54
+ tags: control.tags,
55
+ descs: control.descriptions,
56
+ }));
57
+ });
58
+ return profile;
59
+ }
60
+ exports.processProfileJSON = processProfileJSON;
61
+ function processExecJSON(execJSON) {
62
+ return processEvaluation((0, inspecjs_1.contextualizeEvaluation)(execJSON));
63
+ }
64
+ exports.processExecJSON = processExecJSON;
65
+ function processJSON(json) {
66
+ const convertedFile = (0, inspecjs_1.convertFile)(json, true);
67
+ let profile = new profile_1.default();
68
+ if (convertedFile["1_0_ExecJson"]) {
69
+ profile = processEvaluation((0, inspecjs_1.contextualizeEvaluation)(convertedFile["1_0_ExecJson"])).toUnformattedObject();
70
+ }
71
+ else if (convertedFile["1_0_ProfileJson"]) {
72
+ profile = processProfileJSON((0, inspecjs_1.contextualizeProfile)(JSON.parse(json))).toUnformattedObject();
73
+ }
74
+ else {
75
+ throw new Error("Unknown file type passed");
76
+ }
77
+ profile.controls = lodash_1.default.sortBy(profile.controls, "id");
78
+ return profile;
79
+ }
80
+ exports.processJSON = processJSON;
@@ -0,0 +1,2 @@
1
+ import Profile from '../objects/profile';
2
+ export declare function processXCCDF(xml: string): Profile;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processXCCDF = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const CciNistMappingData_1 = require("@mitre/hdf-converters/lib/src/mappings/CciNistMappingData");
6
+ const profile_1 = tslib_1.__importDefault(require("../objects/profile"));
7
+ const xccdf_1 = require("../utilities/xccdf");
8
+ const control_1 = tslib_1.__importDefault(require("../objects/control"));
9
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
10
+ function processXCCDF(xml) {
11
+ const parsedXML = (0, xccdf_1.convertEncodedXmlIntoJson)(xml);
12
+ const groups = parsedXML.Benchmark.Group;
13
+ const profile = new profile_1.default({
14
+ name: parsedXML.Benchmark['@_id'],
15
+ title: parsedXML.Benchmark.title,
16
+ summary: parsedXML.Benchmark.description
17
+ });
18
+ groups.forEach(group => {
19
+ var _a, _b, _c;
20
+ const extractedDescription = (0, xccdf_1.convertEncodedHTMLIntoJson)((_a = group.Rule) === null || _a === void 0 ? void 0 : _a.description);
21
+ const control = new control_1.default({
22
+ id: group['@_id'],
23
+ title: group.Rule['@_severity'] ? group.Rule.title : `[[[MISSING SEVERITY FROM STIG]]] ${group.Rule.title}`,
24
+ desc: (_b = extractedDescription.VulnDiscussion) === null || _b === void 0 ? void 0 : _b.split('Satisfies: ')[0],
25
+ impact: (0, xccdf_1.severityStringToImpact)(group.Rule['@_severity'] || 'critical'),
26
+ descs: {
27
+ check: group.Rule.check['check-content'],
28
+ fix: group.Rule.fixtext['#text']
29
+ },
30
+ tags: lodash_1.default.omitBy({
31
+ severity: (0, xccdf_1.impactNumberToSeverityString)((0, xccdf_1.severityStringToImpact)(group.Rule['@_severity'] || 'critical')),
32
+ gtitle: group.title,
33
+ satisfies: ((_c = extractedDescription.VulnDiscussion) === null || _c === void 0 ? void 0 : _c.includes('Satisfies: ')) && extractedDescription.VulnDiscussion.split('Satisfies: ').length >= 1 ? extractedDescription.VulnDiscussion.split('Satisfies: ')[1].split(',').map(satisfaction => satisfaction.trim()) : undefined,
34
+ gid: group['@_id'],
35
+ rid: group.Rule['@_id'],
36
+ stig_id: group.Rule.version,
37
+ fix_id: group.Rule.fix['@_id'],
38
+ false_negatives: extractedDescription.FalseNegatives,
39
+ false_positives: extractedDescription.FalsePositives,
40
+ documentable: extractedDescription.Documentable,
41
+ mitigations: extractedDescription.Mitigations,
42
+ severity_override_guidance: extractedDescription.SeverityOverrideGuidance,
43
+ potential_impacts: extractedDescription.PotentialImpacts,
44
+ third_party_tools: extractedDescription.ThirdPartyTools,
45
+ mitigation_control: extractedDescription.MitigationControl,
46
+ mitigation_controls: extractedDescription.MitigationControls,
47
+ responsibility: extractedDescription.Responsibility,
48
+ ia_controls: extractedDescription.IAControls
49
+ }, i => !Boolean(i))
50
+ });
51
+ if ('ident' in group.Rule) {
52
+ const identifiers = Array.isArray(group.Rule.ident) ? group.Rule.ident : [group.Rule.ident];
53
+ // Grab CCI/NIST/Legacy identifiers
54
+ identifiers.forEach(identifier => {
55
+ var _a, _b, _c;
56
+ const identifierText = identifier['#text'];
57
+ if (identifier['@_system'].toLowerCase().endsWith('cci')) {
58
+ (_a = control.tags.cci) === null || _a === void 0 ? void 0 : _a.push(identifierText);
59
+ if (identifierText in CciNistMappingData_1.data) {
60
+ (_b = control.tags.nist) === null || _b === void 0 ? void 0 : _b.push(lodash_1.default.get(CciNistMappingData_1.data, identifierText));
61
+ }
62
+ }
63
+ if (identifier['@_system'].toLowerCase().endsWith('legacy')) {
64
+ (_c = control.tags.legacy) === null || _c === void 0 ? void 0 : _c.push(identifierText);
65
+ }
66
+ });
67
+ }
68
+ profile.controls.push(control);
69
+ });
70
+ profile.controls = lodash_1.default.sortBy(profile.controls, 'id');
71
+ return profile.toUnformattedObject();
72
+ }
73
+ exports.processXCCDF = processXCCDF;
@@ -0,0 +1,3 @@
1
+ import Profile from '../objects/profile';
2
+ import { ProfileDiff } from '../types/diff';
3
+ export declare function diffProfile(fromProfile: Profile, toProfile: Profile): ProfileDiff;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.diffProfile = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const json_diff_1 = require("json-diff");
6
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
+ function diffProfile(fromProfile, toProfile) {
8
+ const profileDiff = {
9
+ addedControlIDs: [],
10
+ removedControlIDs: [],
11
+ changedControls: {}
12
+ };
13
+ const fromControlIDs = fromProfile.controls.map((control) => control.id).sort();
14
+ const toControlIDs = toProfile.controls.map((control) => control.id).sort();
15
+ // Find new controls
16
+ const controlIDDiff = (0, json_diff_1.diff)(fromControlIDs, toControlIDs);
17
+ controlIDDiff.forEach((diffValue) => {
18
+ if (diffValue[0] === '-') {
19
+ profileDiff.removedControlIDs.push(diffValue[1]);
20
+ }
21
+ else if (diffValue[0] === '+') {
22
+ profileDiff.addedControlIDs.push(diffValue[1]);
23
+ }
24
+ });
25
+ // Add new controls to changedControls
26
+ profileDiff.addedControlIDs.forEach((addedControl) => {
27
+ const newControl = toProfile.controls.find((control) => addedControl === control.id);
28
+ if (newControl) {
29
+ profileDiff.changedControls[addedControl] = newControl;
30
+ }
31
+ });
32
+ // Find changed controls
33
+ for (const fromControl of fromProfile.controls) {
34
+ const toControl = toProfile.controls.find((control) => control.id === fromControl.id);
35
+ if (toControl) {
36
+ const controlDiff = (0, json_diff_1.diff)(fromControl, toControl);
37
+ if (controlDiff) {
38
+ Object.entries(controlDiff).forEach(([key, value]) => {
39
+ if (lodash_1.default.has(value, '__new')) {
40
+ lodash_1.default.set(profileDiff, 'changedControls.' + fromControl.id + '.' + key.replace('.', '\\.'), lodash_1.default.get(controlDiff, key + '.__new'));
41
+ }
42
+ else if (typeof value === 'object') {
43
+ Object.entries(value).forEach(([subKey, subValue]) => {
44
+ lodash_1.default.set(profileDiff, 'changedControls.' + fromControl.id + '.' + key.replace('.', '\\.') + '.' + subKey.replace('.', '\\.'), lodash_1.default.get(controlDiff, key + '.' + subKey + '.__new'));
45
+ });
46
+ }
47
+ });
48
+ }
49
+ }
50
+ }
51
+ return profileDiff;
52
+ }
53
+ exports.diffProfile = diffProfile;
@@ -0,0 +1,6 @@
1
+ export declare function wrap(s: string, lineLength?: number): string;
2
+ export declare function unformatText(s: string): string;
3
+ declare const escapeQuotes: (s: string) => string;
4
+ declare const escapeDoubleQuotes: (s: string) => string;
5
+ declare const wrapAndEscapeQuotes: (s: string, lineLength?: number) => string;
6
+ export { escapeQuotes, escapeDoubleQuotes, wrapAndEscapeQuotes };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wrapAndEscapeQuotes = exports.escapeDoubleQuotes = exports.escapeQuotes = exports.unformatText = exports.wrap = void 0;
4
+ // Breaks lines down to lineLength number of characters
5
+ function wrap(s, lineLength = 80) {
6
+ return s.replace(new RegExp(`(?![^\n]{1,${lineLength}}$)([^\n]{1,${lineLength}})`, "g"), "$1\n");
7
+ }
8
+ exports.wrap = wrap;
9
+ function unformatText(s) {
10
+ return s.replace(/\n/g, ' ').replace(/\\n/g, ' ').replace(/( +|\t)/g, ' ');
11
+ }
12
+ exports.unformatText = unformatText;
13
+ const escapeQuotes = (s) => s.replace(/\\/g, "\\\\").replace(/'/g, "\\'"); // Escape backslashes and quotes
14
+ exports.escapeQuotes = escapeQuotes;
15
+ const escapeDoubleQuotes = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); // Escape backslashes and double quotes
16
+ exports.escapeDoubleQuotes = escapeDoubleQuotes;
17
+ const wrapAndEscapeQuotes = (s, lineLength) => escapeDoubleQuotes(wrap(s, lineLength)); // Escape backslashes and quotes, and wrap long lines
18
+ exports.wrapAndEscapeQuotes = wrapAndEscapeQuotes;
@@ -0,0 +1,5 @@
1
+ import { DecodedDescription } from '../types/xccdf';
2
+ export declare function convertEncodedXmlIntoJson(encodedXml: string): any;
3
+ export declare function severityStringToImpact(string: string): number;
4
+ export declare function impactNumberToSeverityString(impact: number): string;
5
+ export declare function convertEncodedHTMLIntoJson(encodedHTML?: string): DecodedDescription;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertEncodedHTMLIntoJson = exports.impactNumberToSeverityString = exports.severityStringToImpact = exports.convertEncodedXmlIntoJson = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fast_xml_parser_1 = tslib_1.__importDefault(require("fast-xml-parser"));
6
+ const htmlparser = tslib_1.__importStar(require("htmlparser2"));
7
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
8
+ function convertEncodedXmlIntoJson(encodedXml) {
9
+ return fast_xml_parser_1.default.parse(encodedXml, {
10
+ ignoreAttributes: false,
11
+ attributeNamePrefix: '@_',
12
+ });
13
+ }
14
+ exports.convertEncodedXmlIntoJson = convertEncodedXmlIntoJson;
15
+ function severityStringToImpact(string) {
16
+ var _a, _b, _c, _d, _e;
17
+ if ((_a = string.match(/none|na|n\/a|not[\s()*_|]?applicable/i)) === null || _a === void 0 ? void 0 : _a.length) {
18
+ return 0.0;
19
+ }
20
+ if ((_b = string.match(/low|cat(egory)?\s*(iii|3)/i)) === null || _b === void 0 ? void 0 : _b.length) {
21
+ return 0.3;
22
+ }
23
+ if ((_c = string.match(/med(ium)?|cat(egory)?\s*(ii|2)/)) === null || _c === void 0 ? void 0 : _c.length) {
24
+ return 0.5;
25
+ }
26
+ if ((_d = string.match(/high|cat(egory)?\s*(i|1)/)) === null || _d === void 0 ? void 0 : _d.length) {
27
+ return 0.7;
28
+ }
29
+ if ((_e = string.match(/crit(ical)?|severe/)) === null || _e === void 0 ? void 0 : _e.length) {
30
+ return 1.0;
31
+ }
32
+ throw new Error(`${string}' is not a valid severity value. It should be one of the approved keywords`);
33
+ }
34
+ exports.severityStringToImpact = severityStringToImpact;
35
+ function impactNumberToSeverityString(impact) {
36
+ // Impact must be 0.0 - 1.0
37
+ if (impact < 0.0 || impact > 1.0) {
38
+ throw new Error('Impact cannot be less than 0.0 or greater than 1.0');
39
+ }
40
+ else {
41
+ if (impact >= 0.9) {
42
+ return 'critical';
43
+ }
44
+ if (impact >= 0.7) {
45
+ return 'high';
46
+ }
47
+ if (impact >= 0.4) {
48
+ return 'medium';
49
+ }
50
+ if (impact >= 0.1) {
51
+ return 'low';
52
+ }
53
+ return 'none';
54
+ }
55
+ }
56
+ exports.impactNumberToSeverityString = impactNumberToSeverityString;
57
+ function convertEncodedHTMLIntoJson(encodedHTML) {
58
+ if (encodedHTML) {
59
+ // Some STIGs regarding XSS put the < character inside of the description which breaks parsing
60
+ const patchedHTML = encodedHTML.replace(/"&lt;"/g, '[[[REPLACE_LESS_THAN]]]');
61
+ const xmlChunks = [];
62
+ const htmlParser = new htmlparser.Parser({
63
+ ontext(text) {
64
+ xmlChunks.push(text);
65
+ },
66
+ });
67
+ htmlParser.write(patchedHTML);
68
+ htmlParser.end();
69
+ const converted = convertEncodedXmlIntoJson(xmlChunks.join(''));
70
+ let cleaned = {};
71
+ if (typeof converted.VulnDiscussion === 'object') { // Some STIGs have xml tags inside of the actual text which breaks processing, e.g U_ASD_STIG_V5R1_Manual-xccdf.xml and all Oracle Database STIGs
72
+ let extractedVulnDescription = '';
73
+ const remainingFields = lodash_1.default.omit(converted.VulnDiscussion, ['FalsePositives', 'FalseNegatives', 'Documentable', 'Mitigations', 'SeverityOverrideGuidance', 'PotentialImpacts', 'ThirdPartyTools', 'MitigationControl', 'Responsibility', 'IAControls']);
74
+ Object.entries(remainingFields).forEach(([field, value]) => {
75
+ extractedVulnDescription += `<${field}> ${value}`;
76
+ });
77
+ cleaned = {
78
+ VulnDiscussion: extractedVulnDescription.replace(/\[\[\[REPLACE_LESS_THAN]]]/, '"<"'),
79
+ };
80
+ Object.entries(converted.VulnDiscussion).forEach(([key, value]) => {
81
+ if (typeof value === 'string') {
82
+ cleaned[key] = value.replace(/\[\[\[REPLACE_LESS_THAN]]]/, '"<"');
83
+ }
84
+ else {
85
+ cleaned[key] = value;
86
+ }
87
+ });
88
+ }
89
+ else {
90
+ Object.entries(converted).forEach(([key, value]) => {
91
+ if (typeof value === 'string') {
92
+ cleaned[key] = value.replace(/\[\[\[REPLACE_LESS_THAN]]]/, '"<"');
93
+ }
94
+ else {
95
+ cleaned[key] = value;
96
+ }
97
+ });
98
+ }
99
+ return cleaned;
100
+ }
101
+ return {};
102
+ }
103
+ exports.convertEncodedHTMLIntoJson = convertEncodedHTMLIntoJson;
Binary file