@mitre/inspec-objects 0.0.3 → 0.0.4
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/lib/parsers/xccdf.js +1 -1
- package/mitre-inspec-objects-v0.0.1.tgz +0 -0
- package/mitre-inspec-objects-v0.0.3.tgz +0 -0
- package/package.json +1 -1
- package/lib/utilities/CciNistMappingData.d.ts +0 -5100
- package/lib/utilities/CciNistMappingData.js +0 -5103
- package/src/index.ts +0 -5
- package/src/mappings/CciNistMappingData.ts +0 -5100
- package/src/objects/control.ts +0 -148
- package/src/objects/profile.ts +0 -93
- package/src/parsers/json.ts +0 -92
- package/src/parsers/oval.ts +0 -18
- package/src/parsers/xccdf.ts +0 -252
- package/src/types/diff.d.ts +0 -9
- package/src/types/oval.d.ts +0 -609
- package/src/types/xccdf.d.ts +0 -883
- package/src/utilities/diff.ts +0 -71
- package/src/utilities/global.ts +0 -52
- package/src/utilities/xccdf.ts +0 -119
package/src/objects/control.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { ExecJSON } from "inspecjs";
|
|
2
|
-
import _ from "lodash";
|
|
3
|
-
import {flatten, unflatten} from "flat"
|
|
4
|
-
import { escapeQuotes, unformatText, wrapAndEscapeQuotes } from "../utilities/global";
|
|
5
|
-
|
|
6
|
-
export default class Control {
|
|
7
|
-
id?: string | null;
|
|
8
|
-
title?: string | null;
|
|
9
|
-
code?: string | null;
|
|
10
|
-
desc?: string | null;
|
|
11
|
-
descs?: ExecJSON.ControlDescription[] | { [key: string]: string | undefined } | null;
|
|
12
|
-
impact?: number;
|
|
13
|
-
ref?: string;
|
|
14
|
-
refs?: (string | {
|
|
15
|
-
ref?: string;
|
|
16
|
-
url?: string;
|
|
17
|
-
uri?: string;
|
|
18
|
-
})[];
|
|
19
|
-
tags: {
|
|
20
|
-
check?: string;
|
|
21
|
-
fix?: string;
|
|
22
|
-
severity?: string;
|
|
23
|
-
gtitle?: string;
|
|
24
|
-
gid?: string;
|
|
25
|
-
satisfies?: string[];
|
|
26
|
-
rid?: string;
|
|
27
|
-
stig_id?: string;
|
|
28
|
-
fix_id?: string | null;
|
|
29
|
-
cci?: string[];
|
|
30
|
-
cis_controls?: Record<string, string[]>[];
|
|
31
|
-
nist?: string[];
|
|
32
|
-
legacy?: string[];
|
|
33
|
-
false_negatives?: string;
|
|
34
|
-
false_positives?: string;
|
|
35
|
-
documentable?: boolean;
|
|
36
|
-
mitigations?: string;
|
|
37
|
-
severity_override_guidance?: string;
|
|
38
|
-
potential_impacts?: string;
|
|
39
|
-
third_party_tools?: string;
|
|
40
|
-
mitigation_controls?: string;
|
|
41
|
-
responsibility?: string;
|
|
42
|
-
ia_controls?: string;
|
|
43
|
-
[key: string]:
|
|
44
|
-
| string
|
|
45
|
-
| string[]
|
|
46
|
-
| Record<string, string[]>[]
|
|
47
|
-
| boolean
|
|
48
|
-
| undefined
|
|
49
|
-
| null;
|
|
50
|
-
} = {};
|
|
51
|
-
|
|
52
|
-
constructor(data?: Partial<Control>) {
|
|
53
|
-
this.refs = []
|
|
54
|
-
this.tags = {}
|
|
55
|
-
if (data) {
|
|
56
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
57
|
-
_.set(this, key, value);
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
toUnformattedObject(): Control {
|
|
63
|
-
const flattened: Record<string, string | number> = flatten(this)
|
|
64
|
-
|
|
65
|
-
Object.entries(flattened).forEach(([key, value]) => {
|
|
66
|
-
if(typeof value === 'string') {
|
|
67
|
-
_.set(flattened, key, unformatText(value));
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
return new Control(unflatten(flattened));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
toRuby(lineLength: number = 80) {
|
|
75
|
-
let result = "# encoding: UTF-8\n\n";
|
|
76
|
-
|
|
77
|
-
result += `control "${this.id}" do\n`;
|
|
78
|
-
if (this.title) {
|
|
79
|
-
result += ` title "${wrapAndEscapeQuotes(this.title, lineLength)}"\n`;
|
|
80
|
-
} else {
|
|
81
|
-
console.error(`${this.id} does not have a title`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (this.desc) {
|
|
85
|
-
result += ` desc "${wrapAndEscapeQuotes(this.desc, lineLength)}"\n`;
|
|
86
|
-
} else {
|
|
87
|
-
console.error(`${this.id} does not have a desc`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (this.descs) {
|
|
91
|
-
Object.entries(this.descs).forEach(([key, desc]) => {
|
|
92
|
-
if (desc) {
|
|
93
|
-
result += ` desc "${key}", "${wrapAndEscapeQuotes(
|
|
94
|
-
desc,
|
|
95
|
-
lineLength
|
|
96
|
-
)}"\n`;
|
|
97
|
-
} else {
|
|
98
|
-
console.error(`${this.id} does not have a desc for the value ${key}`);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (this.impact) {
|
|
104
|
-
result += ` impact ${this.impact}\n`;
|
|
105
|
-
} else {
|
|
106
|
-
console.error(`${this.id} does not have an impact`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (this.refs) {
|
|
110
|
-
this.refs.forEach((ref) => {
|
|
111
|
-
if (typeof ref === 'string') {
|
|
112
|
-
result += ` ref '${escapeQuotes(ref)}'\n`;
|
|
113
|
-
} else {
|
|
114
|
-
result += ` ref '${escapeQuotes(ref.ref || '')}', url: '${escapeQuotes(ref.url || '')}'`
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
Object.entries(this.tags).forEach(([tag, value]) => {
|
|
121
|
-
if (value) {
|
|
122
|
-
if (typeof value === "object") {
|
|
123
|
-
if (Array.isArray(value) && typeof value[0] === "string") {
|
|
124
|
-
result += ` tag ${tag}: ${JSON.stringify(value)}\n`;
|
|
125
|
-
} else {
|
|
126
|
-
// Convert JSON Object to Ruby Hash
|
|
127
|
-
const stringifiedObject = JSON.stringify(value, null, 2)
|
|
128
|
-
.replace(/\n/g, "\n ")
|
|
129
|
-
.replace(/\{\n {6}/g, "{")
|
|
130
|
-
.replace(/\[\n {8}/g, "[")
|
|
131
|
-
.replace(/\n {6}\]/g, "]")
|
|
132
|
-
.replace(/\n {4}\}/g, "}")
|
|
133
|
-
.replace(/": \[/g, '" => [');
|
|
134
|
-
result += ` tag ${tag}: ${stringifiedObject}\n`;
|
|
135
|
-
}
|
|
136
|
-
} else if (typeof value === "string") {
|
|
137
|
-
result += ` tag ${tag}: "${wrapAndEscapeQuotes(
|
|
138
|
-
value,
|
|
139
|
-
lineLength
|
|
140
|
-
)}"\n`;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
result += "end";
|
|
145
|
-
|
|
146
|
-
return result;
|
|
147
|
-
}
|
|
148
|
-
}
|
package/src/objects/profile.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import Control from "./control";
|
|
2
|
-
import YAML from "yaml";
|
|
3
|
-
import _ from "lodash";
|
|
4
|
-
import { unformatText } from "../utilities/global";
|
|
5
|
-
|
|
6
|
-
export default class Profile {
|
|
7
|
-
name?: string | null;
|
|
8
|
-
title?: string | null;
|
|
9
|
-
maintainer?: string | null;
|
|
10
|
-
copyright?: string | null;
|
|
11
|
-
copyright_email?: string | null;
|
|
12
|
-
license?: string | null;
|
|
13
|
-
summary?: string | null;
|
|
14
|
-
description?: string | null;
|
|
15
|
-
version?: string | null;
|
|
16
|
-
inspec_version?: string | null;
|
|
17
|
-
supports: {
|
|
18
|
-
"platform-family"?: string;
|
|
19
|
-
"platform-name"?: string;
|
|
20
|
-
"os-name"?: string;
|
|
21
|
-
"os-family"?: string;
|
|
22
|
-
release?: string;
|
|
23
|
-
platform?: string;
|
|
24
|
-
}[] = [];
|
|
25
|
-
depends: {
|
|
26
|
-
// Required for all
|
|
27
|
-
name: string; // Required for all
|
|
28
|
-
|
|
29
|
-
// Local file
|
|
30
|
-
path?: string; // Local path on disk
|
|
31
|
-
|
|
32
|
-
// Remote HTTP(s)
|
|
33
|
-
url?: string; // Remote URL tarball
|
|
34
|
-
username?: string; // HTTP Basic Authentication Username
|
|
35
|
-
password?: string; // HTTP Basic Authentication Password
|
|
36
|
-
|
|
37
|
-
// Git Repository
|
|
38
|
-
git?: string;
|
|
39
|
-
branch?: string;
|
|
40
|
-
tag?: string;
|
|
41
|
-
commit?: string;
|
|
42
|
-
version?: string;
|
|
43
|
-
relative_path?: string;
|
|
44
|
-
|
|
45
|
-
// Chef Supermarket
|
|
46
|
-
supermarket?: string;
|
|
47
|
-
|
|
48
|
-
// Base Compliance
|
|
49
|
-
compliance?: string;
|
|
50
|
-
}[] = [];
|
|
51
|
-
inputs: { [key: string]: string }[] = [];
|
|
52
|
-
gem_dependencies?: {name: string, version: string}[];
|
|
53
|
-
libraries: string[] = [];
|
|
54
|
-
readme?: string | null;
|
|
55
|
-
files: string[] = [];
|
|
56
|
-
controls: Control[] = [];
|
|
57
|
-
|
|
58
|
-
constructor(data?: Omit<Partial<Profile>, "controls">) {
|
|
59
|
-
if (data) {
|
|
60
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
61
|
-
_.set(this, key, value);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
createInspecYaml(): string {
|
|
67
|
-
return YAML.stringify({
|
|
68
|
-
name: this.name,
|
|
69
|
-
title: this.title,
|
|
70
|
-
maintainer: this.maintainer,
|
|
71
|
-
copyright: this.copyright,
|
|
72
|
-
copyright_email: this.copyright_email,
|
|
73
|
-
license: this.license,
|
|
74
|
-
summary: this.summary,
|
|
75
|
-
description: this.description,
|
|
76
|
-
version: this.version,
|
|
77
|
-
supports: this.supports,
|
|
78
|
-
depends: this.depends,
|
|
79
|
-
inspec_version: this.inspec_version,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
toUnformattedObject(): Profile {
|
|
84
|
-
const unformattedProfile: Profile = new Profile(this);
|
|
85
|
-
Object.entries(this).forEach(([key, value]) => {
|
|
86
|
-
if (typeof value === "string") {
|
|
87
|
-
_.set(unformattedProfile, key, unformatText(value));
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
unformattedProfile.controls = this.controls.map((control) => control.toUnformattedObject())
|
|
91
|
-
return unformattedProfile;
|
|
92
|
-
}
|
|
93
|
-
}
|
package/src/parsers/json.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ContextualizedEvaluation,
|
|
3
|
-
ContextualizedProfile,
|
|
4
|
-
contextualizeEvaluation,
|
|
5
|
-
contextualizeProfile,
|
|
6
|
-
ConversionResult,
|
|
7
|
-
convertFile,
|
|
8
|
-
ExecJSON
|
|
9
|
-
} from "inspecjs";
|
|
10
|
-
import _ from "lodash";
|
|
11
|
-
import Control from "../objects/control";
|
|
12
|
-
import Profile from "../objects/profile";
|
|
13
|
-
|
|
14
|
-
export function processEvaluation(evaluationInput: ContextualizedEvaluation) {
|
|
15
|
-
const topLevelProfile = evaluationInput.contains[0];
|
|
16
|
-
const profile = new Profile({
|
|
17
|
-
name: topLevelProfile.data.name,
|
|
18
|
-
title: topLevelProfile.data.title,
|
|
19
|
-
maintainer: topLevelProfile.data.maintainer,
|
|
20
|
-
copyright: topLevelProfile.data.copyright,
|
|
21
|
-
copyright_email: topLevelProfile.data.copyright_email,
|
|
22
|
-
license: _.get(topLevelProfile.data, "license"),
|
|
23
|
-
summary: _.get(topLevelProfile.data, "summary"),
|
|
24
|
-
description: _.get(topLevelProfile.data, "description"),
|
|
25
|
-
version: topLevelProfile.data.version,
|
|
26
|
-
});
|
|
27
|
-
topLevelProfile.contains.forEach((control) => {
|
|
28
|
-
profile.controls.push(
|
|
29
|
-
new Control({
|
|
30
|
-
id: control.data.id,
|
|
31
|
-
title: control.data.title,
|
|
32
|
-
impact: control.data.impact,
|
|
33
|
-
desc: control.data.desc,
|
|
34
|
-
descs: control.hdf.wraps.descriptions,
|
|
35
|
-
tags: control.hdf.wraps.tags,
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
return profile;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function processProfileJSON(
|
|
43
|
-
profileInput: ContextualizedProfile
|
|
44
|
-
): Profile {
|
|
45
|
-
const profile = new Profile({
|
|
46
|
-
name: profileInput.data.name,
|
|
47
|
-
title: profileInput.data.title,
|
|
48
|
-
maintainer: profileInput.data.maintainer,
|
|
49
|
-
copyright: profileInput.data.copyright,
|
|
50
|
-
copyright_email: profileInput.data.copyright_email,
|
|
51
|
-
license: _.get(profileInput.data, "license"),
|
|
52
|
-
summary: _.get(profileInput.data, "summary"),
|
|
53
|
-
description: _.get(profileInput.data, "description"),
|
|
54
|
-
version: profileInput.data.version,
|
|
55
|
-
});
|
|
56
|
-
profileInput.data.controls.forEach((control) => {
|
|
57
|
-
profile.controls.push(
|
|
58
|
-
new Control({
|
|
59
|
-
id: control.id,
|
|
60
|
-
title: control.title,
|
|
61
|
-
desc: control.desc,
|
|
62
|
-
impact: control.impact,
|
|
63
|
-
code: control.code,
|
|
64
|
-
tags: control.tags,
|
|
65
|
-
descs: control.descriptions,
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
return profile;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function processExecJSON(execJSON: ExecJSON.Execution) {
|
|
73
|
-
return processEvaluation(contextualizeEvaluation(execJSON));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function processJSON(json: string): Profile {
|
|
77
|
-
const convertedFile: ConversionResult = convertFile(json, true);
|
|
78
|
-
let profile = new Profile();
|
|
79
|
-
if (convertedFile["1_0_ExecJson"]) {
|
|
80
|
-
profile = processEvaluation(
|
|
81
|
-
contextualizeEvaluation(convertedFile["1_0_ExecJson"])
|
|
82
|
-
).toUnformattedObject();
|
|
83
|
-
} else if (convertedFile["1_0_ProfileJson"]) {
|
|
84
|
-
profile = processProfileJSON(contextualizeProfile(JSON.parse(json))).toUnformattedObject();
|
|
85
|
-
} else {
|
|
86
|
-
throw new Error("Unknown file type passed");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
profile.controls = _.sortBy(profile.controls, "id");
|
|
90
|
-
|
|
91
|
-
return profile;
|
|
92
|
-
}
|
package/src/parsers/oval.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { convertEncodedXmlIntoJson } from "../utilities/xccdf"
|
|
2
|
-
import {OvalDefinitionValue, Oval} from '../types/oval'
|
|
3
|
-
|
|
4
|
-
export function processOVAL(oval: string): Record<string, OvalDefinitionValue> {
|
|
5
|
-
const parsed: Oval = convertEncodedXmlIntoJson(oval)
|
|
6
|
-
|
|
7
|
-
const extractedDefinitions: Record<string, OvalDefinitionValue> = {}
|
|
8
|
-
|
|
9
|
-
for (const ovalDefinitions of parsed.oval_definitions) {
|
|
10
|
-
for (const definitionList of ovalDefinitions.definitions) {
|
|
11
|
-
for (const definition of definitionList.definition) {
|
|
12
|
-
extractedDefinitions[definition["@_id"]] = definition
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return extractedDefinitions
|
|
18
|
-
}
|
package/src/parsers/xccdf.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import Profile from '../objects/profile';
|
|
2
|
-
import { convertEncodedHTMLIntoJson, convertEncodedXmlIntoJson, impactNumberToSeverityString, removeXMLSpecialCharacters, severityStringToImpact } from '../utilities/xccdf';
|
|
3
|
-
import { BenchmarkGroup, BenchmarkRule, DecodedDescription, FrontMatter, Notice, ParsedXCCDF, RationaleElement } from '../types/xccdf';
|
|
4
|
-
import Control from '../objects/control';
|
|
5
|
-
import _ from 'lodash';
|
|
6
|
-
import { OvalDefinitionValue } from '../types/oval';
|
|
7
|
-
import {data as CciNistMappingData} from '../mappings/CciNistMappingData'
|
|
8
|
-
|
|
9
|
-
export type GroupContextualizedRule = BenchmarkRule & {group: Omit<BenchmarkGroup, 'Rule' | 'Group'>}
|
|
10
|
-
|
|
11
|
-
export function extractAllRules(groups: BenchmarkGroup[]): GroupContextualizedRule[] {
|
|
12
|
-
const rules: GroupContextualizedRule[] = [];
|
|
13
|
-
groups.forEach((group) => {
|
|
14
|
-
if (group.Rule) {
|
|
15
|
-
rules.push(...(group.Rule.map((rule) => {
|
|
16
|
-
return {
|
|
17
|
-
...rule,
|
|
18
|
-
group: _.omit(group, ['Rule', 'Group'])
|
|
19
|
-
}
|
|
20
|
-
})))
|
|
21
|
-
}
|
|
22
|
-
if (group.Group) {
|
|
23
|
-
rules.push(...extractAllRules(group.Group))
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
return rules
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function processXCCDF(xml: string, removeNewlines = false, ovalDefinitions?: Record<string, OvalDefinitionValue>): Profile {
|
|
30
|
-
const parsedXML: ParsedXCCDF = convertEncodedXmlIntoJson(xml)
|
|
31
|
-
const rules = extractAllRules(parsedXML.Benchmark[0].Group)
|
|
32
|
-
|
|
33
|
-
const profile = new Profile({
|
|
34
|
-
name: parsedXML.Benchmark[0]['@_id'],
|
|
35
|
-
title: (parsedXML.Benchmark[0].title[0] as FrontMatter)['#text'],
|
|
36
|
-
summary: (parsedXML.Benchmark[0].description[0] as RationaleElement)['#text']
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
rules.forEach(rule => {
|
|
40
|
-
let extractedDescription: string | DecodedDescription;
|
|
41
|
-
if (Array.isArray(rule.description)) {
|
|
42
|
-
extractedDescription = rule.description[0]['#text']
|
|
43
|
-
} else {
|
|
44
|
-
extractedDescription = convertEncodedHTMLIntoJson(rule.description)
|
|
45
|
-
}
|
|
46
|
-
const control = new Control();
|
|
47
|
-
|
|
48
|
-
control.id = rule.group['@_id']
|
|
49
|
-
|
|
50
|
-
if (removeNewlines) {
|
|
51
|
-
const title = removeXMLSpecialCharacters(rule['@_severity'] ? rule.title : `[[[MISSING SEVERITY FROM STIG]]] ${rule.title}`)
|
|
52
|
-
control.title = title.replace(/\n/g, '{{{{newlineHERE}}}}')
|
|
53
|
-
const desc = removeXMLSpecialCharacters(typeof extractedDescription === 'string' ? extractedDescription : extractedDescription.VulnDiscussion?.split('Satisfies: ')[0] || 'Missing Description')
|
|
54
|
-
control.desc = desc?.replace(/\n/g, '{{{{newlineHERE}}}}')
|
|
55
|
-
} else {
|
|
56
|
-
control.title = removeXMLSpecialCharacters(rule['@_severity'] ? rule.title : `[[[MISSING SEVERITY FROM STIG]]] ${rule.title}`)
|
|
57
|
-
control.desc = removeXMLSpecialCharacters(typeof extractedDescription === 'string' ? extractedDescription : extractedDescription.VulnDiscussion?.split('Satisfies: ')[0] || 'Missing Description')
|
|
58
|
-
}
|
|
59
|
-
control.impact = severityStringToImpact(rule['@_severity'] || 'critical', rule.group['@_id'])
|
|
60
|
-
|
|
61
|
-
if (!control.descs || Array.isArray(control.descs)) {
|
|
62
|
-
control.descs = {}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (rule.check) {
|
|
66
|
-
if (rule.check.some((ruleValue) => 'check-content' in ruleValue)) {
|
|
67
|
-
if (removeNewlines) {
|
|
68
|
-
const check = removeXMLSpecialCharacters(rule.check ? rule.check[0]['check-content'] : 'Missing description')
|
|
69
|
-
control.descs.check = check.replace(/\n/g, '{{{{newlineHERE}}}}')
|
|
70
|
-
} else {
|
|
71
|
-
control.descs.check = removeXMLSpecialCharacters(rule.check ? rule.check[0]['check-content'] : 'Missing description')
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
} else if (rule.check.some((ruleValue) => 'check-content-ref' in ruleValue) && ovalDefinitions) {
|
|
75
|
-
let referenceID: string | null = null;
|
|
76
|
-
for (const checkContent of rule.check) {
|
|
77
|
-
if ('check-content-ref' in checkContent && checkContent['@_system'].includes('oval')) {
|
|
78
|
-
for (const checkContentRef of checkContent['check-content-ref']) {
|
|
79
|
-
if (checkContentRef['@_name']) {
|
|
80
|
-
referenceID = checkContentRef['@_name']
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (referenceID && referenceID in ovalDefinitions) {
|
|
86
|
-
if (removeNewlines) {
|
|
87
|
-
const check = removeXMLSpecialCharacters(ovalDefinitions[referenceID].metadata[0].title)
|
|
88
|
-
control.descs.check = check.replace(/\n/g, '{{{{newlineHERE}}}}')
|
|
89
|
-
} else {
|
|
90
|
-
control.descs.check = removeXMLSpecialCharacters(ovalDefinitions[referenceID].metadata[0].title)
|
|
91
|
-
}
|
|
92
|
-
} else if (referenceID) {
|
|
93
|
-
console.warn(`Could not find OVAL definition for ${referenceID}`)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (removeNewlines) {
|
|
99
|
-
const fix = removeXMLSpecialCharacters(rule.fixtext ? rule.fixtext[0]['#text'] : (rule.fix ? (rule.fix[0] as Notice)['#text'] || 'Missing fix text' : 'Missing fix text'))
|
|
100
|
-
control.descs.fix = fix.replace(/\n/g, '{{{{newlineHERE}}}}')
|
|
101
|
-
} else {
|
|
102
|
-
control.descs.fix = removeXMLSpecialCharacters(rule.fixtext ? rule.fixtext[0]['#text'] : (rule.fix ? (rule.fix[0] as Notice)['#text'] || 'Missing fix text' : 'Missing fix text'))
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
control.tags.severity = impactNumberToSeverityString(severityStringToImpact(rule['@_severity'] || 'critical', control.id))
|
|
106
|
-
control.tags.gid = rule.group['@_id'],
|
|
107
|
-
control.tags.rid = rule['@_id']
|
|
108
|
-
control.tags.stig_id = rule['version']
|
|
109
|
-
|
|
110
|
-
if (typeof rule.group.title === "string") {
|
|
111
|
-
control.tags.gtitle = removeXMLSpecialCharacters(rule.group.title)
|
|
112
|
-
} else {
|
|
113
|
-
control.tags.gtitle = removeXMLSpecialCharacters(_.get(rule.group, 'title[0].#text'))
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (rule['fix'] && rule['fix'].length > 0) {
|
|
117
|
-
control.tags.fix_id = rule['fix'][0]['@_id']
|
|
118
|
-
} else {
|
|
119
|
-
control.tags.fix_id = null
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (rule['rationale']) {
|
|
123
|
-
control.tags.rationale = rule['rationale'][0]['#text']
|
|
124
|
-
} else {
|
|
125
|
-
control.tags.rationale = null
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (typeof extractedDescription === 'object') {
|
|
129
|
-
control.tags.satisfies = extractedDescription.VulnDiscussion?.includes('Satisfies: ') && extractedDescription.VulnDiscussion.split('Satisfies: ').length >= 1 ? extractedDescription.VulnDiscussion.split('Satisfies: ')[1].split(',').map(satisfaction => satisfaction.trim()) : undefined
|
|
130
|
-
control.tags.false_negatives = extractedDescription.FalseNegatives || undefined
|
|
131
|
-
control.tags.false_positives = extractedDescription.FalsePositives || undefined
|
|
132
|
-
control.tags.documentable = typeof extractedDescription.Documentable === 'boolean' ? extractedDescription.Documentable : undefined
|
|
133
|
-
control.tags.mitigations = extractedDescription.Mitigations || undefined
|
|
134
|
-
control.tags.severity_override_guidance = extractedDescription.SeverityOverrideGuidance || undefined
|
|
135
|
-
control.tags.potential_impacts = extractedDescription.PotentialImpacts || undefined
|
|
136
|
-
control.tags.third_party_tools = extractedDescription.ThirdPartyTools || undefined
|
|
137
|
-
control.tags.mitigation_control = extractedDescription.MitigationControl || undefined
|
|
138
|
-
control.tags.mitigation_controls = extractedDescription.MitigationControls || undefined
|
|
139
|
-
control.tags.responsibility = extractedDescription.Responsibility || undefined
|
|
140
|
-
control.tags.ia_controls = extractedDescription.IAControls || undefined
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
control.tags = _.mapValues(_.omitBy(control.tags, (value) => value === undefined), (value) => {
|
|
144
|
-
if (typeof value === 'string') {
|
|
145
|
-
return removeXMLSpecialCharacters(value)
|
|
146
|
-
} else {
|
|
147
|
-
return value
|
|
148
|
-
}
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
// Get all identifiers from the rule
|
|
152
|
-
if (rule.ident) {
|
|
153
|
-
rule.ident.forEach((identifier) => {
|
|
154
|
-
// Get CCIs
|
|
155
|
-
if (identifier['@_system'].toLowerCase().includes('cci')) {
|
|
156
|
-
if (!('cci' in control.tags)) {
|
|
157
|
-
control.tags.cci = []
|
|
158
|
-
}
|
|
159
|
-
control.tags.cci?.push(identifier['#text'])
|
|
160
|
-
}
|
|
161
|
-
// Get legacy identifiers
|
|
162
|
-
else if (identifier['@_system'].toLowerCase().includes('legacy')) {
|
|
163
|
-
if (!('legacy' in control.tags)) {
|
|
164
|
-
control.tags.legacy = []
|
|
165
|
-
}
|
|
166
|
-
control.tags.legacy?.push(identifier['#text'])
|
|
167
|
-
}
|
|
168
|
-
// Get NIST identifiers
|
|
169
|
-
else if (identifier['@_system'].toLowerCase().includes('nist')) {
|
|
170
|
-
if (!('nist' in control.tags)) {
|
|
171
|
-
control.tags.nist = []
|
|
172
|
-
}
|
|
173
|
-
control.tags.nist?.push(identifier['#text'])
|
|
174
|
-
} else {
|
|
175
|
-
// console.log('Alert')
|
|
176
|
-
// console.log(identifier['@_system'])
|
|
177
|
-
// console.log(identifier['#text'])
|
|
178
|
-
}
|
|
179
|
-
})
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
rule.reference?.forEach((reference) => {
|
|
183
|
-
if (_.get(reference, '@_href') === '') {
|
|
184
|
-
control.refs?.push(_.get(reference, '#text'))
|
|
185
|
-
} else {
|
|
186
|
-
try {
|
|
187
|
-
const referenceText = _.get(reference, '#text') || ''
|
|
188
|
-
const referenceURL = _.get(reference, '@_href') || ''
|
|
189
|
-
if (referenceURL) {
|
|
190
|
-
const parsedURL = new URL(_.get(reference, '@_href'))
|
|
191
|
-
if (parsedURL.protocol.toLowerCase().includes('http') || parsedURL.protocol.toLowerCase().includes('https')) {
|
|
192
|
-
control.refs?.push({
|
|
193
|
-
ref: referenceText,
|
|
194
|
-
url: referenceURL
|
|
195
|
-
})
|
|
196
|
-
} else {
|
|
197
|
-
control.refs?.push({
|
|
198
|
-
ref: referenceText,
|
|
199
|
-
uri: referenceURL
|
|
200
|
-
})
|
|
201
|
-
}
|
|
202
|
-
} else {
|
|
203
|
-
if ('title' in reference) {
|
|
204
|
-
control.refs?.push(_.get(reference, 'title') as string)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Add the reference to the control tags when seperated by §
|
|
209
|
-
if (typeof referenceText === 'string' && referenceText.indexOf('§') !== -1) {
|
|
210
|
-
const referenceParts = referenceText.split('§')
|
|
211
|
-
if (referenceParts.length == 2) {
|
|
212
|
-
let [identifierType, identifier] = referenceText.split('§')
|
|
213
|
-
identifierType = identifierType.toLowerCase();
|
|
214
|
-
if (!(identifierType in control.tags)) {
|
|
215
|
-
control.tags[identifierType] = [identifier]
|
|
216
|
-
} else if (Array.isArray(control.tags[identifierType])) {
|
|
217
|
-
control.tags[identifierType] = _.union(control.tags[identifierType] as ArrayLike<string>, [identifier])
|
|
218
|
-
} else {
|
|
219
|
-
console.warn(`Attempted to push identifier to control tags when identifier already exists: ${identifierType}: ${identifier}`)
|
|
220
|
-
}
|
|
221
|
-
} else {
|
|
222
|
-
console.warn("Reference parts of invalid length:")
|
|
223
|
-
console.log(referenceParts)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
} catch (e){
|
|
227
|
-
console.warn(`Error parsing ref for control ${control.id}: `)
|
|
228
|
-
console.warn(JSON.stringify(reference, null, 2))
|
|
229
|
-
console.warn(e);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
// Associate any CCIs with NIST tags
|
|
235
|
-
if (control.tags.cci) {
|
|
236
|
-
control.tags.cci.forEach((cci: string) => {
|
|
237
|
-
if (!('nist' in control.tags)) {
|
|
238
|
-
control.tags.nist = []
|
|
239
|
-
}
|
|
240
|
-
if (cci in CciNistMappingData) {
|
|
241
|
-
control.tags.nist?.push(_.get(CciNistMappingData, cci))
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
profile.controls.push(control)
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
profile.controls = _.sortBy(profile.controls, 'id')
|
|
250
|
-
|
|
251
|
-
return profile.toUnformattedObject()
|
|
252
|
-
}
|