Test Failed
Push — master ( 9376ca...569fe7 )
by Jan
08:47 queued 02:14
created

utils.create-stig-overlay.get_nested_stig_items()   A

Complexity

Conditions 5

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 8
nop 2
dl 0
loc 9
ccs 0
cts 8
cp 0
crap 30
rs 9.3333
c 0
b 0
f 0
1
#!/usr/bin/env python3
2
3
from __future__ import print_function
4
5
import re
6
import sys
7
import argparse
8
import os
9
import ssg
10
import ssg.xml
11
import xml.dom.minidom
12
13
ET = ssg.xml.ElementTree
14
15
16
owner = "disastig"
17
stig_ns = ["https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=operating-systems%2Cunix-linux",
18
           "https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=operating-systems%2Cgeneral-purpose-os",
19
           "https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=app-security%2Capplication-servers",
20
           "https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=app-security%2Capp-security-dev"]
21
xccdf_ns = "http://checklists.nist.gov/xccdf/1.1"
22
dc_ns = "http://purl.org/dc/elements/1.1/"
23
outfile = "stig_overlay.xml"
24
25
26
def yes_no_prompt():
27
    prompt = "Would you like to proceed? (Y/N): "
28
29
    while True:
30
        data = str(raw_input(prompt)).lower()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable raw_input does not seem to be defined.
Loading history...
31
32
        if data in ("yes", "y"):
33
            return True
34
        elif data in ("n", "no"):
35
            return False
36
37
38
def element_value(element, element_obj):
39
    for elem in element_obj.findall("./{%s}%s" % (xccdf_ns, element)):
40
        elem = elem.text
41
    try:
42
        return elem
0 ignored issues
show
introduced by
The variable elem does not seem to be defined in case the for loop on line 39 is not entered. Are you sure this can never be the case?
Loading history...
43
    except UnboundLocalError as e:
44
        return ""
45
46
47
def ssg_xccdf_stigid_mapping(ssgtree):
48
    xccdftostig_idmapping = {}
49
50
    for rule in ssgtree.findall(".//{%s}Rule" % xccdf_ns):
51
        srgs = []
52
        rhid = ""
53
54
        xccdfid = rule.get("id")
55
        if xccdfid is not None:
56
            for references in stig_ns:
57
                stig = [ids for ids in rule.findall(".//{%s}reference[@href='%s']" % (xccdf_ns, references))]
58
                for ref in reversed(stig):
59
                    if not ref.text.startswith("SRG-"):
60
                        rhid = ref.text
61
                    else:
62
                        srgs.append(ref.text)
63
            xccdftostig_idmapping.update({rhid: {xccdfid: srgs}})
64
65
    return xccdftostig_idmapping
66
67
68
def getkey(elem):
69
    return elem.get("ownerid")
70
71
72
def new_stig_overlay(xccdftree, ssgtree, outfile, quiet):
73
    if not ssgtree:
74
        ssg_mapping = False
75
    else:
76
        ssg_mapping = ssg_xccdf_stigid_mapping(ssgtree)
77
78
    new_stig_overlay = ET.Element("overlays", xmlns=xccdf_ns)
79
    for group in xccdftree.findall("./{%s}Group" % xccdf_ns):
80
        vkey = group.get("id").strip('V-')
81
        for title in group.findall("./{%s}title" % xccdf_ns):
82
            srg = title.text
83
        for rule in group.findall("./{%s}Rule" % xccdf_ns):
84
            svkey_raw = rule.get("id")
85
            svkey = svkey_raw.strip()[3:9]
86
            severity = rule.get("severity")
87
            release = svkey_raw.strip()[10:-5]
88
            version = element_value("version", rule)
89
            rule_title = element_value("title", rule)
90
            ident = element_value("ident", rule).strip("CCI-").lstrip("0")
91
92
        if not ssgtree:
93
            mapped_id = "XXXX"
94
        else:
95
            try:
96
                mapped_id = ''.join(ssg_mapping[version].keys())
0 ignored issues
show
introduced by
The variable version does not seem to be defined for all execution paths.
Loading history...
97
            except KeyError as e:
98
                mapped_id = "XXXX"
