Completed
Pull Request — master (#6005)
by
unknown
01:54
created

create-stig-overlay.yes_no_prompt()   A

Complexity

Conditions 4

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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