Issues (70)

utils/create-stig-overlay.py (7 issues)

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