-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhello_nisty.py
134 lines (118 loc) · 4.29 KB
/
hello_nisty.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
'''
Quick attempt to systematically generate Inspec test from the macOS Security Project.
https://github.com/usnistgov/macos_security
'''
from __future__ import print_function
from jinja2 import Template
import generate_baseline as b
# Double quote where needed, then escape
def escape_chars(string):
'''
Escape double quotes initially, add more to maketrans as needed.
'''
return string.translate(str.maketrans({"\"": r"\"",}))
# Inspec Test Template via jinja
INSPEC_TEMPLATE = Template(
'control \'{{control}}\' do\n title \'{{title}}\'\n desc \
\"{{desc}}\"\n impact {{impact}}\n describe command(\"{{test}}\") \
do\n {{exit}}\n end\nend')
def sev_to_inspec(in_sev):
'''
Convert macOS Security Severity to Inspec Weight
'''
if in_sev == 'missing':
return 0
elif in_sev == 'high':
return 1.0
elif in_sev == 'medium':
return 0.5
elif in_sev == 'low':
return 0.1
return 0
# Prettify the block
def indent_desc(string):
'''
Indent and clean up the spacing of the desc field.
'''
new = "\n"
space = " "
new += space + escape_chars(string.replace("\n", "\n "))
return new
def exit_to_ruby(test_dict):
'''
Translate security exit to ruby logic
'''
if 'integer' in test_dict:
return "its('exit_status') { should eq %s }" % test_dict['integer']
elif 'boolean' in test_dict:
return "its('exit_status') { should eq %s }" % test_dict['boolean']
elif 'string' in test_dict:
return "its('stdout') { should match(/%s/) }" % test_dict['string']
return None
def load_rules():
'''
Logic taken directly from generate_baseline.py
'''
all_rules = b.collect_rules()
return all_rules
def aggregate_controls(rules):
'''
Determine Controls from prefixes in the namespace of the rules
'''
controls = set([])
for rule in rules:
controls.add(rule.rule_id.partition('_')[0])
return controls
def create_tests(controls, rules):
'''
Create the tests from the gathered controls and their rules.
'''
for control in controls:
print("Processing control: {}".format(control))
for rule in rules:
inspec_tests = ""
if "{}_".format(control) in rule.rule_id:
print(" Processing rule: {}".format(rule.rule_id))
print(" Tags: {}".format(rule.rule_tags))
if any(item in ['inherent',
'permanent',
'n_a',
'supplemental']
for item in rule.rule_tags):
# Account for controls without tests/results
print(" Skipped test, skip tag.")
elif not exit_to_ruby(rule.rule_result_value):
# Account for controls without tests/results
print(" Skipped test, no result.")
else:
print(" Generated test.")
sev = sev_to_inspec(rule.rule_severity)
inspec_tests += INSPEC_TEMPLATE.render(
control=rule.rule_id,
title=rule.rule_title.replace("\'", "\\\'"),
desc=indent_desc(rule.rule_discussion).replace("\n \n", "\n"),
impact=sev,
test=escape_chars(rule.rule_check.replace("\\", "").rstrip()),
exit=exit_to_ruby(rule.rule_result_value)
)
inspec_tests += "\n"
try:
test = open("../inpsec-macos_security/controls/{}.rb".format(control), "a")
test.write(inspec_tests)
test.close()
except Exception as err:
print("Error writing file:")
print(err)
print("See the README for folder structure and execution.")
print("This script assumes its being ran from within\
the macos_security/scripts directory.")
def main():
'''
Load rules from macOS Security project,
aggregate controls and then create tests.
'''
rules = load_rules()
controls = aggregate_controls(rules)
create_tests(controls, rules)
if __name__ == "__main__":
main()