99
100
        overlay = ET.SubElement(new_stig_overlay, "overlay", owner=owner,
101
                                ruleid=mapped_id, ownerid=version, disa=ident,
0 ignored issues
show
introduced by
The variable ident does not seem to be defined for all execution paths.
Loading history...
102
                                severity=severity)
0 ignored issues
show
introduced by
The variable severity does not seem to be defined for all execution paths.
Loading history...
103
        vmsinfo = ET.SubElement(overlay, "VMSinfo", VKey=vkey,
104
                                SVKey=svkey, VRelease=release)
0 ignored issues
show
introduced by
The variable release does not seem to be defined for all execution paths.
Loading history...
introduced by
The variable svkey does not seem to be defined for all execution paths.
Loading history...
105
        title = ET.SubElement(overlay, "title", text=rule_title)
0 ignored issues
show
introduced by
The variable rule_title does not seem to be defined for all execution paths.
Loading history...
106
107
    lines = new_stig_overlay.findall("overlay")
108
    new_stig_overlay[:] = sorted(lines, key=getkey)
109
110
    dom = xml.dom.minidom.parseString(ET.tostring(new_stig_overlay, encoding="UTF-8", xml_declaration=True))
111
    pretty_xml_as_string = dom.toprettyxml(indent='  ', encoding="UTF-8")
112
113
    overlay_directory = os.path.dirname(outfile)
114
    if not os.path.exists(overlay_directory):
115
        os.makedirs(overlay_directory)
116
        if not quiet:
117
            print("\nOverlay directory created: %s" % overlay_directory)
118
119
    with open(outfile, 'wb') as f:
120
        f.write(pretty_xml_as_string)
121
122
    if not quiet:
123
        print("\nGenerated the new STIG overlay file: %s" % outfile)
124
125
126
def parse_args():
127
    parser = argparse.ArgumentParser()
128
    parser.add_argument("--disa-xccdf", default=False, required=True,
129
                        action="store", dest="disa_xccdf_filename",
130
                        help="A DISA generated XCCDF Manual checks file. \
131
                              For example: disa-stig-rhel8-v1r12-xccdf-manual.xml")
132
    parser.add_argument("--ssg-xccdf", default=None,
133
                        action="store", dest="ssg_xccdf_filename",
134
                        help="A SSG generated XCCDF file. \
135
                              For example: ssg-rhel8-xccdf.xml")
136
    parser.add_argument("-o", "--output", default=outfile,
137
                        action="store", dest="output_file",
138
                        help="STIG overlay XML content file \
139
                             [default: %default]")
140
    parser.add_argument("-q", "--quiet", dest="quiet", default=False,
141
                      action="store_true", help="Do not print anything and assume yes for everything")
142
143
    return parser.parse_args()
144
145
146
def main():
147
    args = parse_args()
148
149
    disa_xccdftree = ET.parse(args.disa_xccdf_filename)
150
151
    if not args.ssg_xccdf_filename:
152
        prompt = True
153
        if not args.quiet:
154
            print("WARNING: You are generating a STIG overlay XML file without mapping it "
155
                "to existing SSG content.")
156
            prompt = yes_no_prompt()
157
        if not prompt:
158
            sys.exit(0)
159
        ssg_xccdftree = False
160
    else:
161
        ssg_xccdftree = ET.parse(args.ssg_xccdf_filename)
162
        ssg = ssg_xccdftree.find(".//{%s}publisher" % dc_ns).text
163
        if ssg != "SCAP Security Guide Project":
164
            if not args.quiet:
165
                sys.exit("%s is not a valid SSG generated XCCDF file." % args.ssg_xccdf_filename)
166
            else:
167
                sys.exit(1)
168
169
    disa = disa_xccdftree.find(".//{%s}source" % dc_ns).text
170
    if disa != "STIG.DOD.MIL":
171
        if not args.quiet:
172
            sys.exit("%s is not a valid DISA generated manual XCCDF file." % args.disa_xccdf_filename)
173
        else:
174
            sys.exit(2)
175
176
    new_stig_overlay(disa_xccdftree, ssg_xccdftree, args.output_file, args.quiet)
177
178
179
if __name__ == "__main__":
180
    main()
181