Test Failed
Push — master ( 0f488d...4bd807 )
by Jan
03:11 queued 12s
created

utils.build_stig_control.get_implemented_stigs()   B

Complexity

Conditions 5

Size

Total Lines 24
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 19
nop 1
dl 0
loc 24
ccs 0
cts 18
cp 0
crap 30
rs 8.9833
c 0
b 0
f 0
1
#!/usr/bin/env python
2
3
from __future__ import print_function
4
5
import argparse
6
import json
7
import os
8
import sys
9
10
import xml.etree.ElementTree as ET
11
import yaml
12
13
import ssg.build_yaml
14
import ssg.environment
15
import ssg.rules
16
import ssg.yaml
17
18
19
SSG_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
20
BUILD_OUTPUT = os.path.join(SSG_ROOT, "build", "stig_control.yml")
21
RULES_JSON = os.path.join(SSG_ROOT, "build", "rule_dirs.json")
22
BUILD_CONFIG = os.path.join(SSG_ROOT, "build", "build_config.yml")
23
24
25
def parse_args():
26
    parser = argparse.ArgumentParser()
27
    parser.add_argument("-r", "--root", type=str, action="store", default=SSG_ROOT,
28
                        help="Path to SSG root directory (defaults to %s)" % SSG_ROOT)
29
    parser.add_argument("-o", "--output", type=str, action="store", default=BUILD_OUTPUT,
30
                        help="File to write yaml output to (defaults to build/stig_control.yml)")
31
    parser.add_argument("-p", "--product", type=str, action="store", required=True,
32
                        help="What product to get STIGs for")
33
    parser.add_argument("-m", "--manual", type=str, action="store", required=True,
34
                        help="Path to XML XCCDF manual file to use as the source of the STIGs")
35
    parser.add_argument("-j", "--json", type=str, action="store", default=RULES_JSON,
36
                        help="Path to the rules_dir.json (defaults to build/stig_control.json)")
37
    parser.add_argument("-c", "--build-config-yaml", default=BUILD_CONFIG,
38
                        help="YAML file with information about the build configuration. ")
39
    parser.add_argument("-ref", "--reference", type=str, default="stigid",
40
                        help="Reference system to check for, defaults to stigid")
41
42
    return parser.parse_args()
43
44
45
def handle_rule_yaml(args, rule_id, rule_dir, guide_dir, env_yaml):
46
    rule_obj = {'id': rule_id, 'dir': rule_dir, 'guide': guide_dir}
47
    rule_file = ssg.rules.get_rule_dir_yaml(rule_dir)
48
49
    rule_yaml = ssg.build_yaml.Rule.from_yaml(rule_file, env_yaml=env_yaml)
50
    rule_yaml.normalize(args.product)
51
    rule_obj['references'] = rule_yaml.references
52
    return rule_obj
53
54
55
def get_platform_rules(args):
56
    rules_json_file = open(args.json, 'r')
57
    rules_json = json.load(rules_json_file)
58
    platform_rules = list()
59
    for rule in rules_json.values():
60
        if args.product in rule['products']:
61
            platform_rules.append(rule)
62
    if not rules_json_file.closed:
63
        rules_json_file.close()
64
    return platform_rules
65
66
67
def get_implemented_stigs(args):
68
    platform_rules = get_platform_rules(args)
69
70
    product_dir = os.path.join(args.root, "products", args.product)
71
    product_yaml_path = os.path.join(product_dir, "product.yml")
72
    env_yaml = ssg.environment.open_environment(args.build_config_yaml, str(product_yaml_path))
73
74
    known_rules = dict()
75
    for rule in platform_rules:
76
        try:
77
            rule_obj = handle_rule_yaml(args, rule['id'],
78
                                        rule['dir'], rule['guide'], env_yaml)
79
        except ssg.yaml.DocumentationNotComplete:
80
            sys.stderr.write('Rule %s throw DocumentationNotComplete' % rule['id'])
81
            # Happens on non-debug build when a rule is "documentation-incomplete"
82
            continue
83
84
        if args.reference in rule_obj['references'].keys():
85
            ref = rule_obj['references'][args.reference]
86
            if ref in known_rules:
87
                known_rules[ref].append(rule['id'])
88
            else:
89
                known_rules[ref] = [rule['id']]
90
    return known_rules
91
92
93
def check_files(args):
94
    if not os.path.exists(args.json):
95
        sys.stderr.write('Unable to find %s\n' % args.json)
96
        sys.stderr.write('Hint: run ./utils/rule_dir_json.py\n')
97
        exit(-1)
98
99
    if not os.path.exists(args.build_config_yaml):
100
        sys.stderr.write('Unable to find %s\n' % args.build_config_yaml)
101
        sys.stderr.write('Hint: build the project,\n')
102
        exit(-1)
103
104
105
def get_controls(known_rules, ns, root):
106
    controls = list()
107
    for group in root.findall('checklist:Group', ns):
108
        for stig in group.findall('checklist:Rule', ns):
109
            stig_id = stig.find('checklist:version', ns).text
110
            control = dict()
111
            control['id'] = stig_id
112
            control['levels'] = [stig.attrib['severity']]
113
            control['title'] = stig.find('checklist:title', ns).text
114
            if stig_id in known_rules.keys():
115
                control['rules'] = known_rules.get(stig_id)
116
                control['status'] = 'automated'
117
            else:
118
                control['status'] = 'pending'
119
            controls.append(control)
120
    return controls
121
122
123
def main():
124
    args = parse_args()
125
    check_files(args)
126
127
    ns = {'checklist': 'http://checklists.nist.gov/xccdf/1.1'}
128
    known_rules = get_implemented_stigs(args)
129
    tree = ET.parse(args.manual)
130
    root = tree.getroot()
131
    output = dict()
132
    output['policy'] = root.find('checklist:title', ns).text
133
    output['title'] = root.find('checklist:title', ns).text
134
    output['id'] = 'stig_%s' % args.product
135
    output['source'] = 'https://public.cyber.mil/stigs/downloads/'
136
    output['levels'] = list()
137
    for level in ['high', 'medium', 'low']:
138
        output['levels'].append({'id': level})
139
    output['controls'] = get_controls(known_rules, ns, root)
140
    with open(args.output, 'w') as f:
141
        f.write(yaml.dump(output, sort_keys=False))
142
143
144
if __name__ == "__main__":
145
    main()
146